Docs

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.

the pattern
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.

both forms are accepted
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.

request
curl -s -X POST \
  https://api.postripple.app/agent/v1/whoami \
  -H "Authorization: Bearer pr_..." \
  -H "Content-Type: application/json" \
  -d '{}'
response · trimmed
{
  "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.

request
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
    }
  }'
response
{
  "ok": true,
  "data": {
    "postId": "...",
    "deduped": false,
    "platform": "tiktok",
    "account": "mybrand",
    "scheduledFor": 1785600000000
  }
}
the failure shape
{
  "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.