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-id on POST /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_approval until a person approves them in the dashboard (/posts/{id}/approve and /reject always 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 with GET /actions/{id} or the action.* 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

MethodPathDescription
GET/accountsConnected accounts; ?externalId= filters by your reference
GET/accounts/{id}One account
PATCH/accounts/{id}Body {externalId} (≤200 chars, null clears it)
GET/accounts/healthHealth of every account: token, scopes, missingPermissions, recommendations (cached 10 min; ?refresh=1)
GET/accounts/{id}/healthHealth of one account (?refresh=1)
GET/accounts/{id}/publishing-limitPosts used/allowed in the 24-hour window (Instagram, Threads)
POST/accounts/{id}/webhook-subscriptionSubscribe 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/pendingUnexpired 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}/selectBody {optionIds}: connect one account per option (expires after 30 min)

Media

MethodPathDescription
GET/mediaThe 200 most recent uploads
POST/mediamultipart file, or JSON {url | base64, fileName?, mimeType?}; returns publicUrl
GET/media/{id}One upload

Posts

MethodPathDescription
GET/postsThe 100 most recent posts with targets; ?status=
POST/postsCreate 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}/publishPublish live now (retries failed targets)

Published posts

MethodPathDescription
POST/posts/{id}/editBody {content, accountId?}: edit the live post (Facebook)
POST/posts/{id}/unpublishBody {accountId?}: delete the live post from the platform (Facebook, Threads)
POST/posts/{id}/comments-settingsBody {enabled, accountId?}: turn comments on or off (Instagram)

Comments & inbox

MethodPathDescription
GET/posts/{id}/commentsComments on the published targets of a post
POST/comments/{id}/replyBody {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/commentsBody {accountId, platformPostId, text}: top-level comment as the account
POST/inbox/comments/{commentId}/replyBody {accountId, text}
POST/DELETE/inbox/comments/{commentId}/hideHide / unhide; {accountId} in body or query
POST/DELETE/inbox/comments/{commentId}/likeLike / unlike (Facebook only); {accountId}
DELETE/inbox/comments/{commentId}Delete a comment (Instagram, Facebook); ?accountId=
GET/inbox/eventsInbound Meta webhook events; ?kind=comment|mention|reply|feed|message|other&accountId&limit
GET/accounts/{id}/mentionsMedia the Instagram account was tagged in; ?limit= (default 25)

Direct messages

MethodPathDescription
GET/inbox/conversationsInstagram, Messenger and WhatsApp conversations, latest first; ?accountId&platform=instagram|facebook|whatsapp&status=active|archived&unread=true&limit&cursor
POST/inbox/conversations/syncBody {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}/messagesMessages, oldest to newest; ?limit&before (nextBefore of the previous page)
POST/inbox/conversations/{id}/messagesBody {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}/readBody {platform?}: mark read, and seen on Meta unless platform is false
POST/inbox/conversations/{id}/syncBody {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.

MethodPathDescription
GET/actionsQueued 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

WhatsApp

See the WhatsApp guide.

MethodPathDescription
POST/whatsapp/connectBody {accessToken, phoneNumberId, wabaId, pin?, register?, subscribe?, externalId?}: connect a number with an existing token (201)
GET/whatsapp/accounts/{id}/phone-numberDisplay number, verified name, quality rating, messaging limit, onboarding state; ?refresh=true
POST/whatsapp/accounts/{id}/registerBody {pin?}: (re-)register the number for the Cloud API
POST/whatsapp/accounts/{id}/messagesBody {to | recipient | conversationId, type, …}: send a live message (201); outside 24 hours only templates
POST/whatsapp/accounts/{id}/messages/{messageId}/readBody {typing?}: mark an inbound message read (blue ticks), optional typing indicator
GET/whatsapp/accounts/{id}/templatesTemplates of the WABA; ?status&name&category&language&limit&after
POST/whatsapp/accounts/{id}/templatesBody {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}/mediaBody {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.

MethodPathDescription
GET/ads/accountsAd accounts of a connected Facebook account; ?accountId
POST/ads/boostBody {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/campaignsBody {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/adsetsBody {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/creativesCreatives of the ad account
POST/ads/creativesBody {objectStoryId | instagramMediaId | link + message + imageUrl, callToAction?}
GET/ads/adsAds; ?parentId (ad set)
POST/ads/adsBody {adSetId, creativeId, name, confirm: true}: create (PAUSED)
GET/ads/ads/{id}One ad
PATCH/ads/ads/{id}Update or pause an ad
GET/ads/audiencesCustom audiences
POST/ads/audiencesBody {name, description?}: customer-list audience
GET/ads/audiences/{id}One audience
DELETE/ads/audiences/{id}Delete an audience
POST/ads/audiences/{id}/usersBody {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

MethodPathDescription
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-followersFollowers online per hour (Instagram)
GET/analytics/best-timeBest UTC weekday/hour slots from past posts; ?accountId&days=90

Webhooks

See Webhooks.

MethodPathDescription
GET/webhooksWebhook endpoints (secrets are never shown again)
POST/webhooksBody {url, events?}: returns the signing secret once
DELETE/webhooks?id={id}Remove an endpoint
GET/webhooks/{id}/deliveriesNewest deliveries; ?status=pending|sending|delivered|failed&limit
POST/webhooks/{id}/testQueue a webhook.test event to this endpoint only
POST/webhooks/deliveries/{deliveryId}/redeliverQueue a delivery again (same X-Idempotency-Key)

On this page