HTTP API
No SDK, no MCP. Just POST.
The MCP server is a wrapper. Underneath, all 51 tools are RPC-style HTTP endpoints that take a JSON body and a bearer key. If your agent, script, or backend can make an HTTP request, it can run your channels.
Endpoints
One shape, 51 times
Every tool is a POST to its own path. There are no GETs, no path
parameters, and no query strings — arguments always travel in the JSON
body. A name that isn't a tool returns unknown_tool, and the message lists every valid one, which makes the API
self-describing when you're exploring.
POST https://api.postripple.app/agent/v1/<tool>
Content-Type: application/json
Authorization: Bearer pr_...
{ ...tool arguments, plus optional organizationId } The tool names are exactly the ones in the tool reference.
Authentication
One header, one key
Send the key as a bearer token, or as X-API-Key if that's easier in your client. Both are equivalent; the bearer
header is checked first.
Authorization: Bearer pr_<64 hex characters>
X-API-Key: pr_<64 hex characters> Shown once
A key is pr_ plus 64 hex characters, and it's displayed exactly once when you
create it. Only a SHA-256 hash is stored, so it can't be recovered —
lose it and you create a new one.
Personal, not shared
A key acts as the member who created it and inherits their access. Membership is re-verified on every request, so losing access in the app immediately means losing it through the API.
Scoped, optionally pinned
The key carries only the scopes you checked at creation, and can be
restricted to a single organization. publish is opt-in.
Request & response
Every response is the same envelope
Success is {"ok": true, "data": …}. Failure is {"ok": false, "error": {code, message}}, with an HTTP status that matches the code. Check ok and branch on error.code — the messages are written for humans and can change.
Start with whoami
It runs without an organization, which makes it the right first call: it
tells you what the key can do and where it can do it. Pass organizationId as well and the response adds a workspace overview under organization; without one, a multi-org key gets the list and a hint instead.
curl -s -X POST \
https://api.postripple.app/agent/v1/whoami \
-H "Authorization: Bearer pr_..." \
-H "Content-Type: application/json" \
-d '{}' {
"ok": true,
"data": {
"actingAsUserId": "...",
"key": {
"name": "Claude Code on my laptop",
"prefix": "pr_9f31ab2c",
"scopes": [
"read", "content:write", "publish"
],
"restrictedToOrganizationId": null
},
"organizations": [
{ "organizationId": "org_7fa2...",
"name": "My Brand", "current": false },
{ "organizationId": "org_c41b...",
"name": "Second Brand", "current": false }
],
"organization": null,
"hint": "This key spans multiple
organizations. Pass \"organizationId\"
with other tool calls to pick one."
}
}
Then schedule_post
scheduledFor is epoch milliseconds over HTTP, and must be in the future — 1785600000000 below is 9:00am Pacific on August 1, 2026. Through the MCP server you can
also pass an ISO-8601 datetime and it converts before sending. idempotencyKey is what makes a retry safe: the second call with the same key comes back
as the same post with deduped: true instead of double-posting. Without tiktokSettings, a TikTok post publishes as SELF_ONLY.
curl -s -X POST \
https://api.postripple.app/agent/v1/schedule_post \
-H "Authorization: Bearer pr_..." \
-H "Content-Type: application/json" \
-d '{
"organizationId": "org_7fa2...",
"videoId": "...",
"accountId": "...",
"scheduledFor": 1785600000000,
"caption": "the one that keeps working",
"idempotencyKey": "week32-tiktok-slot1",
"tiktokSettings": {
"privacyLevel": "PUBLIC_TO_EVERYONE",
"disableComment": false,
"disableDuet": false,
"disableStitch": false,
"brandOrganicToggle": false,
"brandContentToggle": false
}
}' {
"ok": true,
"data": {
"postId": "...",
"deduped": false,
"platform": "tiktok",
"account": "mybrand",
"scheduledFor": 1785600000000
}
} {
"ok": false,
"error": {
"code": "ambiguous_org",
"message": "This key can act in multiple
organizations — pass \"organizationId\"
to pick one: ..."
}
} Organizations
Which workspace a call lands in
Nearly every tool needs an organization. Six don't: whoami and the five shared-library list tools — list_hooks, list_green_screens, list_backgrounds, list_music, and list_slideshow_templates, which read platform-wide libraries rather than your workspace.
whoami is the one you use to discover the rest.
| Situation | What happens |
|---|---|
You pass organizationId | It wins over everything, including POSTRIPPLE_ORG. If the key can't act there, you get org_not_accessible. |
| The key has exactly one org | It's inferred. You never have to send the field. |
| Several orgs, nothing specified | ambiguous_org, and the
message lists the options rather than guessing. |
| Through the MCP server | POSTRIPPLE_ORG fills the
field in when a call omits it — handy for pinning one brand per
repo. |
Errors
Every code it can return
This is the complete list. Branch on error.code, not on the message or the status.
| Code | HTTP | Meaning |
|---|---|---|
missing_key | 401 | No pr_ key on the request. Send Authorization: Bearer pr_… or X-API-Key. |
unknown_key | 401 | No key matches the one you sent. |
revoked | 401 | The key was revoked in-app. Create a new one. |
not_a_member | 401 | The member who created the key no longer has access to its organizations. |
insufficient_scope | 403 | The key is missing the scope this tool requires. The message names the scope. |
org_not_accessible | 403 | The key can't act in the organizationId you passed. The message lists the ones it can. |
ambiguous_org | 400 | The key spans several organizations and this tool needs one. Pass organizationId; whoami lists them. |
upgrade_required | 403 / 402 | 403 from auth when the organization has no agent API access. 402 from a tool — either it needs a plan feature the organization doesn't have, or the organization has used up that feature's quota for the period. This is the API's only quota signal. |
invalid_json | 400 | The request body wasn't valid JSON. |
invalid_arguments | 400 | An argument failed validation — a malformed id or the wrong type. The message says which. |
bad_request | 400 | The tool rejected the request on its own terms (publishing a video that isn't ready, a time in the past). Read the message. |
unknown_tool | 404 | No tool by that name. The message enumerates every tool the API exposes. |
internal_error | 500 | Something failed on our side. Retrying is safe when the call carries an idempotencyKey — schedule_post and publish_slideshow take one. |
Behavior
Four rules that apply everywhere
Approval mode changes the outcome
If the workspace requires team approval, schedule_post and publish_slideshow create in-review items instead of scheduling, and return submittedForReview: true. Treat that as a success with a different meaning, and inspect it
with list_reviews. A workspace on approval can't run rotation groups at all — update_group_schedule returns an error when it attaches a group to an account, since that
queue would publish without review.
Everything is audit-logged
Every authenticated call is logged, success or failure — the tool name, an argument summary, its status and duration, and the member the key belongs to. Requests that never get past auth aren't recorded. You read the log in-app under Recent agent activity on the API Keys page.
List tools cap out
Where a tool takes limit, the maximum is 200. Plain listings default to 100; a search —
passing text to list_images, for instance — defaults to 50. Through the MCP server, a larger
value is rejected before the call leaves your machine.
Plan features gate individual tools
API access itself is on every paid plan, but a few tools also check
the feature they spend — video groups, slideshows, video creations,
scheduled posts, and metered image and video generation credits.
Missing the feature or running out of it returns upgrade_required with a 402 rather than failing silently.
One POST away.
Create a scoped key and call whoami from anything that speaks HTTP. Every paid plan includes it.