> Markdown version of https://socialcannon.app/docs/errors (Error reference). The index of all SocialCannon documentation is at https://socialcannon.app/llms.txt.

[API Docs](https://socialcannon.app/docs.md)

# Error reference

Every error code the SocialCannon API returns in its JSON responses, where it happens, what it means and the safe next action. Browser OAuth connect redirects report their own errors on the page they return to.

An error response looks like `{ "success": false, "error": "...", "code": "...", "details": { ... } }`. Match on `code`, not on the `error` text, which is written for people and can change. Some responses also carry `data` with the post’s stable ID, `limit` for plan limits, or a `Retry-After` header.

## Before you retry

For every write retry, including after fixing authentication, permissions or rate limits: Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

1. Keep the IDs a response returns. On post endpoints `data.id` (or `data.postId` from retry) is the post’s stable ID; on A/B tests `data.id` is the test and each variant’s post is `data.variants[].postId`; auto-schedule lists its items in `data`.
2. If the response has `data.reconciliationRequired` or `data.providerPublished`, or the code is `IDEMPOTENCY_IN_PROGRESS`, `IDEMPOTENCY_OUTCOME_UNCERTAIN`, `PUBLISH_OUTCOME_UNCERTAIN`, `PROVIDER_RECONCILIATION_REQUIRED` or `ACCOUNT_REFRESH_RECONCILIATION_REQUIRED`, do not send it as a new post or under a new key. If you cannot read the post (posts:read), contact support rather than replacing it. For a create, read the outcome with `GET /api/v1/posts/idempotency` and the original `Idempotency-Key`, or with `GET /api/v1/posts/{id}`; repeating the identical request with the same key is also safe (a create sent without a key must never be repeated), except after `ACCOUNT_REFRESH_RECONCILIATION_REQUIRED`, which must not be repeated until support reconciles the account, and except that a recovery verification is repeated only through `POST /api/v1/accounts/{id}/recovery`, never as a plain create. For an A/B test, read `GET /api/v1/ab-tests/{id}` or repeat it with the same key, except after `ACCOUNT_REFRESH_RECONCILIATION_REQUIRED`, which must not be repeated until support reconciles the account. From the retry endpoint, do not retry such a result again; read the post.
3. Retry only when `details.retryable` is true or the entry below says it is safe. On endpoints that take an `Idempotency-Key` (posts, account recovery verification, A/B tests, auto-schedule, and API client provisioning with the same client_secret), repeat the request with the same key: a replay with the same key never publishes twice.
4. Repurpose takes no Idempotency-Key. A reconciliation code stops retries until support resolves it. Never resend the whole request after any target published or may have published, even when none reports success. Check each result: providerPublished or reconciliationRequired, a published post status, platformPostId, platformPostUrl or a recovery object means stop and reconcile, not retry. Before retrying a missing target, follow the full repurpose recovery rule to identify any saved post or draft, including a draft whose acknowledgement was lost. Retry a failed post or delete it before replacing that target, never both; a draft cannot be retried. Any required deletion must succeed first. If the state is unclear, scopes are missing or deletion fails, contact support instead of retrying. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again. [Full repurpose recovery rule](https://socialcannon.app/docs/errors#repurpose-recovery).
5. A new key starts a new operation (a new post, test or schedule). Use one only after the original is confirmed not published.

## Repurpose recovery

First follow the response code's remedy; ACCOUNT_REFRESH_RECONCILIATION_REQUIRED must be reconciled by support before any resend. Repurpose takes no Idempotency-Key, so the fix is judged against what this request already did: if an earlier attempt of the same request published or may have published some targets, send only the targets that are still missing, never the whole request. Read this response first to tell the two cases apart. As the whole response, a request-level refusal, so no target was processed in it, and this request can then be sent again, but only after confirming an earlier attempt of it published nothing; if it did or may have published some targets, send only the ones that are still missing. As one target's entry in data.results, other targets may already have published, so never resend the whole request; if that entry has providerPublished or reconciliationRequired the post may be live, so check it instead of retrying; otherwise, if the entry has a postId, a post was saved: read it first. If it is published or has a platformPostId, platformPostUrl or recovery object, keep it and do not send that target again. If it is failed with no live markers, either retry it with POST /api/v1/posts/{postId}/retry or delete it and retry only that target in a new request naming just it, never both. If it is a draft, it cannot be retried, so delete it and then send only that target again; the deletion releases its reserved quota. In any other state, or if the deletion fails, contact support. Reading needs the posts:read scope and deleting posts:delete; without them, or if the deletion fails, contact support instead of retrying the target. With no postId and neither providerPublished nor reconciliationRequired, a draft may still have been saved if its acknowledgement was lost. Before retrying that target, list that account's drafts with GET /api/v1/posts?status=draft&platform={platform}&accountId={accountId} (posts:read scope), identify any draft created by this request and delete it with DELETE /api/v1/posts/{id} (posts:delete scope). Only after that deletion succeeds, or after confirming this request saved no draft, send only that missing target again. If you cannot identify the draft, lack the scopes or cannot complete its deletion, contact support instead of retrying. If the owner chose this target's content for the account recovery check post, that content is now live, so do not send that target again. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

## On this page

- [Request and client](https://socialcannon.app/docs/errors#request)
- [Plan and usage limits](https://socialcannon.app/docs/errors#plan)
- [Connected accounts](https://socialcannon.app/docs/errors#account)
- [Media upload and preparation](https://socialcannon.app/docs/errors#media)
- [Content, platform and scheduling](https://socialcannon.app/docs/errors#content)
- [Idempotency keys and replays](https://socialcannon.app/docs/errors#idempotency)
- [Publishing results](https://socialcannon.app/docs/errors#publish)
- [Retrying a failed post](https://socialcannon.app/docs/errors#retry)
- [Post state conflicts](https://socialcannon.app/docs/errors#conflict)
- [Deleting a post](https://socialcannon.app/docs/errors#delete)
- [A/B tests, auto-schedule and repurpose](https://socialcannon.app/docs/errors#bulk)
- [Errors without a code](https://socialcannon.app/docs/errors#without-a-code)

## Request and client

The request itself was refused before any work started.

### `INVALID_IDEMPOTENCY_KEY`

**HTTP status of a failed response:** 400

**Where:** `POST /api/v1/posts`, `GET /api/v1/posts/idempotency`, `POST /api/v1/accounts/{id}/recovery (verify_publishing)`

**What it means**

The Idempotency-Key header is not 8 to 128 printable ASCII characters without spaces. The lookup endpoint also returns it when the header is missing. API client provisioning is different: it needs a canonical lowercase UUIDv4.

**From account recovery verification**

Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again. This replaces the general action below for responses from recovery verification. Repeat or recover a verification only through POST /api/v1/accounts/{id}/recovery. Never use POST /api/v1/posts/{id}/retry or a plain create for it, because only the recovery endpoint closes the account's incident. If the outcome is uncertain or in progress (reconciliationRequired, providerPublished, a recovery object, or an uncertain or in-progress code), never use a new key: repeat the recovery request with the original key or wait for reconciliation (for ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, only wait: do not repeat anything until the account is reconciled), because a new key publishes another real post. First fix the cause this entry describes (reconnect the same network account, complete the check the details name, restore permissions), then repeat or recover through the recovery endpoint. Only when the response settles that nothing was published (a failed post with none of those markers) is that verification over, and after ACCOUNT_REFRESH_RECONCILIATION_REQUIRED only once support has reconciled the account. If that response also carried data.id, read the post first: while it is in progress or its outcome is uncertain (IDEMPOTENCY_IN_PROGRESS, IDEMPOTENCY_OUTCOME_UNCERTAIN, ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, a recovery object, reconciliationRequired or providerPublished), keep the original key and wait or recover through the recovery endpoint only, and never retry or delete it. Once it is a settled failed post with no live markers, delete it (DELETE /api/v1/posts/{id}), or contact support instead: a recovery-verification post is never retried through POST /api/v1/posts/{id}/retry, because that endpoint does not close the account's incident. Only after that deletion succeeds, start a new verification: fix the cause and, with the owner's approval, start a new verification with a new Idempotency-Key. For INVALID_IDEMPOTENCY_KEY, nothing was bound, so correct the key's format and send the recovery request. For IDEMPOTENCY_CONFLICT, the key is already bound to an earlier request: keep that key and read GET /api/v1/posts/idempotency with it first; if the binding is that recovery verification, send the original recovery request unchanged to recover its outcome, and otherwise settle the bound post (or contact support) first, and use a new key only after that verification is confirmed not published and the owner approves. Otherwise apply the key guidance above to the recovery request.

**What to do from any other endpoint**

From POST /api/v1/posts: nothing was created, so fix the key and send the request again. From GET /api/v1/posts/idempotency: the lookup could not run, which says nothing about the original request; fix the key and look up again. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### `IDEMPOTENCY_KEY_REQUIRED`

**HTTP status of a failed response:** 400

**Where:** `POST /api/v1/ab-tests`, `POST /api/v1/posts/auto-schedule`

**What it means**

These endpoints always need an Idempotency-Key header (8 to 128 printable ASCII characters, no spaces).

**What to do**

Create one key for this operation, store it, and send it. Reuse the same key for every retry of the same operation. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### `ENDPOINT_REMOVED`

**HTTP status of a failed response:** 410

**Where:** `POST /api/v1/media/upload`, `GET /api/v1/media/upload`

**What it means**

The old upload endpoint, which buffered files through SocialCannon, was removed.

**What to do**

Upload in three steps: POST /api/v1/media/upload-init, PUT the bytes to the returned signed URL with the exact requiredHeaders, then POST /api/v1/media/upload-complete.

### `TRUSTED_CLIENT_PROVISIONING_FORBIDDEN`

**HTTP status of a failed response:** 403

**Where:** `POST /api/v1/auth/clients`

**What it means**

The calling API client is not currently allowed to create API sub-clients.

**What to do**

Do not retry. Ask SocialCannon support to review this client's provisioning access.

### `TENANT_DELETING`

**HTTP status of a failed response:** 409

**Where:** `POST /api/v1/posts`, `POST /api/v1/media/upload-init`, `POST /api/v1/media/upload-complete`, `POST /api/v1/accounts/{id}/recovery`

**What it means**

This SocialCannon account is being deleted, so it no longer accepts posts, uploads or checks.

**What to do**

Stop sending work for this account. The response sets details.retryable to false where details are present.

## Plan and usage limits

A plan feature or usage limit blocked the request, or one part of it.

### `LIMIT_EXCEEDED`

**HTTP status of a failed response:** 403

**Where:** `POST /api/v1/posts`, `POST /api/v1/posts/thread`, `POST /api/v1/posts/{id}/retry`, `POST /api/v1/ab-tests`, `POST /api/v1/posts/repurpose`, `PATCH /api/v1/posts/{id}`, `POST /api/v1/posts/auto-schedule`, `POST /api/v1/links/generate`, `GET /api/v1/accounts/{id}/timing`, `POST /api/v1/timing/optimal-slots`, `POST /api/v1/accounts/{id}/recovery (verify_publishing)`

**Also returned with HTTP 200:** `POST /api/v1/posts/repurpose`

**What it means**

A plan limit or plan feature blocks the affected operation or target. When present, limit.type says which: platform, twitter_posts_per_period, repurpose_per_period, thread_items, tracked_links, concurrent_ab_tests or sandbox_lifetime. Timing features and repurpose target counts return it without a limit object. posts_per_period, scheduled_posts, media_uploads_per_period are not capped on any current plan.

**If this came from POST /api/v1/posts/repurpose**

From repurpose: first make the fix this code's general guidance below describes (for example reconnect the same network account, change plan, or wait for any Retry-After). First follow the response code's remedy; ACCOUNT_REFRESH_RECONCILIATION_REQUIRED must be reconciled by support before any resend. Repurpose takes no Idempotency-Key, so the fix is judged against what this request already did: if an earlier attempt of the same request published or may have published some targets, send only the targets that are still missing, never the whole request. Read this response first to tell the two cases apart. As the whole response, a request-level refusal, so no target was processed in it, and this request can then be sent again, but only after confirming an earlier attempt of it published nothing; if it did or may have published some targets, send only the ones that are still missing. As one target's entry in data.results, other targets may already have published, so never resend the whole request; if that entry has providerPublished or reconciliationRequired the post may be live, so check it instead of retrying; otherwise, if the entry has a postId, a post was saved: read it first. If it is published or has a platformPostId, platformPostUrl or recovery object, keep it and do not send that target again. If it is failed with no live markers, either retry it with POST /api/v1/posts/{postId}/retry or delete it and retry only that target in a new request naming just it, never both. If it is a draft, it cannot be retried, so delete it and then send only that target again; the deletion releases its reserved quota. In any other state, or if the deletion fails, contact support. Reading needs the posts:read scope and deleting posts:delete; without them, or if the deletion fails, contact support instead of retrying the target. With no postId and neither providerPublished nor reconciliationRequired, a draft may still have been saved if its acknowledgement was lost. Before retrying that target, list that account's drafts with GET /api/v1/posts?status=draft&platform={platform}&accountId={accountId} (posts:read scope), identify any draft created by this request and delete it with DELETE /api/v1/posts/{id} (posts:delete scope). Only after that deletion succeeds, or after confirming this request saved no draft, send only that missing target again. If you cannot identify the draft, lack the scopes or cannot complete its deletion, contact support instead of retrying. If the owner chose this target's content for the account recovery check post, that content is now live, so do not send that target again. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

**From account recovery verification**

Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again. This replaces the general action below for responses from recovery verification. Repeat or recover a verification only through POST /api/v1/accounts/{id}/recovery. Never use POST /api/v1/posts/{id}/retry or a plain create for it, because only the recovery endpoint closes the account's incident. If the outcome is uncertain or in progress (reconciliationRequired, providerPublished, a recovery object, or an uncertain or in-progress code), never use a new key: repeat the recovery request with the original key or wait for reconciliation (for ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, only wait: do not repeat anything until the account is reconciled), because a new key publishes another real post. First fix the cause this entry describes (reconnect the same network account, complete the check the details name, restore permissions), then repeat or recover through the recovery endpoint. Only when the response settles that nothing was published (a failed post with none of those markers) is that verification over, and after ACCOUNT_REFRESH_RECONCILIATION_REQUIRED only once support has reconciled the account. If that response also carried data.id, read the post first: while it is in progress or its outcome is uncertain (IDEMPOTENCY_IN_PROGRESS, IDEMPOTENCY_OUTCOME_UNCERTAIN, ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, a recovery object, reconciliationRequired or providerPublished), keep the original key and wait or recover through the recovery endpoint only, and never retry or delete it. Once it is a settled failed post with no live markers, delete it (DELETE /api/v1/posts/{id}), or contact support instead: a recovery-verification post is never retried through POST /api/v1/posts/{id}/retry, because that endpoint does not close the account's incident. Only after that deletion succeeds, start a new verification: fix the cause and, with the owner's approval, start a new verification with a new Idempotency-Key. For INVALID_IDEMPOTENCY_KEY, nothing was bound, so correct the key's format and send the recovery request. For IDEMPOTENCY_CONFLICT, the key is already bound to an earlier request: keep that key and read GET /api/v1/posts/idempotency with it first; if the binding is that recovery verification, send the original recovery request unchanged to recover its outcome, and otherwise settle the bound post (or contact support) first, and use a new key only after that verification is confirmed not published and the owner approves. Otherwise apply the key guidance above to the recovery request.

**What to do from any other endpoint**

A single create, thread or repurpose target refused this way published nothing. An A/B test may already have published earlier variants, and auto-schedule may already have scheduled earlier items, before the limit was reached: read data, do not use a new key, and resume with the same Idempotency-Key once the limit allows. Fix the limit named by limit.type: twitter_posts_per_period, repurpose_per_period reset next billing period or rise with an upgrade (on Enterprise, contact sales@socialcannon.app for a higher cap); tracked_links needs an upgrade (the API cannot remove links); concurrent_ab_tests needs a running test completed or cancelled; platform and the timing features need a plan that includes them; thread_items is capped at 25 on every paid plan, so a paid-plan caller must reduce the thread to at most that many items; only the Free cap is raised by upgrading; sandbox_lifetime needs a real account; a repurpose target-count refusal (no limit object) needs fewer targetPlatforms or an upgrade. For PATCH, link generation and the timing endpoints, simply send the same request again once the limit allows. Then: a fresh create refused this way has no Idempotency-Key binding, so send the create again, with the request corrected where the limit is about content (a thread of more than the item cap, or a repurpose target count the plan does not allow) (with the same key, if you sent one; threads take no key). If the response carries data.id and details.action "upgrade_plan", the post was saved as failed: retry it with POST /api/v1/posts/{id}/retry. From the retry endpoint itself, call it again; the original create cannot be repeated. For an A/B test or auto-schedule, read data, then repeat the request with the same Idempotency-Key to resume. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

**Also appears in**

Inside an HTTP 200 response from POST /api/v1/posts/repurpose in post mode, as one target's code in data.results: the response itself succeeded, so the status listed above applies only when the same code is returned as a request-level refusal.

### `TIER_FEATURE_DISABLED`

**HTTP status of a failed response:** 403

**Where:** `GET /api/v1/posts/{id}/analytics`, `GET /api/v1/analytics/summary`, `POST /api/v1/analytics/refresh`, `POST /api/v1/engagements/{id}/reply`

**What it means**

Analytics or engagement replies are not included in the current plan.

**What to do**

Do not retry. Upgrade to a plan that includes the feature.

### `PLAN_LOOKUP_RETRYABLE`

**HTTP status of a failed response:** 503

**Where:** `POST /api/v1/posts`, `POST /api/v1/posts/thread`, `POST /api/v1/posts/{id}/retry`, `POST /api/v1/ab-tests`, `POST /api/v1/posts/repurpose`, `PATCH /api/v1/posts/{id}`, `POST /api/v1/posts/auto-schedule`, `POST /api/v1/accounts/{id}/recovery (verify_publishing)`

**Also returned with HTTP 200:** `POST /api/v1/posts/repurpose`

**What it means**

SocialCannon could not verify the plan just now. Where details is present, details.retryable is true.

**If this came from POST /api/v1/posts/repurpose**

From repurpose: first make the fix this code's general guidance below describes (for example reconnect the same network account, change plan, or wait for any Retry-After). First follow the response code's remedy; ACCOUNT_REFRESH_RECONCILIATION_REQUIRED must be reconciled by support before any resend. Repurpose takes no Idempotency-Key, so the fix is judged against what this request already did: if an earlier attempt of the same request published or may have published some targets, send only the targets that are still missing, never the whole request. Read this response first to tell the two cases apart. As the whole response, a request-level refusal, so no target was processed in it, and this request can then be sent again, but only after confirming an earlier attempt of it published nothing; if it did or may have published some targets, send only the ones that are still missing. As one target's entry in data.results, other targets may already have published, so never resend the whole request; if that entry has providerPublished or reconciliationRequired the post may be live, so check it instead of retrying; otherwise, if the entry has a postId, a post was saved: read it first. If it is published or has a platformPostId, platformPostUrl or recovery object, keep it and do not send that target again. If it is failed with no live markers, either retry it with POST /api/v1/posts/{postId}/retry or delete it and retry only that target in a new request naming just it, never both. If it is a draft, it cannot be retried, so delete it and then send only that target again; the deletion releases its reserved quota. In any other state, or if the deletion fails, contact support. Reading needs the posts:read scope and deleting posts:delete; without them, or if the deletion fails, contact support instead of retrying the target. With no postId and neither providerPublished nor reconciliationRequired, a draft may still have been saved if its acknowledgement was lost. Before retrying that target, list that account's drafts with GET /api/v1/posts?status=draft&platform={platform}&accountId={accountId} (posts:read scope), identify any draft created by this request and delete it with DELETE /api/v1/posts/{id} (posts:delete scope). Only after that deletion succeeds, or after confirming this request saved no draft, send only that missing target again. If you cannot identify the draft, lack the scopes or cannot complete its deletion, contact support instead of retrying. If the owner chose this target's content for the account recovery check post, that content is now live, so do not send that target again. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

**From account recovery verification**

Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again. This replaces the general action below for responses from recovery verification. Repeat or recover a verification only through POST /api/v1/accounts/{id}/recovery. Never use POST /api/v1/posts/{id}/retry or a plain create for it, because only the recovery endpoint closes the account's incident. If the outcome is uncertain or in progress (reconciliationRequired, providerPublished, a recovery object, or an uncertain or in-progress code), never use a new key: repeat the recovery request with the original key or wait for reconciliation (for ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, only wait: do not repeat anything until the account is reconciled), because a new key publishes another real post. First fix the cause this entry describes (reconnect the same network account, complete the check the details name, restore permissions), then repeat or recover through the recovery endpoint. Only when the response settles that nothing was published (a failed post with none of those markers) is that verification over, and after ACCOUNT_REFRESH_RECONCILIATION_REQUIRED only once support has reconciled the account. If that response also carried data.id, read the post first: while it is in progress or its outcome is uncertain (IDEMPOTENCY_IN_PROGRESS, IDEMPOTENCY_OUTCOME_UNCERTAIN, ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, a recovery object, reconciliationRequired or providerPublished), keep the original key and wait or recover through the recovery endpoint only, and never retry or delete it. Once it is a settled failed post with no live markers, delete it (DELETE /api/v1/posts/{id}), or contact support instead: a recovery-verification post is never retried through POST /api/v1/posts/{id}/retry, because that endpoint does not close the account's incident. Only after that deletion succeeds, start a new verification: fix the cause and, with the owner's approval, start a new verification with a new Idempotency-Key. For INVALID_IDEMPOTENCY_KEY, nothing was bound, so correct the key's format and send the recovery request. For IDEMPOTENCY_CONFLICT, the key is already bound to an earlier request: keep that key and read GET /api/v1/posts/idempotency with it first; if the binding is that recovery verification, send the original recovery request unchanged to recover its outcome, and otherwise settle the bound post (or contact support) first, and use a new key only after that verification is confirmed not published and the owner approves. Otherwise apply the key guidance above to the recovery request.

**What to do from any other endpoint**

Retry later. For a keyed operation, including A/B tests and auto-schedule, keep the same Idempotency-Key and do not create a replacement post. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

**Also appears in**

Inside an HTTP 200 response from POST /api/v1/posts/repurpose in post mode, as one target's code in data.results: the response itself succeeded, so the status listed above applies only when the same code is returned as a request-level refusal.

## Connected accounts

The social account cannot be used until its access or state is fixed.

### `ACCOUNT_RECONNECT_REQUIRED`

**HTTP status of a failed response:** 409

**Where:** `POST /api/v1/posts`, `POST /api/v1/posts/thread`, `POST /api/v1/posts/{id}/retry`, `POST /api/v1/ab-tests`, `POST /api/v1/posts/repurpose`, `DELETE /api/v1/posts/{id}`, `GET /api/v1/posts/{id}/engagements`, `POST /api/v1/engagements/{id}/reply`, `POST /api/v1/accounts/{id}/recovery (verify_publishing)`

**Also returned with HTTP 200:** `POST /api/v1/posts/repurpose`

**What it means**

The account's access expired or was revoked, a required permission is missing (a top-level missingScopes lists it), or Meta put the account behind a security check. Where details is present, details.retryable is false.

**If this came from POST /api/v1/posts/repurpose**

From repurpose: first make the fix this code's general guidance below describes (for example reconnect the same network account, change plan, or wait for any Retry-After). First follow the response code's remedy; ACCOUNT_REFRESH_RECONCILIATION_REQUIRED must be reconciled by support before any resend. Repurpose takes no Idempotency-Key, so the fix is judged against what this request already did: if an earlier attempt of the same request published or may have published some targets, send only the targets that are still missing, never the whole request. Read this response first to tell the two cases apart. As the whole response, a request-level refusal, so no target was processed in it, and this request can then be sent again, but only after confirming an earlier attempt of it published nothing; if it did or may have published some targets, send only the ones that are still missing. As one target's entry in data.results, other targets may already have published, so never resend the whole request; if that entry has providerPublished or reconciliationRequired the post may be live, so check it instead of retrying; otherwise, if the entry has a postId, a post was saved: read it first. If it is published or has a platformPostId, platformPostUrl or recovery object, keep it and do not send that target again. If it is failed with no live markers, either retry it with POST /api/v1/posts/{postId}/retry or delete it and retry only that target in a new request naming just it, never both. If it is a draft, it cannot be retried, so delete it and then send only that target again; the deletion releases its reserved quota. In any other state, or if the deletion fails, contact support. Reading needs the posts:read scope and deleting posts:delete; without them, or if the deletion fails, contact support instead of retrying the target. With no postId and neither providerPublished nor reconciliationRequired, a draft may still have been saved if its acknowledgement was lost. Before retrying that target, list that account's drafts with GET /api/v1/posts?status=draft&platform={platform}&accountId={accountId} (posts:read scope), identify any draft created by this request and delete it with DELETE /api/v1/posts/{id} (posts:delete scope). Only after that deletion succeeds, or after confirming this request saved no draft, send only that missing target again. If you cannot identify the draft, lack the scopes or cannot complete its deletion, contact support instead of retrying. If the owner chose this target's content for the account recovery check post, that content is now live, so do not send that target again. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

**From account recovery verification**

Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again. This replaces the general action below for responses from recovery verification. Repeat or recover a verification only through POST /api/v1/accounts/{id}/recovery. Never use POST /api/v1/posts/{id}/retry or a plain create for it, because only the recovery endpoint closes the account's incident. If the outcome is uncertain or in progress (reconciliationRequired, providerPublished, a recovery object, or an uncertain or in-progress code), never use a new key: repeat the recovery request with the original key or wait for reconciliation (for ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, only wait: do not repeat anything until the account is reconciled), because a new key publishes another real post. First fix the cause this entry describes (reconnect the same network account, complete the check the details name, restore permissions), then repeat or recover through the recovery endpoint. Only when the response settles that nothing was published (a failed post with none of those markers) is that verification over, and after ACCOUNT_REFRESH_RECONCILIATION_REQUIRED only once support has reconciled the account. If that response also carried data.id, read the post first: while it is in progress or its outcome is uncertain (IDEMPOTENCY_IN_PROGRESS, IDEMPOTENCY_OUTCOME_UNCERTAIN, ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, a recovery object, reconciliationRequired or providerPublished), keep the original key and wait or recover through the recovery endpoint only, and never retry or delete it. Once it is a settled failed post with no live markers, delete it (DELETE /api/v1/posts/{id}), or contact support instead: a recovery-verification post is never retried through POST /api/v1/posts/{id}/retry, because that endpoint does not close the account's incident. Only after that deletion succeeds, start a new verification: fix the cause and, with the owner's approval, start a new verification with a new Idempotency-Key. For INVALID_IDEMPOTENCY_KEY, nothing was bound, so correct the key's format and send the recovery request. For IDEMPOTENCY_CONFLICT, the key is already bound to an earlier request: keep that key and read GET /api/v1/posts/idempotency with it first; if the binding is that recovery verification, send the original recovery request unchanged to recover its outcome, and otherwise settle the bound post (or contact support) first, and use a new key only after that verification is confirmed not published and the owner approves. Otherwise apply the key guidance above to the recovery request.

**What to do from any other endpoint**

Do not retry. The account owner reconnects the same network account (the same Page, profile or handle) in the dashboard under Accounts; if a different account would be connected, do not retry the old post, because it would publish to a different audience. If details.action is "complete_meta_security_check_then_reconnect", they first complete Meta's check on facebook.com. For an engagement reply, check the comment thread before sending it again; do not blindly resend a reply. For an engagement read, repeat that read. For a deletion, retry the deletion. For a fresh post, thread or A/B-test create with no data.id, resend the same request after access is restored; keep the original Idempotency-Key for a keyed post or A/B test (threads take no key). For repurpose, follow the target-specific instructions above, resending only missing targets. For saved posts, follow the rules below. From POST /api/v1/posts/{id}/retry (data.postId is the existing post), fix the cause and call the same retry endpoint again; do not resend the original create. If a POST /api/v1/posts response carries data.id, the post was saved as failed: after reconnecting, retry it with POST /api/v1/posts/{id}/retry instead of resending the create. From an A/B test, data.id is the test: if data.status is active or completed, its variants are already durable, so do not create a replacement; while data.status is provisioning, repeat the same request with the same Idempotency-Key; once it is failed or canceled, the key cannot resume it, so before reusing its variants' content in a replacement under a new key (and never reuse the content of a variant the owner published as the account recovery check post), delete with DELETE /api/v1/posts/{id} every variant post in data.variants that is not already deleted (provisioned, scheduled, awaiting_account, publishing, published, reconciliation_required or failed): a failed one can still be retried, and a published one would be duplicated by a replacement with the same content. If any of those deletions cannot be completed (for example PLATFORM_DELETE_UNSUPPORTED, PUBLISH_IN_PROGRESS or a reconciliation code), do not create the replacement test; contact support. A failed thread cannot be retried: check which items are live before creating a new thread, and do not reuse the content of an item the owner published as the account recovery check post. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

**Also appears in**

Inside an HTTP 200 response from POST /api/v1/posts/repurpose in post mode, as one target's code in data.results: the response itself succeeded, so the status listed above applies only when the same code is returned as a request-level refusal.

### `ACCOUNT_ACTION_REQUIRED`

**HTTP status of a failed response:** 409

**Where:** `POST /api/v1/posts`, `POST /api/v1/posts/thread`, `POST /api/v1/posts/{id}/retry`, `POST /api/v1/ab-tests`, `POST /api/v1/posts/repurpose`, `PATCH /api/v1/posts/{id}`, `POST /api/v1/accounts/{id}/recovery (verify_publishing)`

**Also returned with HTTP 200:** `POST /api/v1/posts/repurpose`

**What it means**

With details (publishing endpoints): the account has an open restriction, such as an identity check, missing Page permissions, a platform review or an earlier publication whose outcome is unresolved. details.action names the fix and details.recoveryUrl where to make it. Without details (PATCH /api/v1/posts/{id} on content held during a restriction): the new schedule is not allowed yet, and the message says why, for example a time less than five minutes ahead, posts closer than five minutes apart, or access not yet restored.

**If this came from POST /api/v1/posts/repurpose**

From repurpose: first make the fix this code's general guidance below describes (for example reconnect the same network account, change plan, or wait for any Retry-After). First follow the response code's remedy; ACCOUNT_REFRESH_RECONCILIATION_REQUIRED must be reconciled by support before any resend. Repurpose takes no Idempotency-Key, so the fix is judged against what this request already did: if an earlier attempt of the same request published or may have published some targets, send only the targets that are still missing, never the whole request. Read this response first to tell the two cases apart. As the whole response, a request-level refusal, so no target was processed in it, and this request can then be sent again, but only after confirming an earlier attempt of it published nothing; if it did or may have published some targets, send only the ones that are still missing. As one target's entry in data.results, other targets may already have published, so never resend the whole request; if that entry has providerPublished or reconciliationRequired the post may be live, so check it instead of retrying; otherwise, if the entry has a postId, a post was saved: read it first. If it is published or has a platformPostId, platformPostUrl or recovery object, keep it and do not send that target again. If it is failed with no live markers, either retry it with POST /api/v1/posts/{postId}/retry or delete it and retry only that target in a new request naming just it, never both. If it is a draft, it cannot be retried, so delete it and then send only that target again; the deletion releases its reserved quota. In any other state, or if the deletion fails, contact support. Reading needs the posts:read scope and deleting posts:delete; without them, or if the deletion fails, contact support instead of retrying the target. With no postId and neither providerPublished nor reconciliationRequired, a draft may still have been saved if its acknowledgement was lost. Before retrying that target, list that account's drafts with GET /api/v1/posts?status=draft&platform={platform}&accountId={accountId} (posts:read scope), identify any draft created by this request and delete it with DELETE /api/v1/posts/{id} (posts:delete scope). Only after that deletion succeeds, or after confirming this request saved no draft, send only that missing target again. If you cannot identify the draft, lack the scopes or cannot complete its deletion, contact support instead of retrying. If the owner chose this target's content for the account recovery check post, that content is now live, so do not send that target again. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

**From account recovery verification**

Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again. This replaces the general action below for responses from recovery verification. Repeat or recover a verification only through POST /api/v1/accounts/{id}/recovery. Never use POST /api/v1/posts/{id}/retry or a plain create for it, because only the recovery endpoint closes the account's incident. If the outcome is uncertain or in progress (reconciliationRequired, providerPublished, a recovery object, or an uncertain or in-progress code), never use a new key: repeat the recovery request with the original key or wait for reconciliation (for ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, only wait: do not repeat anything until the account is reconciled), because a new key publishes another real post. First fix the cause this entry describes (reconnect the same network account, complete the check the details name, restore permissions), then repeat or recover through the recovery endpoint. Only when the response settles that nothing was published (a failed post with none of those markers) is that verification over, and after ACCOUNT_REFRESH_RECONCILIATION_REQUIRED only once support has reconciled the account. If that response also carried data.id, read the post first: while it is in progress or its outcome is uncertain (IDEMPOTENCY_IN_PROGRESS, IDEMPOTENCY_OUTCOME_UNCERTAIN, ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, a recovery object, reconciliationRequired or providerPublished), keep the original key and wait or recover through the recovery endpoint only, and never retry or delete it. Once it is a settled failed post with no live markers, delete it (DELETE /api/v1/posts/{id}), or contact support instead: a recovery-verification post is never retried through POST /api/v1/posts/{id}/retry, because that endpoint does not close the account's incident. Only after that deletion succeeds, start a new verification: fix the cause and, with the owner's approval, start a new verification with a new Idempotency-Key. For INVALID_IDEMPOTENCY_KEY, nothing was bound, so correct the key's format and send the recovery request. For IDEMPOTENCY_CONFLICT, the key is already bound to an earlier request: keep that key and read GET /api/v1/posts/idempotency with it first; if the binding is that recovery verification, send the original recovery request unchanged to recover its outcome, and otherwise settle the bound post (or contact support) first, and use a new key only after that verification is confirmed not published and the owner approves. Otherwise apply the key guidance above to the recovery request.

**What to do from any other endpoint**

Do not retry publishing until the cause is fixed. Without details, change the schedule as the message says, or restore access first, then send the update again; if the message says an earlier publication is unresolved, restoring access does not settle it, so contact support before rescheduling. With details, the owner follows details.action. When that action ends in verify, publishing is verified from the dashboard or with POST /api/v1/accounts/{id}/recovery, which publishes one real post that you choose, so get the owner's approval first; if that verification post carries the content this request was trying to publish, it is now live, so do not also send this request again or retry its saved post (delete a saved failed post with no live markers instead); otherwise apply the steps below once verification succeeds. Reconnect only the same network account (the same Page, profile or handle); if a different account would be connected, do not retry the old post. From POST /api/v1/posts/{id}/retry (data.postId is the existing post), fix the cause and call the same retry endpoint again; do not resend the original create. Once it is fixed, a request that returned no data.id saved nothing: send it again, but for a repurpose request send only the targets that are still missing, because other targets may already have published (with the same key for keyed creates and A/B tests; threads take no key). If a POST /api/v1/posts response carries data.id, the post was saved as failed: once access is restored, retry it with POST /api/v1/posts/{id}/retry. From an A/B test, data.id is the test: if data.status is active or completed, its variants are already durable, so do not create a replacement; while data.status is provisioning, repeat the same request with the same Idempotency-Key; once it is failed or canceled, the key cannot resume it, so before reusing its variants' content in a replacement under a new key (and never reuse the content of a variant the owner published as the account recovery check post), delete with DELETE /api/v1/posts/{id} every variant post in data.variants that is not already deleted (provisioned, scheduled, awaiting_account, publishing, published, reconciliation_required or failed): a failed one can still be retried, and a published one would be duplicated by a replacement with the same content. If any of those deletions cannot be completed (for example PLATFORM_DELETE_UNSUPPORTED, PUBLISH_IN_PROGRESS or a reconciliation code), do not create the replacement test; contact support. A failed thread cannot be retried: check which items are live before creating a new thread, and do not reuse the content of an item the owner published as the account recovery check post. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

**Also appears in**

Inside an HTTP 200 response from POST /api/v1/posts/repurpose in post mode, as one target's code in data.results: the response itself succeeded, so the status listed above applies only when the same code is returned as a request-level refusal.

### `ACCOUNT_REFRESH_RETRYABLE`

**HTTP status of a failed response:** 503

**Where:** `POST /api/v1/posts`, `POST /api/v1/posts/thread`, `POST /api/v1/posts/{id}/retry`, `POST /api/v1/ab-tests`, `POST /api/v1/posts/repurpose`, `DELETE /api/v1/posts/{id}`, `POST /api/v1/accounts/{id}/recovery (verify_publishing)`

**Also returned with HTTP 200:** `POST /api/v1/posts/repurpose`

**What it means**

SocialCannon could not renew the account's access for a temporary reason, so this attempt for the affected post or target did not reach the network unless the response also has reconciliationRequired or providerPublished. In repurpose, other targets may have published. From an A/B test, earlier variants may already be published even without those flags, and data.variants in this response may not show it yet: read GET /api/v1/ab-tests/{id} before judging what is live, and resume only with the original key while it can be resumed.

**If this came from POST /api/v1/posts/repurpose**

From repurpose: first make the fix this code's general guidance below describes (for example reconnect the same network account, change plan, or wait for any Retry-After). First follow the response code's remedy; ACCOUNT_REFRESH_RECONCILIATION_REQUIRED must be reconciled by support before any resend. Repurpose takes no Idempotency-Key, so the fix is judged against what this request already did: if an earlier attempt of the same request published or may have published some targets, send only the targets that are still missing, never the whole request. Read this response first to tell the two cases apart. As the whole response, a request-level refusal, so no target was processed in it, and this request can then be sent again, but only after confirming an earlier attempt of it published nothing; if it did or may have published some targets, send only the ones that are still missing. As one target's entry in data.results, other targets may already have published, so never resend the whole request; if that entry has providerPublished or reconciliationRequired the post may be live, so check it instead of retrying; otherwise, if the entry has a postId, a post was saved: read it first. If it is published or has a platformPostId, platformPostUrl or recovery object, keep it and do not send that target again. If it is failed with no live markers, either retry it with POST /api/v1/posts/{postId}/retry or delete it and retry only that target in a new request naming just it, never both. If it is a draft, it cannot be retried, so delete it and then send only that target again; the deletion releases its reserved quota. In any other state, or if the deletion fails, contact support. Reading needs the posts:read scope and deleting posts:delete; without them, or if the deletion fails, contact support instead of retrying the target. With no postId and neither providerPublished nor reconciliationRequired, a draft may still have been saved if its acknowledgement was lost. Before retrying that target, list that account's drafts with GET /api/v1/posts?status=draft&platform={platform}&accountId={accountId} (posts:read scope), identify any draft created by this request and delete it with DELETE /api/v1/posts/{id} (posts:delete scope). Only after that deletion succeeds, or after confirming this request saved no draft, send only that missing target again. If you cannot identify the draft, lack the scopes or cannot complete its deletion, contact support instead of retrying. If the owner chose this target's content for the account recovery check post, that content is now live, so do not send that target again. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

**From account recovery verification**

Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again. This replaces the general action below for responses from recovery verification. Repeat or recover a verification only through POST /api/v1/accounts/{id}/recovery. Never use POST /api/v1/posts/{id}/retry or a plain create for it, because only the recovery endpoint closes the account's incident. If the outcome is uncertain or in progress (reconciliationRequired, providerPublished, a recovery object, or an uncertain or in-progress code), never use a new key: repeat the recovery request with the original key or wait for reconciliation (for ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, only wait: do not repeat anything until the account is reconciled), because a new key publishes another real post. First fix the cause this entry describes (reconnect the same network account, complete the check the details name, restore permissions), then repeat or recover through the recovery endpoint. Only when the response settles that nothing was published (a failed post with none of those markers) is that verification over, and after ACCOUNT_REFRESH_RECONCILIATION_REQUIRED only once support has reconciled the account. If that response also carried data.id, read the post first: while it is in progress or its outcome is uncertain (IDEMPOTENCY_IN_PROGRESS, IDEMPOTENCY_OUTCOME_UNCERTAIN, ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, a recovery object, reconciliationRequired or providerPublished), keep the original key and wait or recover through the recovery endpoint only, and never retry or delete it. Once it is a settled failed post with no live markers, delete it (DELETE /api/v1/posts/{id}), or contact support instead: a recovery-verification post is never retried through POST /api/v1/posts/{id}/retry, because that endpoint does not close the account's incident. Only after that deletion succeeds, start a new verification: fix the cause and, with the owner's approval, start a new verification with a new Idempotency-Key. For INVALID_IDEMPOTENCY_KEY, nothing was bound, so correct the key's format and send the recovery request. For IDEMPOTENCY_CONFLICT, the key is already bound to an earlier request: keep that key and read GET /api/v1/posts/idempotency with it first; if the binding is that recovery verification, send the original recovery request unchanged to recover its outcome, and otherwise settle the bound post (or contact support) first, and use a new key only after that verification is confirmed not published and the owner approves. Otherwise apply the key guidance above to the recovery request.

**What to do from any other endpoint**

First: if the response also has reconciliationRequired or providerPublished, the post may be live: read it and do not retry. Otherwise: Retry after the Retry-After delay when one is given. For a keyed create, repeat the same request with the same Idempotency-Key. For an A/B test, data.id is the test: while data.status is provisioning, repeat the same request with the same Idempotency-Key; if it is active or completed, its variants are durable, so do not create a replacement; once data.status is failed or canceled, the key cannot resume it, so follow INSUFFICIENT_ELIGIBLE_VARIANTS (before reusing its variants' content, and never reusing the content of a variant the owner published as the account recovery check post, delete every variant post that is not already deleted) before any replacement under a new key. From POST /api/v1/posts/{id}/retry (data.postId), first read GET /api/v1/posts/{id}. Retry that same post only if it is failed with no platformPostId, platformPostUrl or recovery object. If any such marker exists, the post is not failed, or it cannot be read, do not retry; wait for reconciliation or contact support. For a thread with data.id, the failed thread cannot be retried: create a new thread only if the response has neither reconciliationRequired nor providerPublished and no item is live; otherwise read it and contact support. For DELETE /api/v1/posts/{id}, retry the deletion after the delay. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

**Also appears in**

Inside an HTTP 200 response from POST /api/v1/posts/repurpose in post mode, as one target's code in data.results: the response itself succeeded, so the status listed above applies only when the same code is returned as a request-level refusal.

### `ACCOUNT_REFRESH_RECONCILIATION_REQUIRED`

**HTTP status of a failed response:** 503

**Where:** `POST /api/v1/posts`, `POST /api/v1/posts/thread`, `POST /api/v1/posts/{id}/retry`, `POST /api/v1/ab-tests`, `POST /api/v1/posts/repurpose`, `POST /api/v1/accounts/{id}/recovery (verify_publishing)`

**Also returned with HTTP 200:** `POST /api/v1/posts/repurpose`

**What it means**

A credential refresh may have changed the account's access, and its result is unknown. details.retryable is false.

**If this came from POST /api/v1/posts/repurpose**

From repurpose: as the whole response, no target was processed, but hold the request until the account is reconciled, and then send only the targets that are still missing (never the whole request if an earlier request may have published some). As one target's entry in data.results: do not retry that target until then either, and never resend the whole request, because other targets may already have published. Once support has reconciled the account, a target entry with a postId was saved (and if the owner chose this target's content for the account recovery check post, that content is now live, so do not send that target again): read it. If it is published or has a platformPostId, platformPostUrl or recovery object, keep it and do not send that target again; if it is failed with no live markers, either retry it with POST /api/v1/posts/{postId}/retry or delete it before sending that target again, never both; if it is a draft, it cannot be retried, so delete it before sending that target again; in any other state, or if the deletion fails, contact support. A target entry without a postId was refused before anything was saved, so send only that target again, unless the owner already published that content as the account recovery check post, in which case it is live and must not be sent again. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

**From account recovery verification**

Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again. This replaces the general action below for responses from recovery verification. Repeat or recover a verification only through POST /api/v1/accounts/{id}/recovery. Never use POST /api/v1/posts/{id}/retry or a plain create for it, because only the recovery endpoint closes the account's incident. If the outcome is uncertain or in progress (reconciliationRequired, providerPublished, a recovery object, or an uncertain or in-progress code), never use a new key: repeat the recovery request with the original key or wait for reconciliation (for ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, only wait: do not repeat anything until the account is reconciled), because a new key publishes another real post. First fix the cause this entry describes (reconnect the same network account, complete the check the details name, restore permissions), then repeat or recover through the recovery endpoint. Only when the response settles that nothing was published (a failed post with none of those markers) is that verification over, and after ACCOUNT_REFRESH_RECONCILIATION_REQUIRED only once support has reconciled the account. If that response also carried data.id, read the post first: while it is in progress or its outcome is uncertain (IDEMPOTENCY_IN_PROGRESS, IDEMPOTENCY_OUTCOME_UNCERTAIN, ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, a recovery object, reconciliationRequired or providerPublished), keep the original key and wait or recover through the recovery endpoint only, and never retry or delete it. Once it is a settled failed post with no live markers, delete it (DELETE /api/v1/posts/{id}), or contact support instead: a recovery-verification post is never retried through POST /api/v1/posts/{id}/retry, because that endpoint does not close the account's incident. Only after that deletion succeeds, start a new verification: fix the cause and, with the owner's approval, start a new verification with a new Idempotency-Key. For INVALID_IDEMPOTENCY_KEY, nothing was bound, so correct the key's format and send the recovery request. For IDEMPOTENCY_CONFLICT, the key is already bound to an earlier request: keep that key and read GET /api/v1/posts/idempotency with it first; if the binding is that recovery verification, send the original recovery request unchanged to recover its outcome, and otherwise settle the bound post (or contact support) first, and use a new key only after that verification is confirmed not published and the owner approves. Otherwise apply the key guidance above to the recovery request.

**What to do from any other endpoint**

Do not retry publishing, and do not reconnect yet: an earlier operation on the account is unresolved, and changing its credentials can interfere with reconciling it. Contact support; reconnect only once that operation is reconciled. Do not create a replacement post or use a new key in the meantime. Once support has reconciled the account, recover by endpoint. From POST /api/v1/ab-tests, data is the test and data.id is the test ID, never a post ID: read it with GET /api/v1/ab-tests/{id} if you need its state, and to continue, send the original request again with its original Idempotency-Key, which resumes the same test and variants without duplicating posts while data.status is provisioning; if it is active or completed, do not create a replacement; once data.status is failed or canceled, the key cannot resume it, so follow INSUFFICIENT_ELIGIBLE_VARIANTS (before reusing its variants' content, and never reusing the content of a variant the owner published as the account recovery check post, delete every variant post that is not already deleted) before any replacement under a new key. From POST /api/v1/posts with data.id, the post was saved but its outcome may still be open: read it with GET /api/v1/posts/{id} (and, if you sent an Idempotency-Key, GET /api/v1/posts/idempotency with it), and retry it with POST /api/v1/posts/{id}/retry only once it is failed with no live markers and no recovery object, and for a keyed create its outcome is not_published; while it is unresolved, wait for support, keep any original key, and never resend the create. From the retry endpoint (data.postId): read the post and, only if it is failed with no live markers, call the same retry endpoint again. From POST /api/v1/posts without data.id: if the request carried an Idempotency-Key, send the original request again with that key, which resumes the saved draft if this was a resumed create and is otherwise still unbound; never switch to a new key. If it carried no key, nothing was saved, so send the create again once the account is reconciled. Either way, if this came from a repurpose request, send only the targets that are still missing and never the whole request, because other targets may already have published. From POST /api/v1/posts/thread, which takes no key: with data.id, the thread was saved and cannot be retried, so read it and check its items before creating a new thread; if the response also has reconciliationRequired or providerPublished, some items may be live, so do not create a new thread and contact support instead; without data.id, nothing was saved, so send the thread again. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

**Also appears in**

Inside an HTTP 200 response from POST /api/v1/posts/repurpose in post mode, as one target's code in data.results: the response itself succeeded, so the status listed above applies only when the same code is returned as a request-level refusal.

### `ACCOUNT_LOOKUP_RETRYABLE`

**HTTP status of a failed response:** 503

**Where:** `POST /api/v1/posts/repurpose`

**Also returned with HTTP 200:** `POST /api/v1/posts/repurpose`

**What it means**

SocialCannon could not read a target account just now. As the whole response, it means no target was processed; as one entry in data.results, other targets may have published.

**If this came from POST /api/v1/posts/repurpose**

From repurpose: first make the fix this code's general guidance below describes (for example reconnect the same network account, change plan, or wait for any Retry-After). First follow the response code's remedy; ACCOUNT_REFRESH_RECONCILIATION_REQUIRED must be reconciled by support before any resend. Repurpose takes no Idempotency-Key, so the fix is judged against what this request already did: if an earlier attempt of the same request published or may have published some targets, send only the targets that are still missing, never the whole request. Read this response first to tell the two cases apart. As the whole response, a request-level refusal, so no target was processed in it, and this request can then be sent again, but only after confirming an earlier attempt of it published nothing; if it did or may have published some targets, send only the ones that are still missing. As one target's entry in data.results, other targets may already have published, so never resend the whole request; if that entry has providerPublished or reconciliationRequired the post may be live, so check it instead of retrying; otherwise, if the entry has a postId, a post was saved: read it first. If it is published or has a platformPostId, platformPostUrl or recovery object, keep it and do not send that target again. If it is failed with no live markers, either retry it with POST /api/v1/posts/{postId}/retry or delete it and retry only that target in a new request naming just it, never both. If it is a draft, it cannot be retried, so delete it and then send only that target again; the deletion releases its reserved quota. In any other state, or if the deletion fails, contact support. Reading needs the posts:read scope and deleting posts:delete; without them, or if the deletion fails, contact support instead of retrying the target. With no postId and neither providerPublished nor reconciliationRequired, a draft may still have been saved if its acknowledgement was lost. Before retrying that target, list that account's drafts with GET /api/v1/posts?status=draft&platform={platform}&accountId={accountId} (posts:read scope), identify any draft created by this request and delete it with DELETE /api/v1/posts/{id} (posts:delete scope). Only after that deletion succeeds, or after confirming this request saved no draft, send only that missing target again. If you cannot identify the draft, lack the scopes or cannot complete its deletion, contact support instead of retrying. If the owner chose this target's content for the account recovery check post, that content is now live, so do not send that target again. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

**What to do**

Retry later. If it is the whole response, send the request again, but only if no earlier attempt of it published any target; otherwise send only the targets that are still missing. If it is one target's result, retry only that target, never the whole request. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

**Also appears in**

Inside an HTTP 200 response from POST /api/v1/posts/repurpose in post mode, as one target's code in data.results: the response itself succeeded, so the status listed above applies only when the same code is returned as a request-level refusal.

### `ACCOUNT_NOT_FOUND`

**HTTP status of a failed response:** 404

**Where:** `POST /api/v1/media/upload-init`

**What it means**

accountId does not name one of this client's connected accounts.

**What to do**

List accounts with GET /api/v1/accounts and use a current ID.

### `ACCOUNT_INACTIVE`

**HTTP status of a failed response:** 409

**Where:** `POST /api/v1/media/upload-init`

**What it means**

The connected account is disconnected.

**What to do**

Reconnect the account, then start the upload again.

### `ACCOUNT_DISCONNECTED`

**HTTP status of a failed response:** 409

**Where:** `POST /api/v1/accounts/{id}/recovery`

**What it means**

The account is disconnected, so its publishing access cannot be checked.

**What to do**

Reconnect the account first.

### `ACCOUNT_CONNECTION_CHECK_UNAVAILABLE`

**HTTP status of a failed response:** 503

**Where:** `POST /api/v1/accounts/{id}/recovery`

**What it means**

The read-only connection check could not complete. details.publishingVerified is false.

**What to do**

Try the check again later.

### `ACCOUNT_RECOVERY_NOT_READY`

**HTTP status of a failed response:** 409

**Where:** `POST /api/v1/accounts/{id}/recovery`

**What it means**

From recheck_connection: the account was not found or is disconnected. From verify_publishing: the account's recovery changed, is already being checked, or an earlier network operation is unresolved.

**What to do**

From recheck_connection, reconnect the exact account, then check again. From verify_publishing, reload the account and review its recovery status in the dashboard, and do not send another verification publication.

### `PROVIDER_MUTATION_IN_PROGRESS`

**HTTP status of a failed response:** 409

**Where:** `DELETE /api/v1/accounts/{id}`

**What it means**

A publish or delete on this account is waiting for a recorded outcome. data.reconciliationRequired is true.

**What to do**

Wait for the operation to settle, or contact support, before disconnecting the account.

## Media upload and preparation

A file could not be accepted, fetched or prepared for publishing.

### `INVALID_REQUEST`

**HTTP status of a failed response:** 400

**Where:** `POST /api/v1/media/upload-init`

**What it means**

The request is missing a field it needs: filename, contentType, a positive size, or a usable accountId (an empty or non-string ID) when the destination policy is resolved.

**What to do**

Fix the field the message names and send the upload again. For accountId, send a connected account's ID, or leave it out to use the general per-file limit. For filename, contentType or size, send them with the values the upload-init request expects.

### `INVALID_POST_TYPE`

**HTTP status of a failed response:** 400

**Where:** `POST /api/v1/media/upload-init`

**What it means**

postType must be one of: feed, reel, story, short, carousel, thread.

**What to do**

Send the post type the file will be published as. SocialCannon never falls back to another type.

### `UNSUPPORTED_MEDIA`

**HTTP status of a failed response:** 400

**Where:** `POST /api/v1/media/upload-init`

**What it means**

This file type cannot be published to that account and post type, or the account is the sandbox, which stores no media at all.

**What to do**

For a real account, use an accepted format from the Media Upload Limits table in the API reference. For the sandbox, no upload works: test without media, or upload for a real account.

### `MEDIA_LIMIT_EXCEEDED`

**HTTP status of a failed response:** 400

**Where:** `POST /api/v1/media/upload-init`

**What it means**

The file is larger than the destination's per-file limit, or than the general limit when no accountId is sent. No upload URL was issued.

**What to do**

Use a smaller file. The Media Upload Limits table lists each limit.

### `MEDIA_UPLOAD_AUTHORITY_UNAVAILABLE`

**HTTP status of a failed response:** 409

**Where:** `POST /api/v1/media/upload-init`

**What it means**

This API client no longer accepts new media uploads.

**What to do**

Do not retry. Contact support.

### `MEDIA_INACCESSIBLE`

**HTTP status of a failed response:** 400

**Where:** `POST /api/v1/posts`, `POST /api/v1/ab-tests`, `POST /api/v1/posts/{id}/retry`, `POST /api/v1/accounts/{id}/recovery (verify_publishing)`

**What it means**

A media URL could not be fetched: it is not public, has expired or was deleted. For a keyed POST /api/v1/posts that carries data.id, the post was saved as failed and its key is settled.

**From account recovery verification**

Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again. This replaces the general action below for responses from recovery verification. Repeat or recover a verification only through POST /api/v1/accounts/{id}/recovery. Never use POST /api/v1/posts/{id}/retry or a plain create for it, because only the recovery endpoint closes the account's incident. If the outcome is uncertain or in progress (reconciliationRequired, providerPublished, a recovery object, or an uncertain or in-progress code), never use a new key: repeat the recovery request with the original key or wait for reconciliation (for ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, only wait: do not repeat anything until the account is reconciled), because a new key publishes another real post. First fix the cause this entry describes (reconnect the same network account, complete the check the details name, restore permissions), then repeat or recover through the recovery endpoint. Only when the response settles that nothing was published (a failed post with none of those markers) is that verification over, and after ACCOUNT_REFRESH_RECONCILIATION_REQUIRED only once support has reconciled the account. If that response also carried data.id, read the post first: while it is in progress or its outcome is uncertain (IDEMPOTENCY_IN_PROGRESS, IDEMPOTENCY_OUTCOME_UNCERTAIN, ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, a recovery object, reconciliationRequired or providerPublished), keep the original key and wait or recover through the recovery endpoint only, and never retry or delete it. Once it is a settled failed post with no live markers, delete it (DELETE /api/v1/posts/{id}), or contact support instead: a recovery-verification post is never retried through POST /api/v1/posts/{id}/retry, because that endpoint does not close the account's incident. Only after that deletion succeeds, start a new verification: fix the cause and, with the owner's approval, start a new verification with a new Idempotency-Key. For INVALID_IDEMPOTENCY_KEY, nothing was bound, so correct the key's format and send the recovery request. For IDEMPOTENCY_CONFLICT, the key is already bound to an earlier request: keep that key and read GET /api/v1/posts/idempotency with it first; if the binding is that recovery verification, send the original recovery request unchanged to recover its outcome, and otherwise settle the bound post (or contact support) first, and use a new key only after that verification is confirmed not published and the owner approves. Otherwise apply the key guidance above to the recovery request.

**What to do from any other endpoint**

First: if the response also has reconciliationRequired or providerPublished, the post may be live: read it and do not retry. Otherwise: If a post was saved (data.id, or the post ID used at the retry endpoint), read it first with GET /api/v1/posts/{id} (needs posts:read). If it is published, has a platformPostId, platformPostUrl or recovery object, is not failed, or cannot be read, do not create a replacement; contact support if it cannot be settled. Only for a failed post with no live markers: if its stored media is reachable again, retry that post with POST /api/v1/posts/{id}/retry; otherwise delete it with DELETE /api/v1/posts/{id} and, only after deletion succeeds, upload the media again and create a new post with a new key. Retrying cannot replace stored media, and a keyed create with data.id has a settled key: repeating that create will not publish. For an immediate create with no saved post, make the file publicly reachable or upload it again, then send the corrected request, retaining any original key. A scheduled post must use media uploaded through SocialCannon. For an A/B test, this media check runs before the test is created, so nothing is bound to the key yet: fix the media and send the request with the same key. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### `MEDIA_STORAGE_UNAVAILABLE`

**HTTP status of a failed response:** 503

**Where:** `POST /api/v1/posts`, `POST /api/v1/posts/thread`, `POST /api/v1/ab-tests`, `PATCH /api/v1/posts/{id}`, `POST /api/v1/accounts/{id}/recovery (verify_publishing)`

**Also returned with HTTP 200:** `POST /api/v1/posts/repurpose`

**What it means**

Storage could not prepare the media in time, so the affected post or target did not publish. In repurpose, other targets may have published; this does not mean the whole request published nothing.

**If this came from POST /api/v1/posts/repurpose**

From repurpose: first make the fix this code's general guidance below describes (for example reconnect the same network account, change plan, or wait for any Retry-After). First follow the response code's remedy; ACCOUNT_REFRESH_RECONCILIATION_REQUIRED must be reconciled by support before any resend. Repurpose takes no Idempotency-Key, so the fix is judged against what this request already did: if an earlier attempt of the same request published or may have published some targets, send only the targets that are still missing, never the whole request. Read this response first to tell the two cases apart. As the whole response, a request-level refusal, so no target was processed in it, and this request can then be sent again, but only after confirming an earlier attempt of it published nothing; if it did or may have published some targets, send only the ones that are still missing. As one target's entry in data.results, other targets may already have published, so never resend the whole request; if that entry has providerPublished or reconciliationRequired the post may be live, so check it instead of retrying; otherwise, if the entry has a postId, a post was saved: read it first. If it is published or has a platformPostId, platformPostUrl or recovery object, keep it and do not send that target again. If it is failed with no live markers, either retry it with POST /api/v1/posts/{postId}/retry or delete it and retry only that target in a new request naming just it, never both. If it is a draft, it cannot be retried, so delete it and then send only that target again; the deletion releases its reserved quota. In any other state, or if the deletion fails, contact support. Reading needs the posts:read scope and deleting posts:delete; without them, or if the deletion fails, contact support instead of retrying the target. With no postId and neither providerPublished nor reconciliationRequired, a draft may still have been saved if its acknowledgement was lost. Before retrying that target, list that account's drafts with GET /api/v1/posts?status=draft&platform={platform}&accountId={accountId} (posts:read scope), identify any draft created by this request and delete it with DELETE /api/v1/posts/{id} (posts:delete scope). Only after that deletion succeeds, or after confirming this request saved no draft, send only that missing target again. If you cannot identify the draft, lack the scopes or cannot complete its deletion, contact support instead of retrying. If the owner chose this target's content for the account recovery check post, that content is now live, so do not send that target again. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

**From account recovery verification**

Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again. This replaces the general action below for responses from recovery verification. Repeat or recover a verification only through POST /api/v1/accounts/{id}/recovery. Never use POST /api/v1/posts/{id}/retry or a plain create for it, because only the recovery endpoint closes the account's incident. If the outcome is uncertain or in progress (reconciliationRequired, providerPublished, a recovery object, or an uncertain or in-progress code), never use a new key: repeat the recovery request with the original key or wait for reconciliation (for ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, only wait: do not repeat anything until the account is reconciled), because a new key publishes another real post. First fix the cause this entry describes (reconnect the same network account, complete the check the details name, restore permissions), then repeat or recover through the recovery endpoint. Only when the response settles that nothing was published (a failed post with none of those markers) is that verification over, and after ACCOUNT_REFRESH_RECONCILIATION_REQUIRED only once support has reconciled the account. If that response also carried data.id, read the post first: while it is in progress or its outcome is uncertain (IDEMPOTENCY_IN_PROGRESS, IDEMPOTENCY_OUTCOME_UNCERTAIN, ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, a recovery object, reconciliationRequired or providerPublished), keep the original key and wait or recover through the recovery endpoint only, and never retry or delete it. Once it is a settled failed post with no live markers, delete it (DELETE /api/v1/posts/{id}), or contact support instead: a recovery-verification post is never retried through POST /api/v1/posts/{id}/retry, because that endpoint does not close the account's incident. Only after that deletion succeeds, start a new verification: fix the cause and, with the owner's approval, start a new verification with a new Idempotency-Key. For INVALID_IDEMPOTENCY_KEY, nothing was bound, so correct the key's format and send the recovery request. For IDEMPOTENCY_CONFLICT, the key is already bound to an earlier request: keep that key and read GET /api/v1/posts/idempotency with it first; if the binding is that recovery verification, send the original recovery request unchanged to recover its outcome, and otherwise settle the bound post (or contact support) first, and use a new key only after that verification is confirmed not published and the owner approves. Otherwise apply the key guidance above to the recovery request.

**What to do from any other endpoint**

Retry later. For a keyed create or an A/B test, use the same Idempotency-Key. For a thread, send the same thread request again after the delay; for PATCH, send the same update again after the delay. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

**Also appears in**

Inside an HTTP 200 response from POST /api/v1/posts/repurpose in post mode, as one target's code in data.results: the response itself succeeded, so the status listed above applies only when the same code is returned as a request-level refusal.

### `SCHEDULED_MEDIA_EXTERNAL_REQUIRES_UPLOAD`

**HTTP status of a failed response:** 400

**Where:** `POST /api/v1/posts`, `POST /api/v1/posts/thread`, `POST /api/v1/ab-tests`, `PATCH /api/v1/posts/{id}`

**What it means**

Scheduled posts cannot point at media hosted elsewhere, because that file could change or disappear before publishing.

**What to do**

Upload the file through /api/v1/media/upload-init and upload-complete, then create the scheduled post with the returned publicUrl. If a keyed POST /api/v1/posts response carries data.id, that key is settled and repeating it will not create the post: read the failed post, and if it has no recovery object, platformPostId or platformPostUrl, delete it (DELETE /api/v1/posts/{id}) and create a new post with a new key (do not use the retry endpoint to schedule it). For an A/B test, this check runs before the test is created, so nothing was provisioned and the key is still unbound: fix the media and send the request again. For a refused thread create (nothing was saved), send it again with the uploaded media. From PATCH: an update cannot replace a post's media. First read the post with GET /api/v1/posts/{id} (needs posts:read; deleting needs posts:delete, and without both, contact support instead): if it has an accountHold, a recovery object, a platformPostId or a platformPostUrl, do not replace it; contact support. Otherwise delete it (DELETE /api/v1/posts/{id}), and only after the deletion succeeds create a replacement with uploaded media; never create a second post or thread while the original is still scheduled. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### `SCHEDULED_MEDIA_NOT_OWNED`

**HTTP status of a failed response:** 400, 403

**Where:** `POST /api/v1/posts`, `POST /api/v1/posts/thread`, `POST /api/v1/ab-tests`, `PATCH /api/v1/posts/{id}`, `POST /api/v1/posts/{id}/retry`

**What it means**

The media was not uploaded by this API client, or its ownership cannot be verified.

**What to do**

For a create, upload the file with this client's own credentials and use that publicUrl. If a keyed POST /api/v1/posts response carries data.id, that key is settled and repeating it will not create the post: read the failed post, and if it has no recovery object, platformPostId or platformPostUrl, delete it (DELETE /api/v1/posts/{id}) and create a new post with a new key (do not use the retry endpoint to schedule it). From POST /api/v1/posts/{id}/retry: first read the failed post with GET /api/v1/posts/{id} (needs posts:read); if it has a platformPostId, a platformPostUrl or a recovery object (SocialCannon shows one whenever an earlier attempt is unresolved), an earlier attempt may be live, so do not create a replacement and contact support. If you cannot read the post, do not create a replacement either. Only if it has neither, delete it (DELETE /api/v1/posts/{id}), upload the media again and create a new post; retrying cannot replace a failed post's media. For an A/B test, this check runs before the test is created, so nothing was provisioned and the key is still unbound: fix the media and send the request again. For a refused thread create (nothing was saved), send it again with the uploaded media. From PATCH: an update cannot replace a post's media. First read the post with GET /api/v1/posts/{id} (needs posts:read; deleting needs posts:delete, and without both, contact support instead): if it has an accountHold, a recovery object, a platformPostId or a platformPostUrl, do not replace it; contact support. Otherwise delete it (DELETE /api/v1/posts/{id}), and only after the deletion succeeds create a replacement with uploaded media; never create a second post or thread while the original is still scheduled. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### `SCHEDULED_MEDIA_UNAVAILABLE`

**HTTP status of a failed response:** 400

**Where:** `POST /api/v1/posts`, `POST /api/v1/posts/thread`, `POST /api/v1/ab-tests`, `PATCH /api/v1/posts/{id}`, `POST /api/v1/posts/{id}/retry`

**What it means**

The uploaded media is no longer available.

**What to do**

Upload the media again, then send the create with the new publicUrl. If a keyed POST /api/v1/posts response carries data.id, that key is settled and repeating it will not create the post: read the failed post, and if it has no recovery object, platformPostId or platformPostUrl, delete it (DELETE /api/v1/posts/{id}) and create a new post with a new key (do not use the retry endpoint to schedule it). From POST /api/v1/posts/{id}/retry: first read the failed post with GET /api/v1/posts/{id} (needs posts:read); if it has a platformPostId, a platformPostUrl or a recovery object (SocialCannon shows one whenever an earlier attempt is unresolved), an earlier attempt may be live, so do not create a replacement and contact support. If you cannot read the post, do not create a replacement either. Only if it has neither, delete it (DELETE /api/v1/posts/{id}), upload the media again and create a new post; retrying cannot replace a failed post's media. For an A/B test, this check runs before the test is created, so nothing was provisioned and the key is still unbound: fix the media and send the request again. For a refused thread create (nothing was saved), send it again with the uploaded media. From PATCH: an update cannot replace a post's media. First read the post with GET /api/v1/posts/{id} (needs posts:read; deleting needs posts:delete, and without both, contact support instead): if it has an accountHold, a recovery object, a platformPostId or a platformPostUrl, do not replace it; contact support. Otherwise delete it (DELETE /api/v1/posts/{id}), and only after the deletion succeeds create a replacement with uploaded media; never create a second post or thread while the original is still scheduled. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### `SCHEDULED_MEDIA_STORAGE_FAILED`

**HTTP status of a failed response:** 503

**Where:** `POST /api/v1/posts`, `POST /api/v1/posts/thread`, `POST /api/v1/ab-tests`, `PATCH /api/v1/posts/{id}`, `POST /api/v1/posts/{id}/retry`

**What it means**

The media could not be prepared or verified in storage just now.

**What to do**

Try again later. For POST /api/v1/posts, retain the original Idempotency-Key and read GET /api/v1/posts/idempotency with it before resending. If the response carries data.id, read that existing post with GET /api/v1/posts/{id}; do not create another post while its outcome is unresolved or it has a recovery object, platformPostId or platformPostUrl. If it is failed without those markers and the keyed outcome is not_published, either retry that post with POST /api/v1/posts/{id}/retry (the retry publishes immediately), or delete it with DELETE /api/v1/posts/{id} and, only after deletion succeeds, create a replacement with the wanted schedule and a new key; never do both. If it remains a draft or cannot be read, contact support before replacing it. With no data.id, repeat the unchanged create with the original key; without a key, nothing was saved by this preparation failure, so repeat the create. For thread or A/B-test creation this preparation check precedes saving the thread/test; repeat the request unchanged, keeping the original key for the A/B test. From PATCH, repeat the same update; from the retry endpoint, read the existing post first and repeat the same retry only once it is failed without a recovery object or provider identity. Do not replace a live or unresolved post. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### `SCHEDULED_MEDIA_REQUIRES_REUPLOAD`

**HTTP status of a failed response:** 400

**Where:** `POST /api/v1/posts/{id}/retry`

**What it means**

This older scheduled post's media is not pinned to an exact uploaded file, so it cannot be published safely.

**What to do**

From POST /api/v1/posts/{id}/retry: first read the failed post with GET /api/v1/posts/{id} (needs posts:read); if it has a platformPostId, a platformPostUrl or a recovery object (SocialCannon shows one whenever an earlier attempt is unresolved), an earlier attempt may be live, so do not create a replacement and contact support. If you cannot read the post, do not create a replacement either. Only if it has neither, delete it (DELETE /api/v1/posts/{id}), upload the media again and create a new post; retrying cannot replace a failed post's media. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### `SCHEDULED_MEDIA_GRAPH_INVALID`

**HTTP status of a failed response:** 400

**Where:** `POST /api/v1/posts/{id}/retry`

**What it means**

The post's stored media record is malformed.

**What to do**

From POST /api/v1/posts/{id}/retry: first read the failed post with GET /api/v1/posts/{id} (needs posts:read); if it has a platformPostId, a platformPostUrl or a recovery object (SocialCannon shows one whenever an earlier attempt is unresolved), an earlier attempt may be live, so do not create a replacement and contact support. If you cannot read the post, do not create a replacement either. Only if it has neither, delete it (DELETE /api/v1/posts/{id}), upload the media again and create a new post; retrying cannot replace a failed post's media. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### `SCHEDULED_MEDIA_GRAPH_CONFLICT`

**HTTP status of a failed response:** 409

**Where:** `PATCH /api/v1/posts/{id}`

**What it means**

The post's media changed while it was being prepared for scheduling.

**What to do**

Retry the update. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

## Content, platform and scheduling

The post, the platform or the requested time does not fit the rules.

### `TIKTOK_PRIVACY_REQUIRED`

**HTTP status of a failed response:** 400

**Where:** `POST /api/v1/posts`, `POST /api/v1/accounts/{id}/recovery (verify_publishing)`

**What it means**

TikTok posts need platformOptions.privacyLevel. TikTok does not allow a default. A/B tests refuse TikTok altogether with PLATFORM_UNSUPPORTED, because a variant cannot set its own privacy level.

**From account recovery verification**

Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again. This replaces the general action below for responses from recovery verification. Repeat or recover a verification only through POST /api/v1/accounts/{id}/recovery. Never use POST /api/v1/posts/{id}/retry or a plain create for it, because only the recovery endpoint closes the account's incident. If the outcome is uncertain or in progress (reconciliationRequired, providerPublished, a recovery object, or an uncertain or in-progress code), never use a new key: repeat the recovery request with the original key or wait for reconciliation (for ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, only wait: do not repeat anything until the account is reconciled), because a new key publishes another real post. First fix the cause this entry describes (reconnect the same network account, complete the check the details name, restore permissions), then repeat or recover through the recovery endpoint. Only when the response settles that nothing was published (a failed post with none of those markers) is that verification over, and after ACCOUNT_REFRESH_RECONCILIATION_REQUIRED only once support has reconciled the account. If that response also carried data.id, read the post first: while it is in progress or its outcome is uncertain (IDEMPOTENCY_IN_PROGRESS, IDEMPOTENCY_OUTCOME_UNCERTAIN, ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, a recovery object, reconciliationRequired or providerPublished), keep the original key and wait or recover through the recovery endpoint only, and never retry or delete it. Once it is a settled failed post with no live markers, delete it (DELETE /api/v1/posts/{id}), or contact support instead: a recovery-verification post is never retried through POST /api/v1/posts/{id}/retry, because that endpoint does not close the account's incident. Only after that deletion succeeds, start a new verification: fix the cause and, with the owner's approval, start a new verification with a new Idempotency-Key. For INVALID_IDEMPOTENCY_KEY, nothing was bound, so correct the key's format and send the recovery request. For IDEMPOTENCY_CONFLICT, the key is already bound to an earlier request: keep that key and read GET /api/v1/posts/idempotency with it first; if the binding is that recovery verification, send the original recovery request unchanged to recover its outcome, and otherwise settle the bound post (or contact support) first, and use a new key only after that verification is confirmed not published and the owner approves. Otherwise apply the key guidance above to the recovery request.

**What to do from any other endpoint**

Read the allowed values from GET /api/v1/accounts/{id}/tiktok/creator-info, ask the user to choose one, and send it. If a keyed POST /api/v1/posts response carries data.id, the key is settled and the retry endpoint cannot add a privacy level: delete it (DELETE /api/v1/posts/{id}) and create a new post with the chosen privacyLevel and a new Idempotency-Key, after confirming the failed post has no recovery object, platformPostId or platformPostUrl. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### `SCHEDULE_EXPIRED`

**HTTP status of a failed response:** 409

**Where:** `POST /api/v1/posts`, `POST /api/v1/posts/thread`, `POST /api/v1/ab-tests`, `PATCH /api/v1/posts/{id}`

**What it means**

While the request was being prepared, scheduledAt fell outside the allowed window: it is now in the past or more than 30 days ahead.

**What to do**

Choose a new time within the next 30 days and send the request again. If a keyed POST /api/v1/posts response carries data.id, that key is settled and repeating it will not create the post: read the failed post, and if it has no recovery object, platformPostId or platformPostUrl, delete it (DELETE /api/v1/posts/{id}) and create a new post with a new key (do not use the retry endpoint to schedule it). Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### `PLATFORM_UNSUPPORTED`

**HTTP status of a failed response:** 400

**Where:** `POST /api/v1/ab-tests`, `GET /api/v1/posts/{id}/analytics`, `GET /api/v1/posts/{id}/engagements`, `POST /api/v1/engagements/{id}/reply`

**What it means**

The platform does not support this feature through SocialCannon, or support is not available yet.

**What to do**

Do not retry. GET /api/v1/platforms lists what each platform supports.

### `INSTAGRAM_STORY_EXPIRED`

**HTTP status of a failed response:** 400

**Where:** `GET /api/v1/posts/{id}/analytics`

**Also returned with HTTP 200:** `GET /api/v1/posts/{id}/analytics`, `POST /api/v1/analytics/refresh`

**What it means**

Instagram only reports Story analytics for 24 hours after publication. As an HTTP 400, it means no snapshot was collected in that time, so there are no figures for this Story.

**What to do**

Do not retry; live figures cannot be fetched again. When snapshots were collected, GET /api/v1/posts/{id}/analytics returns them with HTTP 200 instead.

**Also appears in**

HTTP 200 from GET /api/v1/posts/{id}/analytics as data.liveRefresh.code when snapshots exist, and as a skipped result in POST /api/v1/analytics/refresh.

## Idempotency keys and replays

Answers to a repeated request. A replay never publishes a second time.

### `IDEMPOTENCY_CONFLICT`

**HTTP status of a failed response:** 409

**Where:** `POST /api/v1/posts`, `POST /api/v1/ab-tests`, `POST /api/v1/posts/auto-schedule`, `POST /api/v1/auth/clients`, `POST /api/v1/accounts/{id}/recovery (verify_publishing)`

**What it means**

This Idempotency-Key was already used with a different request body.

**From account recovery verification**

Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again. This replaces the general action below for responses from recovery verification. Repeat or recover a verification only through POST /api/v1/accounts/{id}/recovery. Never use POST /api/v1/posts/{id}/retry or a plain create for it, because only the recovery endpoint closes the account's incident. If the outcome is uncertain or in progress (reconciliationRequired, providerPublished, a recovery object, or an uncertain or in-progress code), never use a new key: repeat the recovery request with the original key or wait for reconciliation (for ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, only wait: do not repeat anything until the account is reconciled), because a new key publishes another real post. First fix the cause this entry describes (reconnect the same network account, complete the check the details name, restore permissions), then repeat or recover through the recovery endpoint. Only when the response settles that nothing was published (a failed post with none of those markers) is that verification over, and after ACCOUNT_REFRESH_RECONCILIATION_REQUIRED only once support has reconciled the account. If that response also carried data.id, read the post first: while it is in progress or its outcome is uncertain (IDEMPOTENCY_IN_PROGRESS, IDEMPOTENCY_OUTCOME_UNCERTAIN, ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, a recovery object, reconciliationRequired or providerPublished), keep the original key and wait or recover through the recovery endpoint only, and never retry or delete it. Once it is a settled failed post with no live markers, delete it (DELETE /api/v1/posts/{id}), or contact support instead: a recovery-verification post is never retried through POST /api/v1/posts/{id}/retry, because that endpoint does not close the account's incident. Only after that deletion succeeds, start a new verification: fix the cause and, with the owner's approval, start a new verification with a new Idempotency-Key. For INVALID_IDEMPOTENCY_KEY, nothing was bound, so correct the key's format and send the recovery request. For IDEMPOTENCY_CONFLICT, the key is already bound to an earlier request: keep that key and read GET /api/v1/posts/idempotency with it first; if the binding is that recovery verification, send the original recovery request unchanged to recover its outcome, and otherwise settle the bound post (or contact support) first, and use a new key only after that verification is confirmed not published and the owner approves. Otherwise apply the key guidance above to the recovery request.

**What to do from any other endpoint**

To repeat the original operation, send the original request unchanged; if its outcome is not_published, the key cannot publish it, so fix the cause and retry it with POST /api/v1/posts/{id}/retry. Before starting a different operation under a new key, settle the original first: for a post, read GET /api/v1/posts/idempotency with this key and wait until its outcome is published or not_published (a not_published post can still be retried with POST /api/v1/posts/{id}/retry if it is failed (a deleted post cannot be retried), so before using a new key, delete it with DELETE /api/v1/posts/{id} or decide to retry it instead, never both); a scheduled original can still publish, so delete it (DELETE /api/v1/posts/{id}) before using a new key if you are replacing it; a published original stays live, so before using a new key for a replacement, delete it as well, because the new operation publishes alongside it otherwise; and if the outcome stays unresolved, do not start a new key and contact support. Every deletion above must succeed before a new key is used: if it fails (for example PLATFORM_DELETE_UNSUPPORTED, a pending provider deletion or a reconciliation code), the original may still be live, so do not create the replacement and contact support; for an A/B test, read its result by repeating the original request. A test under a new key runs alongside the original, so if the new test is unrelated, leave the original and its posts alone. If the new test replaces the original, cancel the original test first while it is active or provisioning (DELETE /api/v1/ab-tests/{id}, which frees its active-test slot but leaves its variant posts live); a test that is already completed or failed cannot be cancelled, so skip the cancellation and delete its variant posts directly. Then delete with DELETE /api/v1/posts/{id} every variant post in data.variants that is not already deleted (provisioned, scheduled, awaiting_account, publishing, published, reconciliation_required or failed; use its postId and state). Leaving a variant out of the new test is not enough: a scheduled one still publishes and a failed one can still be retried. If any of those deletions cannot be completed (for example PLATFORM_DELETE_UNSUPPORTED, PUBLISH_IN_PROGRESS or a reconciliation code), do not create the replacement test; contact support; for auto-schedule, replay the original request unchanged to obtain its data: items in data.unscheduled are settled and a replay skips them, so schedule those separately if still wanted, and repeat the original until nothing is pending. Then remove any of its data.scheduled posts you do not want with DELETE /api/v1/posts/{id} before starting another. If you meant to change the original, a new key creates a second operation alongside it, so delete or cancel the original first. For POST /api/v1/auth/clients, replay the original request to get its result; provisioned sub-clients cannot be listed or cancelled through the API, so contact support before provisioning another. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### `IDEMPOTENCY_IN_PROGRESS`

**HTTP status of a failed response:** 409

**Where:** `POST /api/v1/posts`, `PATCH /api/v1/posts/{id}`, `DELETE /api/v1/posts/{id}`, `POST /api/v1/accounts/{id}/recovery (verify_publishing)`

**What it means**

From POST /api/v1/posts: the original request with this key is still being processed, and this replay did not publish again; data.id is the post's stable ID. From PATCH or DELETE: the keyed create behind this post is still running, so the post cannot be changed yet.

**From account recovery verification**

Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again. This replaces the general action below for responses from recovery verification. Repeat or recover a verification only through POST /api/v1/accounts/{id}/recovery. Never use POST /api/v1/posts/{id}/retry or a plain create for it, because only the recovery endpoint closes the account's incident. If the outcome is uncertain or in progress (reconciliationRequired, providerPublished, a recovery object, or an uncertain or in-progress code), never use a new key: repeat the recovery request with the original key or wait for reconciliation (for ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, only wait: do not repeat anything until the account is reconciled), because a new key publishes another real post. First fix the cause this entry describes (reconnect the same network account, complete the check the details name, restore permissions), then repeat or recover through the recovery endpoint. Only when the response settles that nothing was published (a failed post with none of those markers) is that verification over, and after ACCOUNT_REFRESH_RECONCILIATION_REQUIRED only once support has reconciled the account. If that response also carried data.id, read the post first: while it is in progress or its outcome is uncertain (IDEMPOTENCY_IN_PROGRESS, IDEMPOTENCY_OUTCOME_UNCERTAIN, ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, a recovery object, reconciliationRequired or providerPublished), keep the original key and wait or recover through the recovery endpoint only, and never retry or delete it. Once it is a settled failed post with no live markers, delete it (DELETE /api/v1/posts/{id}), or contact support instead: a recovery-verification post is never retried through POST /api/v1/posts/{id}/retry, because that endpoint does not close the account's incident. Only after that deletion succeeds, start a new verification: fix the cause and, with the owner's approval, start a new verification with a new Idempotency-Key. For INVALID_IDEMPOTENCY_KEY, nothing was bound, so correct the key's format and send the recovery request. For IDEMPOTENCY_CONFLICT, the key is already bound to an earlier request: keep that key and read GET /api/v1/posts/idempotency with it first; if the binding is that recovery verification, send the original recovery request unchanged to recover its outcome, and otherwise settle the bound post (or contact support) first, and use a new key only after that verification is confirmed not published and the owner approves. Otherwise apply the key guidance above to the recovery request.

**What to do from any other endpoint**

For the create: wait, then read the outcome with GET /api/v1/posts/idempotency using the same key, or repeat the identical request with the same key. Never send it as a new post or under a new key. For PATCH or DELETE: wait, read the post with GET /api/v1/posts/{id}, then send the update or deletion again. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### `IDEMPOTENCY_OUTCOME_UNCERTAIN`

**HTTP status of a failed response:** 409

**Where:** `POST /api/v1/posts`, `POST /api/v1/accounts/{id}/recovery (verify_publishing)`

**What it means**

The network may or may not have published this post, and SocialCannon could not confirm which. data.id identifies the post; a same-key replay also returns data.status and data.recovery (code PROVIDER_RECONCILIATION_REQUIRED). No replay publishes again. The first uncertain create answers with an uncoded HTTP 503 and data.reconciliationRequired, except after a failed account refresh, when it answers with this code and HTTP 409.

**From account recovery verification**

Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again. This replaces the general action below for responses from recovery verification. Repeat or recover a verification only through POST /api/v1/accounts/{id}/recovery. Never use POST /api/v1/posts/{id}/retry or a plain create for it, because only the recovery endpoint closes the account's incident. If the outcome is uncertain or in progress (reconciliationRequired, providerPublished, a recovery object, or an uncertain or in-progress code), never use a new key: repeat the recovery request with the original key or wait for reconciliation (for ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, only wait: do not repeat anything until the account is reconciled), because a new key publishes another real post. First fix the cause this entry describes (reconnect the same network account, complete the check the details name, restore permissions), then repeat or recover through the recovery endpoint. Only when the response settles that nothing was published (a failed post with none of those markers) is that verification over, and after ACCOUNT_REFRESH_RECONCILIATION_REQUIRED only once support has reconciled the account. If that response also carried data.id, read the post first: while it is in progress or its outcome is uncertain (IDEMPOTENCY_IN_PROGRESS, IDEMPOTENCY_OUTCOME_UNCERTAIN, ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, a recovery object, reconciliationRequired or providerPublished), keep the original key and wait or recover through the recovery endpoint only, and never retry or delete it. Once it is a settled failed post with no live markers, delete it (DELETE /api/v1/posts/{id}), or contact support instead: a recovery-verification post is never retried through POST /api/v1/posts/{id}/retry, because that endpoint does not close the account's incident. Only after that deletion succeeds, start a new verification: fix the cause and, with the owner's approval, start a new verification with a new Idempotency-Key. For INVALID_IDEMPOTENCY_KEY, nothing was bound, so correct the key's format and send the recovery request. For IDEMPOTENCY_CONFLICT, the key is already bound to an earlier request: keep that key and read GET /api/v1/posts/idempotency with it first; if the binding is that recovery verification, send the original recovery request unchanged to recover its outcome, and otherwise settle the bound post (or contact support) first, and use a new key only after that verification is confirmed not published and the owner approves. Otherwise apply the key guidance above to the recovery request.

**What to do from any other endpoint**

Do not resend and do not create a replacement under a new key. Read the post with GET /api/v1/posts/idempotency and the same key if you sent one, or with GET /api/v1/posts/{id} and data.id. Its outcome stays "unresolved" until SocialCannon reconciles it. Check the network itself or contact support before deciding anything else.

### `IDEMPOTENCY_NOT_FOUND`

**HTTP status of a failed response:** 404

**Where:** `GET /api/v1/posts/idempotency`

**What it means**

No post is bound to that lookup key. This does not prove that an unkeyed attempt or an attempt under a different key saved or published nothing. With the original key, the request may never have reached SocialCannon or may have been refused before a post was created, for example by validation or a plan limit.

**What to do**

First confirm the lookup uses the original request's key. If it uses a different key, correct only the lookup header to the original key and look up again. If the original request was unkeyed, never add a key or resend it on the basis of this 404; read any known post ID, check the network and contact support instead. If the original request may still be running, wait and look up again. Only once you confirm the original request never arrived or was refused before anything was saved or published (for example invalid input or a plan limit), fix the cause and send it with the same original key. A 404 lookup alone does not prove that an earlier attempt never reached the network, so if such an attempt may have happened, contact support or read the post instead of sending the request again. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### `IDEMPOTENT_POST_FAILED`

**HTTP status of a failed response:** 400, 403, 404, 409, 410, 413, 422

**Where:** `POST /api/v1/posts`, `POST /api/v1/accounts/{id}/recovery (verify_publishing)`

**What it means**

The original request with this key failed, or its post was deleted, and no more specific recovery applies. This replay did not publish.

**From account recovery verification**

Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again. This replaces the general action below for responses from recovery verification. Repeat or recover a verification only through POST /api/v1/accounts/{id}/recovery. Never use POST /api/v1/posts/{id}/retry or a plain create for it, because only the recovery endpoint closes the account's incident. If the outcome is uncertain or in progress (reconciliationRequired, providerPublished, a recovery object, or an uncertain or in-progress code), never use a new key: repeat the recovery request with the original key or wait for reconciliation (for ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, only wait: do not repeat anything until the account is reconciled), because a new key publishes another real post. First fix the cause this entry describes (reconnect the same network account, complete the check the details name, restore permissions), then repeat or recover through the recovery endpoint. Only when the response settles that nothing was published (a failed post with none of those markers) is that verification over, and after ACCOUNT_REFRESH_RECONCILIATION_REQUIRED only once support has reconciled the account. If that response also carried data.id, read the post first: while it is in progress or its outcome is uncertain (IDEMPOTENCY_IN_PROGRESS, IDEMPOTENCY_OUTCOME_UNCERTAIN, ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, a recovery object, reconciliationRequired or providerPublished), keep the original key and wait or recover through the recovery endpoint only, and never retry or delete it. Once it is a settled failed post with no live markers, delete it (DELETE /api/v1/posts/{id}), or contact support instead: a recovery-verification post is never retried through POST /api/v1/posts/{id}/retry, because that endpoint does not close the account's incident. Only after that deletion succeeds, start a new verification: fix the cause and, with the owner's approval, start a new verification with a new Idempotency-Key. For INVALID_IDEMPOTENCY_KEY, nothing was bound, so correct the key's format and send the recovery request. For IDEMPOTENCY_CONFLICT, the key is already bound to an earlier request: keep that key and read GET /api/v1/posts/idempotency with it first; if the binding is that recovery verification, send the original recovery request unchanged to recover its outcome, and otherwise settle the bound post (or contact support) first, and use a new key only after that verification is confirmed not published and the owner approves. Otherwise apply the key guidance above to the recovery request.

**What to do from any other endpoint**

Read the post (data.id) for its failureReason. Repeating this key will never publish. If it is failed (not deleted), it may still be retryable with POST /api/v1/posts/{id}/retry: retry it or delete it and replace it, never both; a deleted post cannot be retried. Create a new post with a new key only after confirming the original is not live: it has no recovery object, no platformPostId and no platformPostUrl, or it was deleted and its network post is confirmed removed (no recovery object). Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

## Publishing results

What happened when SocialCannon tried to publish, and what is safe next.

### `MEDIA_TOO_LARGE`

**HTTP status of a failed response:** 413

**Where:** `POST /api/v1/posts`, `POST /api/v1/posts/thread`, `POST /api/v1/posts/{id}/retry`, `POST /api/v1/accounts/{id}/recovery (verify_publishing)`

**What it means**

A file is over this publishing route's size limit, found before anything reached the network. details.maxBytes gives the limit when it is known. No post was published.

**From account recovery verification**

Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again. This replaces the general action below for responses from recovery verification. Repeat or recover a verification only through POST /api/v1/accounts/{id}/recovery. Never use POST /api/v1/posts/{id}/retry or a plain create for it, because only the recovery endpoint closes the account's incident. If the outcome is uncertain or in progress (reconciliationRequired, providerPublished, a recovery object, or an uncertain or in-progress code), never use a new key: repeat the recovery request with the original key or wait for reconciliation (for ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, only wait: do not repeat anything until the account is reconciled), because a new key publishes another real post. First fix the cause this entry describes (reconnect the same network account, complete the check the details name, restore permissions), then repeat or recover through the recovery endpoint. Only when the response settles that nothing was published (a failed post with none of those markers) is that verification over, and after ACCOUNT_REFRESH_RECONCILIATION_REQUIRED only once support has reconciled the account. If that response also carried data.id, read the post first: while it is in progress or its outcome is uncertain (IDEMPOTENCY_IN_PROGRESS, IDEMPOTENCY_OUTCOME_UNCERTAIN, ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, a recovery object, reconciliationRequired or providerPublished), keep the original key and wait or recover through the recovery endpoint only, and never retry or delete it. Once it is a settled failed post with no live markers, delete it (DELETE /api/v1/posts/{id}), or contact support instead: a recovery-verification post is never retried through POST /api/v1/posts/{id}/retry, because that endpoint does not close the account's incident. Only after that deletion succeeds, start a new verification: fix the cause and, with the owner's approval, start a new verification with a new Idempotency-Key. For INVALID_IDEMPOTENCY_KEY, nothing was bound, so correct the key's format and send the recovery request. For IDEMPOTENCY_CONFLICT, the key is already bound to an earlier request: keep that key and read GET /api/v1/posts/idempotency with it first; if the binding is that recovery verification, send the original recovery request unchanged to recover its outcome, and otherwise settle the bound post (or contact support) first, and use a new key only after that verification is confirmed not published and the owner approves. Otherwise apply the key guidance above to the recovery request.

**What to do from any other endpoint**

First: if the response also has reconciliationRequired or providerPublished, the post may be live: read it and do not retry. Otherwise: If a failed post was saved (data.id, or data.postId from the retry endpoint), read it first: if it has a platformPostId, a platformPostUrl or a recovery object, keep it, do not create a replacement, and contact support. Otherwise delete it (DELETE /api/v1/posts/{id}), then create a new post with smaller media and a new Idempotency-Key. For a thread, create a new thread with smaller media; threads take no key. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### `POST_DEFINITIVELY_REJECTED`

**HTTP status of a failed response:** 422

**Where:** `POST /api/v1/posts`, `POST /api/v1/posts/{id}/retry`, `POST /api/v1/accounts/{id}/recovery (verify_publishing)`

**What it means**

The publish was rejected before it took effect. No post exists on the network.

**From account recovery verification**

Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again. This replaces the general action below for responses from recovery verification. Repeat or recover a verification only through POST /api/v1/accounts/{id}/recovery. Never use POST /api/v1/posts/{id}/retry or a plain create for it, because only the recovery endpoint closes the account's incident. If the outcome is uncertain or in progress (reconciliationRequired, providerPublished, a recovery object, or an uncertain or in-progress code), never use a new key: repeat the recovery request with the original key or wait for reconciliation (for ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, only wait: do not repeat anything until the account is reconciled), because a new key publishes another real post. First fix the cause this entry describes (reconnect the same network account, complete the check the details name, restore permissions), then repeat or recover through the recovery endpoint. Only when the response settles that nothing was published (a failed post with none of those markers) is that verification over, and after ACCOUNT_REFRESH_RECONCILIATION_REQUIRED only once support has reconciled the account. If that response also carried data.id, read the post first: while it is in progress or its outcome is uncertain (IDEMPOTENCY_IN_PROGRESS, IDEMPOTENCY_OUTCOME_UNCERTAIN, ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, a recovery object, reconciliationRequired or providerPublished), keep the original key and wait or recover through the recovery endpoint only, and never retry or delete it. Once it is a settled failed post with no live markers, delete it (DELETE /api/v1/posts/{id}), or contact support instead: a recovery-verification post is never retried through POST /api/v1/posts/{id}/retry, because that endpoint does not close the account's incident. Only after that deletion succeeds, start a new verification: fix the cause and, with the owner's approval, start a new verification with a new Idempotency-Key. For INVALID_IDEMPOTENCY_KEY, nothing was bound, so correct the key's format and send the recovery request. For IDEMPOTENCY_CONFLICT, the key is already bound to an earlier request: keep that key and read GET /api/v1/posts/idempotency with it first; if the binding is that recovery verification, send the original recovery request unchanged to recover its outcome, and otherwise settle the bound post (or contact support) first, and use a new key only after that verification is confirmed not published and the owner approves. Otherwise apply the key guidance above to the recovery request.

**What to do from any other endpoint**

If details.action is "edit_media_or_destination", fix the media or destination. If the response carries data.id or data.postId, read that failed post first: if it has a platformPostId, a platformPostUrl or a recovery object, keep it, do not create a replacement, and contact support; otherwise delete it (DELETE /api/v1/posts/{id}). Then create a new post with a new Idempotency-Key (the retry endpoint cannot change media or destination). If it is "contact_support", the stored credentials could not be read; contact support, because editing the post will not help. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### `POST_SETTLED_FAILED_RETRY_REQUIRED`

**HTTP status of a failed response:** 409

**Where:** `POST /api/v1/posts`, `POST /api/v1/posts/{id}/retry`, `POST /api/v1/accounts/{id}/recovery (verify_publishing)`

**What it means**

Nothing was published in this attempt, and the post is saved as failed. A keyed create replayed with the same key will not publish it; an unkeyed create sent again would create a second post. details.action may name the cause, such as wait_for_publishing_limit, review_account_restriction or repair_permissions.

**From account recovery verification**

Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again. This replaces the general action below for responses from recovery verification. Repeat or recover a verification only through POST /api/v1/accounts/{id}/recovery. Never use POST /api/v1/posts/{id}/retry or a plain create for it, because only the recovery endpoint closes the account's incident. If the outcome is uncertain or in progress (reconciliationRequired, providerPublished, a recovery object, or an uncertain or in-progress code), never use a new key: repeat the recovery request with the original key or wait for reconciliation (for ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, only wait: do not repeat anything until the account is reconciled), because a new key publishes another real post. First fix the cause this entry describes (reconnect the same network account, complete the check the details name, restore permissions), then repeat or recover through the recovery endpoint. Only when the response settles that nothing was published (a failed post with none of those markers) is that verification over, and after ACCOUNT_REFRESH_RECONCILIATION_REQUIRED only once support has reconciled the account. If that response also carried data.id, read the post first: while it is in progress or its outcome is uncertain (IDEMPOTENCY_IN_PROGRESS, IDEMPOTENCY_OUTCOME_UNCERTAIN, ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, a recovery object, reconciliationRequired or providerPublished), keep the original key and wait or recover through the recovery endpoint only, and never retry or delete it. Once it is a settled failed post with no live markers, delete it (DELETE /api/v1/posts/{id}), or contact support instead: a recovery-verification post is never retried through POST /api/v1/posts/{id}/retry, because that endpoint does not close the account's incident. Only after that deletion succeeds, start a new verification: fix the cause and, with the owner's approval, start a new verification with a new Idempotency-Key. For INVALID_IDEMPOTENCY_KEY, nothing was bound, so correct the key's format and send the recovery request. For IDEMPOTENCY_CONFLICT, the key is already bound to an earlier request: keep that key and read GET /api/v1/posts/idempotency with it first; if the binding is that recovery verification, send the original recovery request unchanged to recover its outcome, and otherwise settle the bound post (or contact support) first, and use a new key only after that verification is confirmed not published and the owner approves. Otherwise apply the key guidance above to the recovery request.

**What to do from any other endpoint**

Resolve the cause in the message, then call POST /api/v1/posts/{id}/retry (details.retryEndpoint) for the saved post. Do not resend the create, keyed or not. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### `POST_SETTLED_THREAD_NO_POST`

**HTTP status of a failed response:** 409

**Where:** `POST /api/v1/posts/thread`

**What it means**

No part of the thread was published, and the failure is recorded. details.action names the remedy.

**What to do**

If details.action is "contact_support", the stored credentials could not be read: contact support, because a new thread will fail the same way. Otherwise resolve the cause the message names, then create a new thread. Failed threads cannot be retried. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### `PUBLISH_CAPACITY_RETRYABLE`

**HTTP status of a failed response:** 503

**Where:** `POST /api/v1/posts`, `POST /api/v1/posts/thread`, `POST /api/v1/posts/{id}/retry`, `POST /api/v1/ab-tests`, `POST /api/v1/posts/repurpose`, `POST /api/v1/accounts/{id}/recovery (verify_publishing)`

**Also returned with HTTP 200:** `POST /api/v1/posts/repurpose`

**What it means**

Publishing capacity was briefly busy, so the post or item it names was not sent (unless the response also has reconciliationRequired or providerPublished). Other variants of an A/B test or other repurpose targets may already have published. The response usually has a Retry-After header, normally 30 seconds.

**If this came from POST /api/v1/posts/repurpose**

From repurpose: first make the fix this code's general guidance below describes (for example reconnect the same network account, change plan, or wait for any Retry-After). First follow the response code's remedy; ACCOUNT_REFRESH_RECONCILIATION_REQUIRED must be reconciled by support before any resend. Repurpose takes no Idempotency-Key, so the fix is judged against what this request already did: if an earlier attempt of the same request published or may have published some targets, send only the targets that are still missing, never the whole request. Read this response first to tell the two cases apart. As the whole response, a request-level refusal, so no target was processed in it, and this request can then be sent again, but only after confirming an earlier attempt of it published nothing; if it did or may have published some targets, send only the ones that are still missing. As one target's entry in data.results, other targets may already have published, so never resend the whole request; if that entry has providerPublished or reconciliationRequired the post may be live, so check it instead of retrying; otherwise, if the entry has a postId, a post was saved: read it first. If it is published or has a platformPostId, platformPostUrl or recovery object, keep it and do not send that target again. If it is failed with no live markers, either retry it with POST /api/v1/posts/{postId}/retry or delete it and retry only that target in a new request naming just it, never both. If it is a draft, it cannot be retried, so delete it and then send only that target again; the deletion releases its reserved quota. In any other state, or if the deletion fails, contact support. Reading needs the posts:read scope and deleting posts:delete; without them, or if the deletion fails, contact support instead of retrying the target. With no postId and neither providerPublished nor reconciliationRequired, a draft may still have been saved if its acknowledgement was lost. Before retrying that target, list that account's drafts with GET /api/v1/posts?status=draft&platform={platform}&accountId={accountId} (posts:read scope), identify any draft created by this request and delete it with DELETE /api/v1/posts/{id} (posts:delete scope). Only after that deletion succeeds, or after confirming this request saved no draft, send only that missing target again. If you cannot identify the draft, lack the scopes or cannot complete its deletion, contact support instead of retrying. If the owner chose this target's content for the account recovery check post, that content is now live, so do not send that target again. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

**From account recovery verification**

Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again. This replaces the general action below for responses from recovery verification. Repeat or recover a verification only through POST /api/v1/accounts/{id}/recovery. Never use POST /api/v1/posts/{id}/retry or a plain create for it, because only the recovery endpoint closes the account's incident. If the outcome is uncertain or in progress (reconciliationRequired, providerPublished, a recovery object, or an uncertain or in-progress code), never use a new key: repeat the recovery request with the original key or wait for reconciliation (for ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, only wait: do not repeat anything until the account is reconciled), because a new key publishes another real post. First fix the cause this entry describes (reconnect the same network account, complete the check the details name, restore permissions), then repeat or recover through the recovery endpoint. Only when the response settles that nothing was published (a failed post with none of those markers) is that verification over, and after ACCOUNT_REFRESH_RECONCILIATION_REQUIRED only once support has reconciled the account. If that response also carried data.id, read the post first: while it is in progress or its outcome is uncertain (IDEMPOTENCY_IN_PROGRESS, IDEMPOTENCY_OUTCOME_UNCERTAIN, ACCOUNT_REFRESH_RECONCILIATION_REQUIRED, a recovery object, reconciliationRequired or providerPublished), keep the original key and wait or recover through the recovery endpoint only, and never retry or delete it. Once it is a settled failed post with no live markers, delete it (DELETE /api/v1/posts/{id}), or contact support instead: a recovery-verification post is never retried through POST /api/v1/posts/{id}/retry, because that endpoint does not close the account's incident. Only after that deletion succeeds, start a new verification: fix the cause and, with the owner's approval, start a new verification with a new Idempotency-Key. For INVALID_IDEMPOTENCY_KEY, nothing was bound, so correct the key's format and send the recovery request. For IDEMPOTENCY_CONFLICT, the key is already bound to an earlier request: keep that key and read GET /api/v1/posts/idempotency with it first; if the binding is that recovery verification, send the original recovery request unchanged to recover its outcome, and otherwise settle the bound post (or contact support) first, and use a new key only after that verification is confirmed not published and the owner approves. Otherwise apply the key guidance above to the recovery request.

**What to do from any other endpoint**

First: if the response also has reconciliationRequired or providerPublished, the post may be live: read it and do not retry. Otherwise: Wait for Retry-After, then follow the endpoint-specific action. For a keyed post or an A/B test, repeat the same request with the same Idempotency-Key; for an A/B test this resumes the same variants. For a thread, data.retryAction "create_thread" means send the same thread request again. From POST /api/v1/posts/{id}/retry, first read GET /api/v1/posts/{id} using the saved post ID. Retry that same post only if it is failed with no platformPostId, platformPostUrl or recovery object. If any such marker exists, the post is not failed, or it cannot be read, do not retry; wait for reconciliation or contact support. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

**Also appears in**

Inside an HTTP 200 response from POST /api/v1/posts/repurpose in post mode, as one target's code in data.results: the response itself succeeded, so the status listed above applies only when the same code is returned as a request-level refusal.

### `PUBLISH_OUTCOME_UNCERTAIN`

**HTTP status of a failed response:** 409, 503

**Where:** `POST /api/v1/posts/{id}/retry`

**What it means**

The retry may have reached the network but its result is unknown, or an earlier attempt is still unresolved. data.reconciliationRequired is true, and data.providerPublished is true when the network accepted it. Only the retry endpoint sends this code: an uncertain first create is an HTTP 503 without a code (see Errors without a code), or 409 IDEMPOTENCY_OUTCOME_UNCERTAIN after a failed account refresh.

**What to do**

Do not retry again, and do not create a replacement or use a new key: the post may be live. Read the post with GET /api/v1/posts/{id}, using the ID from the request path (data.postId or data.id), check the network, and wait for reconciliation or contact support.

### `PROVIDER_RECONCILIATION_REQUIRED`

**HTTP status of a failed response:** 503

**Where:** `POST /api/v1/ab-tests`

**Also returned with HTTP 200:** `GET /api/v1/posts`, `GET /api/v1/posts/{id}`, `GET /api/v1/posts/idempotency`, `GET /api/v1/calendar`, `POST /api/v1/posts/auto-schedule`

**What it means**

The result of a network call is unknown and needs reconciliation.

**What to do**

Do not create a replacement or publish again. From an A/B test, repeating the same request with the same Idempotency-Key is safe and does not duplicate posts. On a post's recovery.code, the post stays held until SocialCannon reconciles it; check the network or contact support if you need the outcome sooner. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

**Also appears in**

HTTP 200 post objects (for example GET /api/v1/posts/{id} and GET /api/v1/posts/idempotency) as recovery.code, with recovery.kind "publish_outcome" or "provider_delete"; also as data.recovery.code on 409 and 410 idempotency replay responses.

## Retrying a failed post

Answers from POST /api/v1/posts/{id}/retry that stop a retry.

### `THREAD_RETRY_UNSUPPORTED`

**HTTP status of a failed response:** 409

**Where:** `POST /api/v1/posts/{id}/retry`

**What it means**

Failed threads cannot be retried through the single-post endpoint. With data.reconciliationRequired, the thread may have been partly published.

**What to do**

Without reconciliationRequired, create a new thread from the saved items. With it, do not create or replace a thread: the partial thread must be reconciled on the network first, so check what is live and contact support. Only create a new thread once reconciliation confirms that nothing from the earlier thread is live. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### `PARTIAL_THREAD_COMPENSATION_REQUIRED`

**HTTP status of a failed response:** 409

**Where:** `POST /api/v1/posts/{id}/retry`

**What it means**

This thread may include a live item that SocialCannon has not confirmed.

**What to do**

Do not retry or replace it. Contact support to reconcile the live chain.

### `LOCAL_PUBLISH_REPAIR_FAILED`

**HTTP status of a failed response:** 503

**Where:** `POST /api/v1/posts/{id}/retry`

**What it means**

The network confirmed the post, but SocialCannon could not update its record.

**What to do**

Do not publish again. Calling retry later is safe: for a confirmed post it repairs the record without republishing.

## Post state conflicts

The post changed, or is busy, while an update, retry or delete was being prepared.

### `PROVIDER_DELETE_IN_PROGRESS`

**HTTP status of a failed response:** 409

**Where:** `DELETE /api/v1/posts/{id}`, `POST /api/v1/posts/{id}/retry`, `PATCH /api/v1/posts/{id}`

**Also returned with HTTP 200:** `GET /api/v1/posts`, `GET /api/v1/posts/{id}`, `GET /api/v1/posts/idempotency`, `GET /api/v1/calendar`, `POST /api/v1/posts/auto-schedule`

**What it means**

Deleting this post on the network is pending.

**What to do**

From DELETE: repeat DELETE /api/v1/posts/{id} to finish the deletion; supported networks treat an already deleted post as done. If repeating it does not settle (for example PLATFORM_POST_ID_REQUIRED), read the post and contact support. From PATCH or retry: the post is being deleted, so do not update or republish it, and do not start a deletion you were not asked for; read the post again later. As a post's recovery.code, the deletion is still pending: do not republish, replace or change the post; read it again later.

**Also appears in**

HTTP 200 post objects as recovery.code, with recovery.kind "provider_delete"; also as data.recovery.code on 409 and 410 idempotency replay responses.

### `USAGE_RESERVATION_OWNER_UNCERTAIN`

**HTTP status of a failed response:** 409

**Where:** `POST /api/v1/posts/{id}/retry`, `PATCH /api/v1/posts/{id}`, `DELETE /api/v1/posts/{id}`, `POST /api/v1/ab-tests`

**What it means**

SocialCannon cannot match the usage held for this post to its owner.

**What to do**

Do not retry. Contact support.

### `POST_DELETED`

**HTTP status of a failed response:** 409

**Where:** `DELETE /api/v1/posts/{id}`

**What it means**

The post was deleted. With data.reconciliationRequired, the network post has not been proven removed.

**What to do**

Without reconciliationRequired, nothing more is needed for this post; create a new one if you still want it. With reconciliationRequired, read the post and check the network first, and do not create a replacement until the original is confirmed gone; contact support if it is still live. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### `PUBLISH_IN_PROGRESS`

**HTTP status of a failed response:** 409

**Where:** `PATCH /api/v1/posts/{id}`, `DELETE /api/v1/posts/{id}`

**What it means**

The post is publishing, or waiting for its outcome to be reconciled, so it cannot be changed now.

**What to do**

Read the post again later with GET /api/v1/posts/{id}. Once publishing has settled, send a deletion again if you still want it; send an update again only if the post is back in an editable state (draft, scheduled or awaiting_account). Do not publish a replacement post while this post is publishing, because it may still go out.

### `PUBLISH_ATTEMPT_MISMATCH`

**HTTP status of a failed response:** 409

**Where:** `DELETE /api/v1/posts/{id}`

**What it means**

The post changed while the request was being prepared.

**What to do**

Read the post again and decide from its current state. Do not publish it again.

### `PROVIDER_ID_MISMATCH`

**HTTP status of a failed response:** 409

**Where:** `DELETE /api/v1/posts/{id}`

**What it means**

The post's recorded network identity does not allow this change, for example because it is already published.

**What to do**

Do not publish it again. Read the post, and contact support if its state looks wrong.

### `PROVIDER_DELETE_NOT_JOURNALED`

**HTTP status of a failed response:** 409

**Where:** `DELETE /api/v1/posts/{id}`

**What it means**

The record of a pending network deletion changed or is incomplete.

**What to do**

Read the post again. If it still shows a pending deletion, contact support.

### `PARTIAL_THREAD_RECONCILIATION_REQUIRED`

**HTTP status of a failed response:** 409

**Where:** `DELETE /api/v1/posts/{id}`

**What it means**

The recorded X post IDs for this thread may cover only part of the live chain.

**What to do**

Contact support to reconcile the whole chain before deleting it.

## Deleting a post

Answers from DELETE /api/v1/posts/{id}.

### `ACCOUNT_AUTHORITY_CHANGED`

**HTTP status of a failed response:** 409

**Where:** `DELETE /api/v1/posts/{id}`

**What it means**

The connected account changed while the deletion was being prepared, so this request deleted nothing.

**What to do**

If an earlier deletion of this post was interrupted, read the post first with GET /api/v1/posts/{id}: anything that deletion already removed stays removed. Retry the deletion. If the account was reconnected to a different network account, reconnect the original one first; the post can only be deleted through the account that published it.

### `ACCOUNT_UNAVAILABLE`

**HTTP status of a failed response:** 409

**Where:** `DELETE /api/v1/posts/{id}`

**What it means**

The post's account is missing, disconnected or no longer matches the post's platform.

**What to do**

Reconnect the original network account, then retry the deletion. If that account no longer exists, contact support.

### `CREDENTIAL_REFRESH_FAILED`

**HTTP status of a failed response:** 503

**Where:** `DELETE /api/v1/posts/{id}`

**What it means**

SocialCannon could not renew the account's access. No network deletion was attempted.

**What to do**

Retry the deletion later.

### `CREDENTIAL_REFRESH_RECONCILIATION_REQUIRED`

**HTTP status of a failed response:** 503

**Where:** `DELETE /api/v1/posts/{id}`

**What it means**

SocialCannon could not safely renew the account's access. No network deletion was attempted.

**What to do**

Do not retry yet, and do not reconnect first: an earlier operation on the account is unresolved. Contact support, and retry the deletion once it is reconciled.

### `PLATFORM_POST_ID_MISMATCH`

**HTTP status of a failed response:** 409

**Where:** `DELETE /api/v1/posts/{id}`

**What it means**

The platformPostId in the request body does not match the one recorded for this post.

**What to do**

Read the post and send its recorded ID as { "strictCompensation": true, "platformPostId": "\<recorded id>" }, or send no body.

### `PLATFORM_POST_ID_UNVERIFIED`

**HTTP status of a failed response:** 409

**Where:** `DELETE /api/v1/posts/{id}`

**What it means**

A platformPostId was sent, but none is recorded for this post. SocialCannon never deletes an ID it did not record.

**What to do**

Read the post. If it is a draft, scheduled or failed post with no platformPostId, platformPostUrl, publishedAt or recovery object, send DELETE /api/v1/posts/{id} again without a body to remove it; a scheduled post left in place will still publish. Contact support if it is published or shows any provider evidence, or if it is live on the network.

### `PLATFORM_POST_ID_REQUIRED`

**HTTP status of a failed response:** 409

**Where:** `DELETE /api/v1/posts/{id}`

**What it means**

The post may have reached the network, but no exact network post ID is recorded, so it cannot be deleted safely.

**What to do**

Check the network. If the post is live there, remove it on the network yourself. Either way, contact support so the SocialCannon record can be settled: without the network post ID, the API cannot delete it.

### `LEGACY_PROVIDER_DELETE_UNVERIFIED`

**HTTP status of a failed response:** 409

**Where:** `DELETE /api/v1/posts/{id}`

**What it means**

An older deleted post has no proof that it was removed from the network.

**What to do**

Contact support.

### `PLATFORM_DELETE_UNSUPPORTED`

**HTTP status of a failed response:** 409

**Where:** `DELETE /api/v1/posts/{id}`

**What it means**

This network does not let SocialCannon delete published posts through its API (Instagram and TikTok). The SocialCannon post is kept.

**What to do**

Delete the post in the network's own app, then contact support so the SocialCannon record (which keeps data.reconciliationRequired) can be settled. Do not publish it again.

### `PLATFORM_DELETE_FAILED`

**HTTP status of a failed response:** 503

**Where:** `DELETE /api/v1/posts/{id}`

**What it means**

The network rejected or failed the deletion. With data.reconciliationRequired, part of it may have happened.

**What to do**

Without reconciliationRequired, retry the deletion. With it, check the network before retrying.

### `LOCAL_DELETE_FAILED`

**HTTP status of a failed response:** 503

**Where:** `DELETE /api/v1/posts/{id}`

**What it means**

SocialCannon could not finish deleting its own record.

**What to do**

Retry the request.

### `LOCAL_DELETE_JOURNAL_FAILED`

**HTTP status of a failed response:** 503

**Where:** `DELETE /api/v1/posts/{id}`

**What it means**

SocialCannon could not safely start the network deletion. No network deletion was attempted.

**What to do**

Retry the deletion later.

### `LOCAL_DELETE_PERSISTENCE_FAILED`

**HTTP status of a failed response:** 503

**Where:** `DELETE /api/v1/posts/{id}`

**What it means**

SocialCannon could not save the deletion of its own record. With data.providerDeletionConfirmed, the network post is already gone; data.reconciliationRequired is also set until the record is saved.

**What to do**

Repeat the deletion: it resumes the same deletion, and networks that support deletion treat an already deleted post as done.

## A/B tests, auto-schedule and repurpose

Codes specific to operations that create several posts at once.

### `INSUFFICIENT_ELIGIBLE_VARIANTS`

**HTTP status of a failed response:** 503

**Where:** `POST /api/v1/ab-tests`

**What it means**

The A/B test ended with fewer than two eligible published variants. Its active-test slot was released. Some variants may already be live: data.variants shows each one's state and platformPostId.

**What to do**

A replacement test needs a new Idempotency-Key and publishes new posts. Before reusing a variant's content, delete with DELETE /api/v1/posts/{id} every variant post in data.variants that is not already deleted (provisioned, scheduled, awaiting_account, publishing, published, reconciliation_required or failed); leaving it out is not enough, because a scheduled one still publishes and a failed one can still be retried. If any of those deletions cannot be completed (for example PLATFORM_DELETE_UNSUPPORTED, PUBLISH_IN_PROGRESS or a reconciliation code), do not create the replacement test; contact support. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### `PARTIAL_FAILURE`

**HTTP status of a failed response:** 503

**Where:** `POST /api/v1/ab-tests`

**What it means**

The A/B test is active, but some variants failed definitively and are excluded from the comparison.

**What to do**

Read data.variants. The test continues with the rest; no retry is needed.

### `PROVIDER_AUTHORITY_CHANGED`

**HTTP status of a failed response:** 409

**Where:** `POST /api/v1/ab-tests`

**What it means**

The social account changed after the A/B test was set up. Its posts can still be recovered with the same Idempotency-Key.

**What to do**

Reconnect the exact account, then repeat the request with the same key. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### `AB_TEST_OPERATION_INCOMPLETE`

**HTTP status of a failed response:** 503

**Where:** `POST /api/v1/ab-tests`

**What it means**

The A/B test is set up, but processing stopped part way.

**What to do**

Repeat the request with the same Idempotency-Key to resume the same posts. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### `AUTO_SCHEDULE_RETRY_REQUIRED`

**HTTP status of a failed response:** 500

**Where:** `POST /api/v1/posts/auto-schedule`

**What it means**

The auto-schedule operation is incomplete. data.scheduled, data.pending and data.unscheduled list each item's state.

**What to do**

Repeat the request with the same Idempotency-Key to resume the pending items. Items in data.unscheduled are settled and will not be retried by a replay: read their reason and schedule only those items separately if still wanted. Never resend the original payload under a new key, because items in data.scheduled would be scheduled twice. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### `AUTO_SCHEDULE_ACCOUNT_AUTHORITY_CHANGED`

**HTTP status of a failed response:** 409

**Where:** `POST /api/v1/posts/auto-schedule`

**What it means**

The account behind an existing auto-schedule operation is gone, inactive or different.

**What to do**

Reconnect the exact account, then repeat the original request with the same Idempotency-Key to resume the operation; if that account cannot be reconnected, contact support to reconcile the listed items. Do not resend under a new key. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### `AUTO_SCHEDULE_RECONCILIATION_REQUIRED`

**HTTP status of a failed response:** 500

**Where:** `POST /api/v1/posts/auto-schedule`

**What it means**

The operation's record points at a post that is missing.

**What to do**

Contact support. Do not resend under a new key.

### `REPURPOSE_UNAVAILABLE`

**HTTP status of a failed response:** 503

**Where:** `POST /api/v1/posts/repurpose`

**What it means**

The AI adaptation failed. Nothing was drafted or posted. details.retryable says whether retrying can help, and details.missingPlatforms names a platform the model left out.

**What to do**

If retryable is true, retry later, optionally without the missing platforms. If false, retrying will not help until SocialCannon fixes its AI provider. This request published nothing. If it was itself a retry of an earlier repurpose request that may have published some targets, never resend the whole request: send only the targets that are still missing. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### `REPURPOSE_CONTENT_REJECTED`

**HTTP status of a failed response:** 422

**Where:** `POST /api/v1/posts/repurpose`

**What it means**

The AI provider declined to adapt this text, for example because of a content filter.

**What to do**

Edit the source text before trying again. This request published nothing. If it was itself a retry of an earlier repurpose request that may have published some targets, never resend the whole request: send only the targets that are still missing. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### `NO_PUBLISHABLE_TARGETS`

**HTTP status of a failed response:** 400

**Where:** `POST /api/v1/posts/repurpose`

**What it means**

In post mode, no target account can publish, so nothing was generated. data.results explains each platform.

**What to do**

Fix the accounts named in data.results, then send the request again. This request published nothing. If it was itself a retry of an earlier repurpose request that may have published some targets, never resend the whole request: send only the targets that are still missing. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

## Errors without a code

These failures have no `code` field. Handle them by HTTP status and the fields shown.

### HTTP 400

**When**

"Invalid input" (details holds the field errors), "Content validation failed" (details lists the platform rules broken), "Social account is not active", a missing Idempotency-Key or invalid body on POST /api/v1/accounts/{id}/recovery, an Idempotency-Key that is not paired with client_secret or is not a canonical lowercase UUIDv4 on POST /api/v1/auth/clients, or a GET /api/v1/calendar range longer than the plan allows ("Calendar range limited to N days on your plan"). Also a retry on a non-failed post ("Cannot retry post with status …") or a PATCH on a non-editable post ("Cannot update post with status …"). Also DELETE /api/v1/ab-tests/{id} or POST /api/v1/ab-tests/{id}/complete on a test whose status does not allow it ("Cannot cancel test with status …", "Cannot complete test with status …").

**What to do**

For "Social account is not active", reconnect the same network account before trying again, then apply the saved-post rules below if data.id is present; keep any original key when resending an unchanged fresh create. For "Cannot retry post with status" or "Cannot update post with status", do not resend unchanged: read GET /api/v1/posts/{id} and act on its current status. Only failed posts can use the retry endpoint, and a live provider identity or recovery object must be resolved before any new publication; PATCH edits only a draft, scheduled or eligible awaiting_account post. For an A/B test cancel or complete refused because of its status, nothing changed: do not send it again; read the test with GET /api/v1/ab-tests/{id} and act on its current status. For POST /api/v1/auth/clients, send the Idempotency-Key together with the same client_secret, as a canonical lowercase UUIDv4. For repurpose post mode, follow the full repurpose recovery rule above before sending a corrected target; a validation failure does not prove an earlier attempt published nothing. For A/B tests or auto-schedule, retain the original Idempotency-Key when resending; if a corrected body conflicts with an existing binding, read and reconcile that operation instead of starting under a new key. For a thread, check any previously saved post and every live item before a replacement; if its outcome is unclear, contact support instead of sending another thread. Otherwise, fix the request and send it again. If a keyed POST /api/v1/posts response carries data.id, the key is settled and repeating it will not publish: if the cause was the account (restored now), retry that post with POST /api/v1/posts/{id}/retry; if it was the content or the account ID, which the retry endpoint cannot change, create a new post with a new key after confirming it has no recovery object, platformPostId or platformPostUrl. Do one or the other, never both: delete the failed post before replacing it. For account recovery verification, use neither: start a new verification through POST /api/v1/accounts/{id}/recovery with the owner's approval. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### HTTP 401

**When**

"Missing or invalid Authorization header" or "Invalid or expired token": the bearer token is missing, malformed or past its one-hour life. "Client is deactivated": the API client itself was switched off.

**What to do**

For a token problem, get a new token from POST /api/v1/auth/token. A deactivated client cannot get a working token; contact support. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### HTTP 403

**When**

"Insufficient scopes" names the missing scope. POST /api/v1/ab-tests also returns 403 without a code when A/B testing is not on the plan or the test has more variants than the plan allows.

**What to do**

Use a client with the scope, or upgrade the plan. For an A/B test with more variants than the plan allows, you can instead send fewer variants. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### HTTP 404

**When**

The requested resource (a post, account, engagement, A/B test or API client) does not exist or belongs to another client. Engagement reads and replies also return "Associated account not found or inactive".

**What to do**

For "Associated account not found or inactive", restore or reconnect the same network account, then confirm the associated account is active before repeating the request; if the saved post or engagement still refers to a missing account, contact support. Before repeating a reply, check the comment thread first and do not blindly resend it. Otherwise, check the ID. Lists are available from the matching GET endpoint. If a keyed POST /api/v1/posts response carries data.id, the key is settled and repeating it will not publish: if the cause was the account (restored now), retry that post with POST /api/v1/posts/{id}/retry; if it was the content or the account ID, which the retry endpoint cannot change, create a new post with a new key after confirming it has no recovery object, platformPostId or platformPostUrl. Do one or the other, never both: delete the failed post before replacing it. For account recovery verification, use neither: start a new verification through POST /api/v1/accounts/{id}/recovery with the owner's approval. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### HTTP 200

**When**

POST /api/v1/posts/repurpose in post mode: an entry in data.results with success false and only an error message, no code (for example media that could not be fetched, a missing TikTok privacy level, or a usage limit reached while publishing).

**What to do**

If the entry has providerPublished or reconciliationRequired, the post may be live: check it and do not retry. Otherwise, if the entry has a postId, a post was saved: read it first. If it is published or has a platformPostId, platformPostUrl or recovery object, keep it and do not send that target again; only if it is failed with no live markers, either retry it with POST /api/v1/posts/{postId}/retry or delete it before retrying the target in a new request, never both; in any other state, contact support and do not retry the target. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again. With neither, a draft may still have been saved if the failure came while saving it, and it can count against your post quota. Before retrying, list that account's drafts with GET /api/v1/posts?status=draft&platform={platform}&accountId={accountId} (posts:read scope) and delete any created during this request with DELETE /api/v1/posts/{id} (posts:delete scope), which releases it from your quota; if you lack those scopes, the deletion fails, or you cannot tell whether a draft came from this request, contact support instead of retrying. Then fix what the message names and retry only that target in a new request. Never resend the whole request, because other targets may have published.

### HTTP 200

**When**

POST /api/v1/analytics/refresh: a result with success false and no code (for example an inactive account or an unsupported platform). A skipped Instagram Story carries INSTAGRAM_STORY_EXPIRED instead.

**What to do**

Nothing changed for that post. Fix the cause named in the result, or leave it; refreshing again is safe.

### HTTP 409

**When**

POST /api/v1/posts or POST /api/v1/posts/thread with scheduledAt "optimal": "No optimal time slot available in the next 7 days."

**What to do**

Nothing was created. Send the post or thread with an explicit scheduledAt instead. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### HTTP 409

**When**

DELETE /api/v1/ab-tests/{id}: "The A/B test changed before it could be canceled". POST /api/v1/ab-tests/{id}/complete: the test cannot complete yet (fewer than two eligible variants are published, a variant is unresolved, or analytics are not yet available for every eligible variant), or a variant changed before completion.

**What to do**

Nothing changed. Read the test with GET /api/v1/ab-tests/{id}. Cancel again only if it is still active or provisioning; complete it later, once its variants are published and their analytics are available. Do not create a replacement test because of this response.

### HTTP 429

**When**

The per-client rate limit was exceeded.

**What to do**

Wait for the Retry-After header, then continue. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### HTTP 503

**When**

POST /api/v1/posts (or account recovery verification) answered with data.id and no code: the publish outcome is uncertain, or a failure could not be recorded. data.reconciliationRequired is usually set; data.providerPublished: true means the network accepted the post but SocialCannon could not record it.

**What to do**

Do not resend it as a new post or under a new key, and do not create a replacement. If you sent an Idempotency-Key, repeating the identical request with that key is safe; without one, never repeat the request, because it would create a second post. Read GET /api/v1/posts/{id} with data.id first; if you sent an Idempotency-Key, GET /api/v1/posts/idempotency is an extra check, but a 404 there does not prove nothing exists, because the key may not have been bound. For recovery verification, repeat only POST /api/v1/accounts/{id}/recovery with the original key, never POST /api/v1/posts/{id}/retry or a plain create. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### HTTP 503

**When**

POST /api/v1/posts/thread answered with data.id and no code: the thread's outcome is uncertain. data.providerPostIds and data.providerPublished: true mean some items are live; data.reconciliationRequired is set while it is unresolved.

**What to do**

Do not create another thread. Read the post with GET /api/v1/posts/{id} and check the live items on the network; contact support to reconcile it.

### HTTP 503

**When**

POST /api/v1/posts/{id}/retry answered with no code: the retry's outcome is uncertain, or its failure could not be recorded.

**What to do**

Do not call retry again yet and do not create a replacement. Read GET /api/v1/posts/{id}; contact support if it does not settle.

### HTTP 503

**When**

POST /api/v1/engagements/{id}/reply: "Failed to reply", with no code. The network may or may not have posted the reply.

**What to do**

Do not resend automatically. Check the comment thread on the network first, then reply again only if your reply is missing.

### HTTP 503

**When**

POST /api/v1/media/upload-complete: "Upload could not be finalized."

**What to do**

Call upload-complete again with the same publicUrl; finalizing the same upload twice is safe.

### HTTP 503

**When**

A read such as GET /api/v1/posts/{id}/analytics or GET /api/v1/posts/{id}/engagements failed upstream ("Failed to fetch ...").

**What to do**

Retry later; these requests change nothing on the network.

### HTTP 503

**When**

POST /api/v1/ab-tests: "The A/B test could not be provisioned."

**What to do**

Repeat the request with the same Idempotency-Key. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

### HTTP 501

**When**

POST /api/v1/posts/repurpose when AI repurposing is not configured on this server. No work was started.

**What to do**

Do not retry in a loop. Contact support.

### HTTP 500

**When**

An unexpected server error.

**What to do**

For a write, treat the outcome as unknown and do not resend it automatically. A keyed request (posts, A/B tests, auto-schedule, account recovery verification through its recovery endpoint, and API client provisioning with the same key and client_secret) can be repeated with its original key, or looked up first with GET /api/v1/posts/idempotency for posts. An unkeyed post, thread or repurpose request may already have been saved (an unkeyed client provisioning may have created a client that the API cannot list, so contact support before provisioning again): do not send it again until both are settled, in this order. First, the network shows it was not published (checking GET /api/v1/posts alone is not enough, because the network may have accepted it before SocialCannon recorded it). For a repurpose request, check every target you asked for, not only one, and never resend the whole request once any target may have published: send only the target that is still missing. Then, SocialCannon has no saved copy: look for it in GET /api/v1/posts, including status=scheduled and draft, and delete a saved copy you are replacing, because a saved scheduled copy can still publish later. If the network shows it live or you cannot tell, keep any saved copy and contact support; also contact support if you cannot identify and settle the original. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again.

The full request and response formats are in the [API reference](https://socialcannon.app/docs.md). Stuck on an error? Email [support@socialcannon.app](mailto:support@socialcannon.app) with the code, the post ID and the time of the request.
