Errors
Every failure has the same shape, so one handler covers every endpoint.
Error shape
Every failure has the same shape. type is the class to branch on, code is the stable identifier for the specific problem, and param names the offending field where there is one. Treat message as copy for a human reading a log; it can change.
4xx / 5xx
{
"error": {
"type": "invalid_request_error",
"code": "scheduled_for_too_far",
"message": "Posts can be scheduled at most 30 days ahead. The latest accepted time is 2026-10-22T10:00:00.000Z.",
"param": "scheduledFor"
}
}| Type | Means |
|---|---|
| authentication_error | The key is missing, malformed, unknown or revoked. Always 401; code says which. |
| permission_error | The key is valid but lacks the scope, or the account behind it has no active subscription. 403. |
| invalid_request_error | Something about the request is wrong. 400, or 404 for a post that does not exist on your account. |
| rate_limit_error | A ceiling was reached. 429, with Retry-After. |
| api_error | Something broke on our side. 500. Safe to retry with the same idempotency key. |
Branch on code, never on message. The codes are part of the contract; the sentences are not.
Error codes
| Code | Status and meaning |
|---|---|
| api_key_missing | 401 — no Authorization: Bearer header. |
| api_key_malformed | 401 — the value is not shaped like an Antwork key. |
| api_key_unknown | 401 — no key with this id and secret exists. |
| api_key_revoked | 401 — the key was revoked in settings. |
| insufficient_scope | 403 — the key lacks the scope this call needs. |
| subscription_inactive | 403 — the account behind the key has no active subscription. |
| invalid_body | 400 — the body is not a JSON object. |
| accounts_required | 400 — accounts is missing or empty. |
| too_many_accounts | 400 — more accounts than one request allows. |
| invalid_text | 400 — text is not a string. |
| invalid_texts | 400 — texts is not an object of strings keyed by account id. |
| invalid_media | 400 — media is not an array of strings. |
| media_not_hosted | 400 — a media URL is not in your Antwork library. |
| invalid_scheduled_for | 400 — scheduledFor is not a valid ISO 8601 time. |
| scheduled_for_in_past | 400 — scheduledFor is in the past. |
| scheduled_for_too_far | 400 — scheduledFor is beyond the scheduling horizon. |
| workspace_required | 400 — you own several workspaces and sent no workspaceId. |
| workspace_not_found | 400 — workspaceId is not a workspace you own. |
| invalid_option | 400 — an options key is unknown, mistyped or for a platform not in the request. |
| no_targets_accepted | 400 — no account was accepted and none gave a more specific reason. |
| invalid_status | 400 — the status filter is not one of the four post statuses. |
| invalid_limit | 400 — limit is not a whole number in range. |
| invalid_cursor | 400 — cursor did not come from this API. |
| post_not_found | 404 — no such post on your account. |
| idempotency_in_flight | 409 — a request with this Idempotency-Key is still running. |
| idempotency_key_reused | 422 — this Idempotency-Key was used for a different body. |
| rate_limit_burst | 429 — over the per-minute limit. |
| rate_limit_daily | 429 — over the daily limit. |
| internal_error | 500 — something broke on our side. |
When no account in a create request is accepted, the answer is a 400 whose code is the first account's rejection reason (see the next table), with the full posts array beside it.
Why an account was rejected
The reason on a rejected or failed entry in a create response.
| Code | Status and meaning |
|---|---|
| account_unresolved | The id is not a connected account in this workspace. |
| platform_not_available | The account is on a platform the API does not publish to yet. |
| text_empty | No text for this account, and the platform needs some. |
| text_too_long | The text is over this platform's character limit. |
| media_required | The platform needs an image or video and none was attached. |
| unsupported_media | The platform does not accept one of the attached file types. |
| video_too_long | The video is longer than the platform allows. |
| token_invalid | The account's connection has expired. Reconnect it in Antwork. |
| token_refresh_failed | The connection could not be refreshed. Reconnect it in Antwork. |
| quota_exceeded | Your plan's post allowance for this cycle is used up. |
| schedule_failed | The post was created but could not be queued (failed). It exists and will not go out. |