REST API
Publish, schedule, moderate and answer direct messages across Instagram, Facebook Pages, Threads, TikTok, Telegram, Discord, Bluesky, Mastodon and WhatsApp over HTTP (X coming soon).
Publish, schedule and moderate posts on Instagram, Facebook Pages, Threads and TikTok, publish to Telegram, Discord, Bluesky and Mastodon (X is coming soon), answer Instagram and Messenger direct messages, and send and receive WhatsApp messages (WhatsApp guide). These pages are a concise reference; the machine-readable spec is at /api/v1/openapi.json and every operation can be tried in the API reference. AI agents can use the same operations through the MCP server.
Basics
- Base URL:
https://staging.postgale.com/api/v1. JSON in, JSON out. - Authentication:
Authorization: Bearer sk_…with a key from Dashboard → API keys. A key acts for its organization only. See Authentication. - Rate limit: 60 requests per minute per key by default; over the limit answers 429. See Errors & rate limits.
- Idempotency: send
x-request-idonPOST /posts. A retry with the same id returns the original post (200 instead of 201). The same text cannot be posted twice within 24 hours. - Approval: when the organization, an account or the key requires approval, new posts are held as
pending_approvaluntil a person approves them in the dashboard (/posts/{id}/approveand/rejectalways answer 403 to API keys). While approval applies, comment replies and moderation, published-post changes, direct messages and X engagement are queued for a person (202 {id, status: "pending_approval"}; follow withGET /actions/{id}or theaction.*webhooks); reads are never blocked. - API keys (
/keys) and organization settings (/org-settings) are dashboard-session only.
Endpoints
Paths are relative to https://staging.postgale.com/api/v1. Request and response schemas for each one are in the API reference.
Accounts & connect
| Method | Path | Description |
|---|---|---|
| GET | /accounts | Connected accounts; ?externalId= filters by your reference |
| GET | /accounts/{id} | One account |
| PATCH | /accounts/{id} | Body {externalId} (≤200 chars, null clears it) |
| GET | /accounts/health | Health of every account: token, scopes, missingPermissions, recommendations (cached 10 min; ?refresh=1) |
| GET | /accounts/{id}/health | Health of one account (?refresh=1) |
| GET | /accounts/{id}/publishing-limit | Posts used/allowed in the 24-hour window (Instagram, Threads) |
| POST | /accounts/{id}/webhook-subscription | Subscribe the account to Meta webhooks (comments / Page feed and the messaging fields) for comment.received and message.* |
| GET | /connect/{platform} | OAuth URL for the account owner (valid 10 min); ?externalId= is stored on the new account(s). WhatsApp: the Embedded Signup page |
| GET | /connect/pending | Unexpired pending connections of the organization, newest first (find the id after an API-started connect) |
| GET | /connect/pending/{id} | Options of a pending connection, e.g. the Facebook Pages to pick from |
| POST | /connect/pending/{id}/select | Body {optionIds}: connect one account per option (expires after 30 min) |
Media
| Method | Path | Description |
|---|---|---|
| GET | /media | The 200 most recent uploads |
| POST | /media | multipart file, or JSON {url | base64, fileName?, mimeType?}; returns publicUrl |
| GET | /media/{id} | One upload |
Posts
| Method | Path | Description |
|---|---|---|
| GET | /posts | The 100 most recent posts with targets; ?status= |
| POST | /posts | Create a draft, scheduled or live post (see Create a post) |
| GET | /posts/{id} | One post with per-target status, platformPostId, URL and error |
| PATCH | /posts/{id} | Body {content}: replace the text of a draft, scheduled, pending_approval or failed post |
| DELETE | /posts/{id} | Delete from Postgale (cancels scheduled posts; published posts stay on the platforms) |
| POST | /posts/{id}/publish | Publish live now (retries failed targets) |
Published posts
| Method | Path | Description |
|---|---|---|
| POST | /posts/{id}/edit | Body {content, accountId?}: edit the live post (Facebook) |
| POST | /posts/{id}/unpublish | Body {accountId?}: delete the live post from the platform (Facebook, Threads) |
| POST | /posts/{id}/comments-settings | Body {enabled, accountId?}: turn comments on or off (Instagram) |
Comments & inbox
| Method | Path | Description |
|---|---|---|
| GET | /posts/{id}/comments | Comments on the published targets of a post |
| POST | /comments/{id}/reply | Body {platformPostId, text}: reply to a comment (Instagram, Facebook, Threads; X coming soon) |
| GET | /inbox/comments | ?postId= or ?accountId=&platformPostId=; limit, after (Instagram, Facebook, Threads) |
| POST | /inbox/comments | Body {accountId, platformPostId, text}: top-level comment as the account |
| POST | /inbox/comments/{commentId}/reply | Body {accountId, text} |
| POST/DELETE | /inbox/comments/{commentId}/hide | Hide / unhide; {accountId} in body or query |
| POST/DELETE | /inbox/comments/{commentId}/like | Like / unlike (Facebook only); {accountId} |
| DELETE | /inbox/comments/{commentId} | Delete a comment (Instagram, Facebook); ?accountId= |
| GET | /inbox/events | Inbound Meta webhook events; ?kind=comment|mention|reply|feed|message|other&accountId&limit |
| GET | /accounts/{id}/mentions | Media the Instagram account was tagged in; ?limit= (default 25) |
Direct messages
| Method | Path | Description |
|---|---|---|
| GET | /inbox/conversations | Instagram, Messenger and WhatsApp conversations, latest first; ?accountId&platform=instagram|facebook|whatsapp&status=active|archived&unread=true&limit&cursor |
| POST | /inbox/conversations/sync | Body {accountId, limit?, pages?}: backfill conversations and their latest messages from Meta |
| GET | /inbox/conversations/{id} | One conversation with its messagingWindow |
| PATCH | /inbox/conversations/{id} | Body {status: active | archived} |
| GET | /inbox/conversations/{id}/messages | Messages, oldest to newest; ?limit&before (nextBefore of the previous page) |
| POST | /inbox/conversations/{id}/messages | Body {text? | mediaUrl? | mediaId?, attachmentType?, tag?: HUMAN_AGENT}: send a live DM (201). WhatsApp also takes template, interactive, location, reaction, whatsappMediaId, replyTo |
| POST | /inbox/conversations/{id}/read | Body {platform?}: mark read, and seen on Meta unless platform is false |
| POST | /inbox/conversations/{id}/sync | Body {limit?}: fetch the latest messages of one conversation from Meta |
Approvals
Agent actions queued while approval applies (see Approval). Approving, editing and rejecting is done by a person in the dashboard.
| Method | Path | Description |
|---|---|---|
| GET | /actions | Queued actions, newest first; ?status=pending_approval|approved|rejected|executed|failed&kind&limit&before |
| GET | /actions/{id} | One action: status, the exact call and text, the result once executed or the error once failed |
See the WhatsApp guide.
| Method | Path | Description |
|---|---|---|
| POST | /whatsapp/connect | Body {accessToken, phoneNumberId, wabaId, pin?, register?, subscribe?, externalId?}: connect a number with an existing token (201) |
| GET | /whatsapp/accounts/{id}/phone-number | Display number, verified name, quality rating, messaging limit, onboarding state; ?refresh=true |
| POST | /whatsapp/accounts/{id}/register | Body {pin?}: (re-)register the number for the Cloud API |
| POST | /whatsapp/accounts/{id}/messages | Body {to | recipient | conversationId, type, …}: send a live message (201); outside 24 hours only templates |
| POST | /whatsapp/accounts/{id}/messages/{messageId}/read | Body {typing?}: mark an inbound message read (blue ticks), optional typing indicator |
| GET | /whatsapp/accounts/{id}/templates | Templates of the WABA; ?status&name&category&language&limit&after |
| POST | /whatsapp/accounts/{id}/templates | Body {name, language, category, components}: submit for review (201) |
| GET | /whatsapp/accounts/{id}/templates/{templateId} | One template |
| DELETE | /whatsapp/accounts/{id}/templates/{templateId} | Delete this language; ?allLanguages=true every language of the name; ?name= |
| POST | /whatsapp/accounts/{id}/media | Body {mediaId | url}: upload to WhatsApp, returns the WhatsApp media id (201) |
| GET | /whatsapp/accounts/{id}/media/{waMediaId} | Media info; ?download=1 streams the bytes |
| DELETE | /whatsapp/accounts/{id}/media/{waMediaId} | Delete the media from WhatsApp |
Meta Ads
See the Meta Ads guide.
| Method | Path | Description |
|---|---|---|
| GET | /ads/accounts | Ad accounts of a connected Facebook account; ?accountId |
| POST | /ads/boost | Body {accountId, adAccountId, postId | platformPostId, budget, schedule?, targeting?, confirm: true}: boost a post (PAUSED by default) |
| GET | /ads/campaigns | ?accountId&adAccountId&statuses&limit&after |
| POST | /ads/campaigns | Body {accountId, adAccountId, name, objective | goal, budget?, confirm: true}: create (PAUSED) |
| GET | /ads/campaigns/{id} | One campaign; ?accountId |
| PATCH | /ads/campaigns/{id} | Body {accountId, name?, status?, budgets?, confirm?}: update or pause |
| GET | /ads/adsets | ?accountId&adAccountId&parentId (campaign) |
| POST | /ads/adsets | Body {accountId, adAccountId, campaignId, name, targeting, dailyBudget | lifetimeBudget, startTime?, endTime?, confirm: true} |
| GET | /ads/adsets/{id} | One ad set |
| PATCH | /ads/adsets/{id} | Update or pause an ad set |
| GET | /ads/creatives | Creatives of the ad account |
| POST | /ads/creatives | Body {objectStoryId | instagramMediaId | link + message + imageUrl, callToAction?} |
| GET | /ads/ads | Ads; ?parentId (ad set) |
| POST | /ads/ads | Body {adSetId, creativeId, name, confirm: true}: create (PAUSED) |
| GET | /ads/ads/{id} | One ad |
| PATCH | /ads/ads/{id} | Update or pause an ad |
| GET | /ads/audiences | Custom audiences |
| POST | /ads/audiences | Body {name, description?}: customer-list audience |
| GET | /ads/audiences/{id} | One audience |
| DELETE | /ads/audiences/{id} | Delete an audience |
| POST | /ads/audiences/{id}/users | Body {emails | phones}: hashed with SHA-256 before upload |
| GET | /ads/insights | ?accountId&objectId&datePreset|since&until&level&breakdowns&timeIncrement |
| GET | /ads/targeting/search | ?accountId&q&type=adinterest|adgeolocation|… |
Analytics
| Method | Path | Description |
|---|---|---|
| GET | /analytics/posts/{id} | Insights per published target; ?metrics=a,b&accountId= |
| GET | /analytics/accounts/{id} | Account insights; ?metrics&period&since&until&metricType&breakdown&timeframe |
| GET | /analytics/accounts/{id}/demographics | ?breakdown=age|city|country|gender&metric&timeframe (Instagram needs 100+ followers) |
| GET | /analytics/accounts/{id}/online-followers | Followers online per hour (Instagram) |
| GET | /analytics/best-time | Best UTC weekday/hour slots from past posts; ?accountId&days=90 |
Webhooks
See Webhooks.
| Method | Path | Description |
|---|---|---|
| GET | /webhooks | Webhook endpoints (secrets are never shown again) |
| POST | /webhooks | Body {url, events?}: returns the signing secret once |
| DELETE | /webhooks?id={id} | Remove an endpoint |
| GET | /webhooks/{id}/deliveries | Newest deliveries; ?status=pending|sending|delivered|failed&limit |
| POST | /webhooks/{id}/test | Queue a webhook.test event to this endpoint only |
| POST | /webhooks/deliveries/{deliveryId}/redeliver | Queue a delivery again (same X-Idempotency-Key) |