Posthive
FreeNot checkedSchedule and manage social media posts across 13 platforms (Bluesky, Threads, Instagram, LinkedIn, Mastodon, YouTube, Facebook, Pinterest, Telegram, Nostr, X/Tw
About
Schedule and manage social media posts across 13 platforms (Bluesky, Threads, Instagram, LinkedIn, Mastodon, YouTube, Facebook, Pinterest, Telegram, Nostr, X/Twitter, Discord, Tumblr and more). OAuth 2.0 + PKCE, 10 tools, draft-first workflow for AI agents.
README
Posthive
The agentic social media scheduling platform. Plan, create, and publish posts across every major social network with built-in MCP support for Claude, Cursor, ChatGPT and AI agents.
Self-hostable · Open-source · AGPL-3.0
Features
Scheduling
- Multi-platform posting - write once, publish to multiple platforms simultaneously
- Bulk CSV scheduling - upload a spreadsheet to schedule hundreds of posts; per-row platform exclusions (
!instagram) - Post templates - save, load, and delete reusable post drafts
- Dry run mode - full pipeline test without making real API calls
- First comment scheduling - auto-reply immediately after the main post goes live
- Per-platform overrides - custom text and first comment per account (Pro+)
AI & Agents
- MCP server - connect Claude, ChatGPT, Cursor, VS Code, Claude Code, Codex, OpenClaw, Hermes Agent, or any MCP-compatible agent via one bare URL
- OAuth 2.0 + PKCE - full dynamic client registration flow; no API key to paste for any client — the agent opens your browser to sign in
posthive-cli- shell CLI (npx posthive-cli) withlogin/logout/whoami, mirrors the full public API, ships a bundledSKILL.mdfor agent self-discoveryposthive-mcp- standalone MCP server package (npx posthive-mcp); shares the same login asposthive-clivia~/.posthive/config.json- 10 MCP tools - list accounts, create/get/update/delete posts, approve drafts, duplicate, list/use templates
- Media type support - Instagram media type (post/reel/story) and YouTube type (short/video) via MCP
- Draft-first - every agent-created post lands as a draft for human review unless scheduling is explicitly requested
- Plan-gated - MCP/API access requires Pro or Team plan (self-hosted with billing disabled: unlimited)
Media
- Images (up to 4 per post; plan-gated), video (up to 100 MB)
- Instagram Reels, Stories, and carousel (up to 10 items)
- YouTube Shorts + long-form video with dedicated Title/Description fields
- Pinterest Pins with dedicated Title/Description fields image required
- Alt text on every image
- Clipboard paste and drag-and-drop upload
Calendar & Posts
- FullCalendar month/week/day views with drag-to-reschedule
- Real-time job status via Server-Sent Events
- Platform filter in list and calendar views
- Retry failed platforms (re-queues only failed targets; skips already-successful ones)
- Inline edit and reschedule from list view
Auth & Accounts
- Email + password auth (JWT, bcrypt, silent refresh)
- Email verification and password reset via Resend
- API keys for programmatic access (Pro/Team plans)
- Token expiry warnings with one-click reconnect
- Background token auto-refresh (Threads, Instagram, Facebook, YouTube every 12h)
Infrastructure
- BullMQ queue backed by Redis (Upstash or Railway)
- AES-256-GCM encryption for all stored OAuth credentials
- Sentry error monitoring
- Orphan upload cleanup cron (every 6h)
- Rate limiting per route
- CSRF nonce on all OAuth flows
Billing (optional)
- Dodo Payments - Creator, Pro, Team plans
- 14-day free trial; INR/USD currency detection
- Set
ENABLE_BILLING=falsefor self-hosted mode all features unlocked, no plan limits
Tech Stack
| Layer | Technology |
|---|---|
| Frontend | Next.js 16 (App Router), React 18, Tailwind CSS |
| Backend | Fastify v4, TypeScript ESM, Node.js |
| Database | Prisma 5 - Postgres (dev via Docker or local install) / Postgres (prod) |
| Queue | BullMQ 5 + Redis (Upstash or Railway) |
| Resend | |
| Storage | Local disk (dev) / Supabase Storage (prod) |
| Billing | Dodo Payments |
| Monitoring | Sentry |
| Calendar | FullCalendar 6 |
Project Structure
posthive/
├── apps/
│ ├── api/ # Fastify v4 API (Node.js, TypeScript, ESM)
│ │ ├── prisma/ # Schema + migrations
│ │ └── src/
│ │ ├── adapters/ # Platform adapters (Bluesky, Threads, Instagram, LinkedIn, Mastodon, YouTube, Facebook, Pinterest, X/Twitter, Telegram, Nostr, Discord, Tumblr, Lemmy)
│ │ ├── lib/ # Auth, queue, worker, encryption, storage, mailer, plans
│ │ └── routes/ # auth, accounts, jobs, templates, upload, billing, user, apiKeys, publicApi, mcp, oauth
│ ├── web/ # Next.js 16 frontend
│ │ └── src/
│ │ ├── app/ # Pages: compose, jobs, accounts, billing, settings, docs, agent, features, platforms
│ │ └── components/ # Sidebar, CalendarView, BulkScheduleModal, Toast, PlatformPreview, AgentSetupTabs, etc.
│ ├── cli/ # posthive-cli — npm-published shell CLI (login/logout/whoami + full API)
│ └── mcp/ # posthive-mcp — npm-published standalone MCP server
└── package.json # pnpm workspace root
Self-hosting
The fastest path is Docker — no Node.js or pnpm needed on the host. You get two containers (API + Web) backed by Postgres and Redis, all wired up in one docker compose command.
Prerequisites
- Docker Engine 24+ with the Compose v2 plugin (
docker compose, notdocker-compose)
1. Clone and configure
git clone https://github.com/AstaBlackClove/posthive.git
cd posthive
cp apps/api/.env.example .env
Open .env and set at minimum:
# ── Postgres ────────────────────────────────────────────
POSTGRES_PASSWORD=change_me_to_a_strong_password
# ── Secrets (generate each with the command below) ──────
ENCRYPTION_KEY=<64-char hex> # NEVER change after accounts are saved
JWT_ACCESS_SECRET=<64-char hex>
JWT_REFRESH_SECRET=<64-char hex>
# ── URLs ────────────────────────────────────────────────
WEB_URL=http://localhost:3000 # public URL of the web app
PUBLIC_API_URL=http://localhost:3001 # public URL of the API (must be reachable from the internet for Meta/Instagram/Threads image fetches)
Generate secrets:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
ENCRYPTION_KEYencrypts all stored OAuth tokens. Write it down — changing it after accounts are saved makes all connected accounts permanently unusable.
2. Start
docker compose up -d --build
The first build takes 2–5 minutes. Once done, Posthive is running:
| Service | URL |
|---|---|
| Web | http://localhost:3000 |
| API | http://localhost:3001 |
Register your account at /register. Billing is disabled by default — all features are unlocked.
3. Custom domain with a reverse proxy
Set the public URLs in .env before building, then point your reverse proxy at the containers.
Caddy (recommended — auto HTTPS)
yourdomain.com {
reverse_proxy localhost:3000
}
api.yourdomain.com {
reverse_proxy localhost:3001
}
Set in .env:
WEB_URL=https://yourdomain.com
PUBLIC_API_URL=https://api.yourdomain.com
Then rebuild:
docker compose up -d --build
nginx
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
server {
listen 80;
server_name api.yourdomain.com;
location / {
proxy_pass http://localhost:3001;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
4. Persistent uploads
By default, uploaded images are stored inside the container and lost on rebuild. To persist them, add a volume to docker-compose.yml:
api:
...
volumes:
- uploads:/app/apps/api/uploads
volumes:
postgres_data:
redis_data:
uploads: # ← add this
For production, use Supabase Storage instead — set STORAGE_PROVIDER=supabase and the SUPABASE_* vars.
5. Updating
git pull
docker compose up -d --build
Migrations run automatically on API startup.
Prerequisites (developer setup)
- Node.js >= 20
- pnpm >= 9 -
npm install -g pnpm - Redis - Upstash free tier or Railway Redis
- Postgres - Docker Desktop (recommended, zero config
pnpm dev:dbpullspostgres:15automatically on first run) or a local Postgres install
Getting Started
1. Clone and install
git clone https://github.com/AstaBlackClove/posthive.git
cd posthive
pnpm install
2. Configure environment
cp apps/api/.env.example apps/api/.env
Fill in the values see Environment Variables below.
3. Database
Option A - Docker (recommended, no Postgres install needed)
# Start a Postgres container (creates it on first run, starts it on subsequent runs)
pnpm dev:db
Use this DATABASE_URL in apps/api/.env:
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres"
The container is named posthive-pg. Stop it with pnpm db:stop, restart with pnpm db:start.
Option B - SQLite (no Docker, quickest setup)
Change the provider in apps/api/prisma/schema.prisma:
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
Use this DATABASE_URL in apps/api/.env:
DATABASE_URL="file:./dev.db"
SQLite is fine for local development and self-hosting on a single server. Use Postgres in production for reliability and concurrent writes.
4. Run migrations
cd apps/api
pnpm db:migrate
5. Run dev servers
# from project root starts Docker Postgres + API + Web in parallel
pnpm dev
If you chose SQLite (Option B), skip
pnpm dev:dbjust runpnpm dev:apiandpnpm dev:webseparately, or usepnpm devafter commenting out thedev:dbstep.
| Service | URL |
|---|---|
| Web | http://localhost:3000 |
| API | http://localhost:3001 |
| Prisma Studio | pnpm db:studio → http://localhost:5555 |
Environment Variables
apps/api/.env
Core
| Variable | Required | Description |
|---|---|---|
PORT |
No | API port. Defaults to 3001 |
DATABASE_URL |
Yes | postgresql://postgres:postgres@localhost:5432/postgres (Docker dev) or file:./dev.db (SQLite dev) or Postgres URL in prod |
ENCRYPTION_KEY |
Yes | 64-char hex. AES-256-GCM key for credentials. Never change after data is written |
REDIS_URL |
Yes | Upstash or Railway Redis URL |
WEB_URL |
Yes | Frontend origin for CORS + OAuth redirects. http://localhost:3000 in dev |
NODE_ENV |
Prod | Set to production |
SECURE_COOKIES |
Prod | Set to true to require HTTPS for auth cookies |
Auth
| Variable | Required | Description |
|---|---|---|
AUTH_PROVIDER |
No | local (default) or supabase |
JWT_ACCESS_SECRET |
local auth | 64-char hex |
JWT_REFRESH_SECRET |
local auth | 64-char hex |
SUPABASE_URL |
Supabase | https://your-project.supabase.co |
SUPABASE_ANON_KEY |
Supabase | Supabase anon key |
SUPABASE_SERVICE_ROLE_KEY |
Supabase | Supabase service role key |
| Variable | Required | Description |
|---|---|---|
RESEND_API_KEY |
No | Resend API key. Falls back to console in dev |
EMAIL_FROM |
No | Verified sender address. e.g. Posthive <[email protected]> |
Storage
| Variable | Required | Description |
|---|---|---|
STORAGE_PROVIDER |
No | supabase for prod; unset = local disk |
SUPABASE_STORAGE_BUCKET |
Supabase | Bucket name. Defaults to media |
PUBLIC_API_URL |
Public HTTPS URL of the API Meta fetches images from here |
OAuth - Threads
| Variable | Description |
|---|---|
THREADS_APP_ID |
Meta app ID |
THREADS_APP_SECRET |
Meta app secret |
THREADS_REDIRECT_URI |
Must be public HTTPS |
OAuth - Instagram
| Variable | Description |
|---|---|
INSTAGRAM_APP_ID |
Meta app ID |
INSTAGRAM_APP_SECRET |
Meta app secret |
INSTAGRAM_REDIRECT_URI |
Must be public HTTPS |
OAuth - LinkedIn
| Variable | Description |
|---|---|
LINKEDIN_CLIENT_ID |
LinkedIn app client ID |
LINKEDIN_CLIENT_SECRET |
LinkedIn app client secret |
LINKEDIN_REDIRECT_URI |
Must be public HTTPS |
OAuth - Mastodon
| Variable | Description |
|---|---|
MASTODON_CLIENT_ID |
Client key from Mastodon app settings |
MASTODON_CLIENT_SECRET |
Client secret from Mastodon app settings |
MASTODON_REDIRECT_URI |
Must be public HTTPS |
OAuth - YouTube
| Variable | Description |
|---|---|
YOUTUBE_CLIENT_ID |
Google OAuth client ID |
YOUTUBE_CLIENT_SECRET |
Google OAuth client secret |
YOUTUBE_REDIRECT_URI |
Use http://localhost:3001/auth/youtube/callback Google rejects tunnel domains; localhost is exempt |
OAuth - Facebook Pages
| Variable | Description |
|---|---|
FACEBOOK_APP_ID |
Meta app ID |
FACEBOOK_APP_SECRET |
Meta app secret |
FACEBOOK_REDIRECT_URI |
Must be public HTTPS |
OAuth - Pinterest
| Variable | Description |
|---|---|
PINTEREST_CLIENT_ID |
Pinterest App ID from developers.pinterest.com |
PINTEREST_CLIENT_SECRET |
Pinterest App secret key |
PINTEREST_REDIRECT_URI |
Must be public HTTPS |
PINTEREST_SANDBOX |
Set true while app has Trial access routes API calls to the sandbox |
PINTEREST_SANDBOX_TOKEN |
Manually-generated sandbox token (My Apps → Generate Access Token → Sandbox). Only used when PINTEREST_SANDBOX=true |
Pinterest requires Standard access approval for production pin creation. Apply at developers.pinterest.com → My Apps → Request upgraded access. Until approved, set
PINTEREST_SANDBOX=trueand use a sandbox token.
OAuth - X (Twitter)
| Variable | Description |
|---|---|
X_API_KEY |
API Key (Consumer Key) from developer.x.com |
X_API_SECRET |
API Key Secret (Consumer Secret) |
X_CALLBACK_URL |
Must be public HTTPS — OAuth 1.0a callback URL |
Uses OAuth 1.0a (not 2.0). Requires Pro or Team plan when billing is enabled.
OAuth - Discord
| Variable | Description |
|---|---|
DISCORD_CLIENT_ID |
OAuth2 Client ID from discord.com/developers |
DISCORD_CLIENT_SECRET |
OAuth2 Client Secret |
DISCORD_BOT_TOKEN |
Bot token (Bot tab in your Discord app) |
DISCORD_REDIRECT_URI |
Must be public HTTPS — OAuth 2.0 callback URL |
OAuth - Tumblr
| Variable | Description |
|---|---|
TUMBLR_CONSUMER_KEY |
Consumer Key from tumblr.com/oauth/apps |
TUMBLR_CONSUMER_SECRET |
Consumer Secret |
TUMBLR_REDIRECT_URI |
Must match the Default callback URL registered in your Tumblr app exactly |
Tumblr uses OAuth 1.0a. Only one callback URL can be registered — use your production URL.
Billing
| Variable | Required | Description |
|---|---|---|
ENABLE_BILLING |
No | Set to true to enable Dodo Payments and plan limits |
DODO_ENV |
Billing | test_mode or live_mode |
DODO_API_KEY |
Billing | Dodo API key |
DODO_WEBHOOK_SECRET |
Billing | whsec_... webhook signing secret |
DODO_PRODUCT_CREATOR |
Billing | Dodo product ID for Creator plan |
DODO_PRODUCT_PRO |
Billing | Dodo product ID for Pro plan |
DODO_PRODUCT_TEAM |
Billing | Dodo product ID for Team plan |
Monitoring
| Variable | Required | Description |
|---|---|---|
SENTRY_DSN |
No | Sentry DSN. Omit to disable |
Generate secrets:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
apps/web/.env.local
| Variable | Required | Description |
|---|---|---|
NEXT_PUBLIC_API_URL |
Yes | API URL from the browser. http://localhost:3001 in dev |
NEXT_PUBLIC_WEB_URL |
Yes | Public URL of the web app. Used for OG images, sitemap, robots.txt. http://localhost:3000 in dev, https://yourdomain.com in prod |
NEXT_PUBLIC_ENABLE_BILLING |
No | Must match ENABLE_BILLING in the API |
Connecting Platforms
Bluesky
- Go to Accounts in the app
- Enter your handle (e.g.
you.bsky.social) - Generate an app password at bsky.app → Settings → App Passwords
- Enter it and click Connect no OAuth needed
Threads
- Create an app at developers.facebook.com and add the Threads use case
- Set the OAuth redirect URI to
https://your-domain/auth/threads/callback - Add
THREADS_APP_IDandTHREADS_APP_SECRETto.env
In development mode only Threads Testers can connect. Submit for Meta App Review (
threads_basic+threads_content_publish) for public access.
- Add the Instagram product to your Meta app
- Set the OAuth redirect URI to
https://your-domain/auth/instagram/callback - Requires a Professional (Business or Creator) Instagram account
- Create an app at developer.linkedin.com
- Add the Share on LinkedIn and Sign In with LinkedIn using OpenID Connect products
- Set redirect URI to
https://your-domain/auth/linkedin/callback
Mastodon
- Log in to your Mastodon instance → Settings → Development → New Application
- Scopes:
read:accounts,write:statuses,write:media - Set redirect URI to
https://your-domain/auth/mastodon/callback
Works with any Mastodon-compatible instance mastodon.social, fosstodon.org, hachyderm.io, etc.
YouTube
- Create a project in Google Cloud Console
- Enable the YouTube Data API v3
- Create OAuth 2.0 credentials; set redirect URI to
http://localhost:3001/auth/youtube/callback
Google requires app verification for production. Until verified, refresh tokens expire after 7 days.
Facebook Pages
- Use the same Meta app as Threads/Instagram
- Add Manage everything on your Page use case
- Set redirect URI to
https://your-domain/auth/facebook/callback - Requires a Facebook Page personal profiles are not supported by the Graph API
First comment support requires
pages_manage_engagement(pending Meta app review).
- Create an app at developers.pinterest.com
- Enable scopes:
boards:read,pins:read,pins:write,user_accounts:read - Set redirect URI to
https://your-domain/auth/pinterest/callback - Set
PINTEREST_CLIENT_IDandPINTEREST_CLIENT_SECRETin.env
Trial access (default): New apps get Trial access pins can only be created in the sandbox.
- Set
PINTEREST_SANDBOX=true - Generate a sandbox token: My Apps → Manage → Generate Access Token → Sandbox
- Set
PINTEREST_SANDBOX_TOKENto that token
Standard access (production): Apply at developers.pinterest.com → My Apps → Request upgraded access. Once approved, set PINTEREST_SANDBOX=false and remove PINTEREST_SANDBOX_TOKEN. Users connect via normal OAuth.
Posts as Pins on the user's first board. Image is required posts without an image are blocked at the UI level.
X (Twitter)
- Create a project + app at developer.x.com
- Set App permissions to Read and write
- Enable OAuth 1.0a and set the callback URL to
https://your-domain/auth/twitter/callback - Copy the API Key and API Key Secret into
X_API_KEY/X_API_SECRET
Requires Pro or Team plan when billing is enabled. Uses OAuth 1.0a, not OAuth 2.0.
Telegram
No OAuth needed — uses the Telegram Bot API directly.
- Open Telegram and message @BotFather →
/newbot→ follow the prompts to get a bot token - Create a Telegram channel (public or private)
- Add your bot to the channel as an Administrator with at least the Post Messages permission
- Go to Accounts in Posthive → Connect Telegram Channel
- Paste the bot token and your channel username (e.g.
@mychannel) or numeric chat ID (e.g.-1001234567890)
Private channels: use the numeric chat ID. Forward any message from the channel to @userinfobot to get it. One bot can serve multiple channels — connect each channel separately in Posthive. No environment variables needed. Credentials are stored encrypted per-user.
Nostr
No OAuth, no app registration — uses your keypair directly.
- Go to Accounts in Posthive → Connect Nostr
- Paste your
nsec1...private key — or click Generate a new keypair to create a fresh one - Your profile name and photo are fetched automatically from relays
Your
nsecis stored AES-256-GCM encrypted and never exposed. Posts publish as Kind 1 notes to four default relays (Damus, Nostr.band, nos.lol, Snort). Images are appended as URLs in the note content and tagged with NIP-92 imeta for clients that support inline rendering. No environment variables needed.
Discord
OAuth + Bot API. Posts are sent via a webhook auto-created at channel connect time.
- Go to discord.com/developers → New Application → name it Posthive
- Go to Bot tab → Add Bot → copy the bot token
- Go to OAuth2 → General → copy Client ID and Client Secret → add redirect URI
- Add to
.env:
DISCORD_CLIENT_ID="your-client-id"
DISCORD_CLIENT_SECRET="your-client-secret"
DISCORD_BOT_TOKEN="your-bot-token"
DISCORD_REDIRECT_URI="https://your-domain.com/auth/discord/callback"
- Go to Accounts in Posthive → Connect Discord → authorise on Discord → pick a channel
Posthive auto-creates a webhook for the selected channel so messages appear under the Posthive name. Discord shows an APP label on all programmatic posts — this is a Discord platform requirement.
Tumblr
OAuth 1.0a (HMAC-SHA1). Tokens never expire — connect once and it works indefinitely.
- Go to tumblr.com/oauth/apps → Register application (instant, no review)
- Set Default callback URL to
https://your-domain.com/auth/tumblr/callback - Add to
.env:
TUMBLR_CONSUMER_KEY="your-consumer-key"
TUMBLR_CONSUMER_SECRET="your-consumer-secret"
TUMBLR_REDIRECT_URI="https://your-domain.com/auth/tumblr/callback"
- Go to Accounts in Posthive → Connect Tumblr → approve the OAuth prompt
Posts to your primary Tumblr blog using NPF (Neue Post Format). Text and images supported. Tumblr app registration requires no business verification or API review.
Lemmy
Username + password auth. No OAuth, no app registration, no API approval — works with any Lemmy instance.
- Create a Lemmy account on any instance (e.g. lemmy.world, lemmy.ml, beehaw.org)
- Go to Accounts in Posthive → Connect Lemmy
- Enter your instance URL, username, password, and the community to post to (e.g.
[email protected])
No environment variables needed. Credentials are stored AES-256-GCM encrypted. The first line of your post becomes the Lemmy post title; the rest becomes the body. Images are uploaded via pictrs.
Bulk CSV Scheduling
Upload a CSV to schedule multiple posts at once. Available from the Posts page (Bulk button) and the Compose page (Bulk CSV button).
CSV format:
scheduled_for,text,accounts,comment,image_urls
2026-08-01 09:00,Good morning 🌅,all,,
2026-08-02 14:30,Blog post link,bluesky|mastodon,Link in first comment,
2026-08-03 18:00,Skip Instagram today,!instagram,,
2026-08-04 10:00,With an image,linkedin,,https://example.com/image.jpg
2026-08-05 12:00,Two images,bluesky|threads,,https://img1.jpg;https://img2.jpg
Accounts column syntax:
all- all connected accounts except YouTubebluesky|mastodon- specific platforms, pipe-separated!instagram- all platforms except Instagram (and YouTube)all|!instagram|!linkedin- all except Instagram and LinkedIn
YouTube is not supported in bulk scheduling it requires a video file. Use Compose instead. Instagram rows must include at least one image URL.
How Scheduling Works
- Write a post in Compose, pick accounts, set a time
- API creates a
PostJobin the DB and enqueues a BullMQ delayed job - At the scheduled time BullMQ fires the job (~1 second accuracy)
- The worker processes each platform independently:
- Refreshes OAuth tokens if needed
- Posts the main content
- Posts the first comment as a reply (if provided)
- Each step is persisted before the next crash-safe and resumable
- Real-time status updates via Server-Sent Events on the Posts page
Job state machine (per PostJobTarget)
pending → running → post_done → comment_done (= done)
↘ post_failed
↘ comment_failed
Post Templates
Save and reuse post drafts from the Compose page:
- Write a post and click + Save in the POST section header
- Give it a name (must be unique)
- Click Templates to open the dropdown and load any saved template
- Hover a template and click ✕ to delete it
Templates save: post text, first comment, YouTube title/description/type, and Pinterest title/description.
API Reference
Posthive includes a full public REST API for Pro and Team plans (or all users when billing is disabled).
Base URL: https://your-api-domain/api/v1
Authentication: Bearer token — create an API key in Settings.
Authorization: Bearer ph_your_api_key
Endpoints:
| Method | Path | Description |
|---|---|---|
| GET | /me | Identify the authenticated user (used by CLI/MCP login) |
| GET | /accounts | List connected accounts |
| POST | /posts | Schedule a post |
| GET | /posts | List posts (cursor-paginated) |
| GET | /posts/:id | Get single post |
| PATCH | /posts/:id | Update/reschedule pending post |
| POST | /posts/:id/approve | Promote a draft to scheduled |
| POST | /posts/:id/duplicate | Clone a post as a new draft |
| DELETE | /posts/:id | Delete post |
| POST | /upload | Upload media file |
| GET | /templates | List templates |
| POST | /templates | Create template |
See the full documentation for request/response schemas.
MCP — AI Agent Integration
Posthive exposes an MCP (Model Context Protocol) server so AI agents can schedule and manage posts directly. Every client below connects with the same bare URL — no API key to generate, copy, or paste. See posthive.co/agent for a full interactive walkthrough per client.
Requires: Pro or Team plan (self-hosted with billing disabled: unlimited) · Rate limit: 60 req/min
Claude / ChatGPT (OAuth connector, no install)
- Claude: Settings → Connectors → Add custom connector. ChatGPT: Settings → Apps → Advanced settings → enable Developer mode, then Settings → Apps → Add app.
- Enter
https://your-api/mcp - Approve access in the browser prompt that opens
Claude Code
claude mcp add --transport http posthive https://your-api/mcp
The first tool call opens your browser to sign in.
Cursor
Add to .cursor/mcp.json:
{
"mcpServers": {
"posthive": { "url": "https://your-api/mcp" }
}
}
VS Code (GitHub Copilot Chat)
Add to .vscode/mcp.json:
{
"servers": {
"posthive": { "type": "http", "url": "https://your-api/mcp" }
}
}
Codex
Add to ~/.codex/config.toml:
[mcp_servers.posthive]
url = "https://your-api/mcp"
OpenClaw
openclaw mcp set posthive '{"url":"https://your-api/mcp","transport":"streamable-http"}'
Hermes Agent
Add to ~/.hermes/config.yaml:
mcp_servers:
posthive:
url: "https://your-api/mcp"
CLI for shell agents (OpenClaw, custom pipelines, scripts)
Not every agent speaks MCP. posthive-cli is a thin shell wrapper over the same public API:
npx posthive-cli login # opens your browser, no API key needed
npx posthive-cli accounts:list
npx posthive-cli posts:create --content "Hello" --accounts acc_1,acc_2
Every command outputs structured JSON and ships a bundled SKILL.md for agent self-discovery. posthive-mcp (the npm-published MCP server) shares the same login via ~/.posthive/config.json — sign in once, use both.
Fallback: API key in URL
For a client that doesn't support OAuth discovery, embed the key directly instead of the bare URL above: https://your-api/mcp/ph_your_api_key_here. Keep this URL private — revoke and regenerate from Settings → API Keys if it leaks.
Available Tools
| Tool | Description |
|---|---|
list_accounts |
List all connected social accounts |
create_post |
Create a scheduled or draft post |
get_post |
Get a single post by ID |
list_scheduled_posts |
List posts with optional status filter |
approve_draft |
Promote a draft to scheduled |
update_post |
Update content or reschedule a pending post |
duplicate_post |
Clone a post as a new draft |
delete_post |
Delete a post |
list_templates |
List saved post templates |
create_from_template |
Create a post from a template |
Media via MCP
MCP tools accept media_urls (array of public URLs). Binary upload is not possible via MCP — upload files first via POST /api/v1/upload and pass the returned URLs to create_post.
Instagram media_type: post · reel · story
YouTube youtube_type: short · video
Plans
| Plan | Accounts | Posts/month | API Keys |
|---|---|---|---|
| Creator | 3 | 400 | - |
| Pro | 15 | Unlimited | Unlimited |
| Team | 50 | Unlimited | Unlimited |
All plans include a 14-day free trial. Powered by Dodo Payments.
Set ENABLE_BILLING=false for self-hosted mode all features unlocked, no plan limits, no Dodo account needed.
Character Limits
| Platform | Limit |
|---|---|
| Bluesky | 300 graphemes |
| Threads | 500 characters |
| 2,200 characters | |
| 3,000 characters | |
| Mastodon | 500 characters |
| YouTube | Title: 100 · Description: 5,000 |
| Facebook Pages | 63,206 characters |
| Title: 100 · Description: 500 | |
| X (Twitter) | 280 characters |
| Telegram | 4,096 characters |
| Nostr | 10,000 characters |
| Discord | 2,000 characters |
| Tumblr | 4,096 characters |
| Lemmy | 10,000 characters |
Self-Hosting
Posthive is designed to be self-hosted. Billing is optional.
Without billing (default):
# apps/api/.env
ENABLE_BILLING=false
# apps/web/.env.local
NEXT_PUBLIC_ENABLE_BILLING=false
All features are unlocked for all users. No Dodo account needed. Onboarding skips plan selection.
With billing:
ENABLE_BILLING=true
NEXT_PUBLIC_ENABLE_BILLING=true
Create a Dodo Payments account and fill in all DODO_* env vars. Users get a 14-day free trial on signup.
Production Deployment
Recommended stack: Railway (API + Redis) · Supabase (Postgres + Storage) · Vercel (Next.js)
1. Supabase
- Create a project at supabase.com
- Storage → create a bucket named
media→ set it to Public - Settings → Database → Connection String → Session pooler (port 5432) → copy URI
2. Railway - API
- New project → Deploy from GitHub → Root Directory: leave blank (repo root)
- Add a Redis service → copy private URL as
${{ Redis.REDIS_URL }} - Set environment variables (see table above)
- Builder: Railpack (default — do not switch to Dockerfile)
- Build Command:
pnpm install --frozen-lockfile --filter api... && pnpm --filter api exec prisma generate && pnpm --filter api build - Start Command:
cd apps/api && npx prisma migrate deploy && node dist/index.js - Add custom domain → set port to
3001
3. Vercel - Frontend
- Import same repo → Root Directory:
apps/web - Add env vars:
NEXT_PUBLIC_API_URLandNEXT_PUBLIC_ENABLE_BILLING
4. Migrations
Migrations run automatically on deploy via prisma migrate deploy. To create a migration locally:
cd apps/api
npx prisma migrate dev --name describe_your_change
git add prisma/migrations
git commit -m "db: add <describe_your_change> migration"
Use the direct connection URL (not the pooler) when running
prisma migrate devlocally.
Adding a New Platform
- Create
apps/api/src/adapters/<platform>.tsimplementingPlatformAdapter - Register in
apps/api/src/adapters/index.ts - Add OAuth routes in
apps/api/src/routes/auth.ts - Add platform card in
apps/web/src/app/accounts/page.tsx— also add toPLATFORM_METAandRECONNECT_URLS - Add favicon domain in
apps/web/src/components/PlatformIcon.tsx - Add
PLATFORM_COLORandPLATFORM_LIMITinapps/web/src/components/PlatformPreview.tsx - Add preview component in
apps/web/src/components/PlatformPreview.tsx - Add to
PLATFORMS_GRIDinapps/web/src/app/page.tsx(landing page grid + hero card) - Add to
PLATFORMS_NAVinapps/web/src/components/LandingNav.tsx - Add platform data object in
apps/web/src/app/platforms/[platform]/page.tsx - Add docs section in
apps/web/src/app/docs/page.tsx - Add to
NO_COMMENT_PLATFORMSin compose, EditPostDialog, andjobRunner.tsif the platform has no comment API - Add env vars to
apps/api/.env.exampleand document in this README
Community
Join the Posthive Discord for support, feature requests, and to connect with other users:
License
GNU Affero General Public License v3.0 see LICENSE for details.
If you modify this project and run it as a network service, you must make your modified source code available to users of that service.
Installing Posthive
This server has no published package — it is built from source. Open the repository and follow its README.
▸ github.com/AstaBlackClove/posthiveFAQ
Is Posthive MCP free?
Yes, Posthive MCP is free — one-click install via Unyly at no cost.
Does Posthive need an API key?
No, Posthive runs without API keys or environment variables.
Is Posthive hosted or self-hosted?
Self-hosted: the server runs locally on your machine via the install command above.
How do I install Posthive in Claude Desktop, Claude Code or Cursor?
Open Posthive on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.
Related MCPs
Gmail
Read, send and search emails from Claude
by GoogleSlack
Send, search and summarize Slack messages
by SlackRunbear
No-code MCP client for team chat platforms, such as Slack, Microsoft Teams, and Discord.
Discord Server
A community discord server dedicated to MCP by [Frank Fiegel](https://github.com/punkpeye)
Compare Posthive with
Not sure what to pick?
Find your stack in 60 seconds
Author?
Embed badge for your README
Browse similar
All communication MCPs
