Getting started

Errors

Every failure is { error: { code, message, params }, meta }; branch on code. The table is ordered by how often you will meet each one.

Code reference

codeHTTPMeaningWhat to do
unauthenticated401Missing Bearer credential, or it is invalid, disabled, expired, or not a long-lived credential (popkit_it_…).Check or re-issue the credential under Settings → Integrations → Long-lived credentials; send Authorization: Bearer <credential>.
forbidden403The credential lacks the required scope, the issuer's workspace role forbids the action, or the requested publish tier is not on the credential's allow-list. params carries scope / action.Issue a credential with the required scope / tier; role problems need the workspace admin.
workspace_not_found403The workspace bound to the credential was dissolved, or the issuer is no longer an active member or is banned.Have an admin who is still in the workspace issue a new credential.
not_found404The target does not exist or does not belong to this workspace (not distinguished, to prevent enumeration).Fetch the id again from the list endpoint; never reuse a replyRowId as a commentId.
invalid_input400Invalid input: missing required field, bad enum, timestamp without timezone, broken cursor, publish tier without Idempotency-Key, etc. message names the fields.Fix the fields named in message and retry; the same Idempotency-Key may be reused (failures are not cached).
plan_limit402Plan quota exhausted (e.g. scheduled posts this month). params carries limit / used.Check quotas with GET /v1/workspace; wait for next month or upgrade the plan.
no_account409The target platform has no usable account in this workspace.Have a user connect an account under Accounts in the app; integrations cannot do this.
needs_reauth409The target account's platform authorization has expired.Have the user re-authorize in the app; look for status: "expired" in GET /v1/accounts.
rate_limited429This credential exceeded the per-minute cap for the tier, or hit the daily publish / comment-reply cap or the per-account minimum interval. params carries limit / windowSec / retryAfterSec or reason.Wait for the Retry-After header and retry; daily-cap reasons do not clear until the next day.
replay409A request with the same Idempotency-Key is still in progress.Retry the same key shortly; once finished you get the first result back with meta.replayed: true.
external_writes_off503External writes are disabled in this environment (test-environment safety valve); publishing / replying will not go out.Environment configuration, not an input problem; call the production environment.
platform_error502The platform rejected or failed the request (policy, duplicate, 5xx). params.reason carries a machine code.Inspect per-account errorCode via GET /v1/posts/{postId}; retry through POST /v1/posts/{postId}/publish.
internal500Unclassified server error; it has been logged.Contact us with the meta.requestId from the response.

Example

HTTP/1.1 429 Too Many Requests · Retry-After: 42
{
  "error": {
    "code": "rate_limited",
    "message": "rate limit exceeded for tool create_post (write): 10 calls per 60s",
    "params": {
      "limit": 10,
      "windowSec": 60,
      "retryAfterSec": 42
    }
  },
  "meta": {
    "requestId": "3f9c1e2a-7d4b-4c1e-9a6f-0b2d8e5c7a19"
  }
}

Per-account failures

Publishing is asynchronous: a successful status: "publishing" only means the request was accepted. A later failure on one account does not become an HTTP error; it shows up in the post detail as targets[].status = "failed" with an errorCode (e.g. media_required, token_expired). When any account failed the post's status is failed, and publish / retry with an accountId retries just that one.