overads
Browse the docs

Start

Errors

The one error shape, every code in the catalogue, and what to do about each.

The shape

Every error the public API raises itself has one body. code is stable for the life of v1 and is the thing to switch on; message is for a person; field names the offending input when there is one.

{
  "error": {
    "code": "SCHEDULED_TIME_IN_PAST",
    "message": "scheduledAt is 42 minutes in the past. Send a time in the future, or omit it to create a draft.",
    "field": "scheduledAt"
  }
}

Three families do not use this shape. Authentication and rate-limit failures answer with the flat body shown on Authentication and Rate limits. Schema validation of a request body (an unknown field, a wrong type, a missing required field) answers 400 with the framework's flat body: { "statusCode": 400, "message": ["..."], "error": "Bad Request" }. Unknown fields are refused, never ignored. And a post that breaks one target channel's limits on create or edit is refused by the shared publish checks, which answer 400 with a flat body carrying the code at the top level: { "statusCode": 400, "code": "TOO_LONG", "message": "X posts are limited to 280 characters (this one is 301)." }. Those codes are listed after the catalogue. Every flat body also carries error, path and timestamp beside the fields shown here; the examples on these pages list the fields worth switching on.

Warnings

Things that happened without failing the call ride on successful writes as warnings: [{ code, message }]. Two exist: APPROVAL_REQUIRED (the post is queued as pending) and SCHEDULED_TIME_COERCED (a scheduledAt up to five minutes in the past was moved to now).

The catalogue

CodeStatusMeaningRecovery
MEDIA_REQUIRED400The post targets Instagram and carries no media. (YouTube, TikTok and Pinterest refuse a text-only scheduled post with the same code in the flat channel-limit body below.)Add mediaUrls or mediaIds.
MEDIA_NOT_FOUND404A mediaIds entry is not a media asset in this workspace.Use the id returned by POST /media/upload-url.
MEDIA_NOT_READY409The asset has not been completed, or the bytes are not in storage yet.PUT the file, then POST /media/:id/complete.
MEDIA_TOO_LARGE400Over the cap: 15 MB images, 500 MB video by upload, 25 MB video by URL. (A video over one target channel's own size cap carries the same code in the flat channel-limit body below.)Shrink it, or use the presigned upload for large video.
MEDIA_URL_REFUSED400The URL is not public https, did not answer 2xx, or did not return an image or video.Host the file on a public https URL.
MEDIA_STORAGE_UNAVAILABLE503Object storage is not configured or not reachable.Retry later; nothing was written.
SCHEDULED_TIME_IN_PAST400scheduledAt is more than five minutes in the past.Send a future time, or omit it for a draft.
PLATFORM_SETTING_UNKNOWN400A key in platformSettings is unknown, mistyped, or for a platform the post does not target.The field names the path; check the platform page.
POST_NOT_FOUND404No post with that id in this workspace.List with GET /posts.
POST_NOT_EDITABLE409The post is publishing, published, partially published or cancelled.Create a new post.
CONNECTION_NOT_FOUND404A connectionIds entry is not in this workspace.List with GET /connections.
CONNECTION_EXPIRED409The connection is not active or its token has expired.Reconnect the account in the app.
APPROVAL_REQUIRED409POST /posts/:id/publish on a post that is pending approval.A member approves it in the app. Do not retry.
IDEMPOTENCY_KEY_CONFLICT422The Idempotency-Key was used with a different request.Use a fresh key for a new request.
IDEMPOTENCY_IN_FLIGHT409A request with this key is still running.Wait, then retry with the same key to get its result.
SCOPE_MISSING403The key lacks the route's scope.Create a key with the scope.
VALIDATION_FAILED400 (409 for a taken slug)An Idempotency-Key over 255 characters, an upload contentType the kind does not accept, a taken workspace slug, or a managed key asking only for workspaces:manage.The field names it.
WEBHOOK_NOT_FOUND404No endpoint with that id.List with GET /webhooks.
WEBHOOK_URL_REFUSED400Not https, or a localhost, private or link-local address, or credentials in the URL.Use a public https URL.
WEBHOOK_EVENT_UNKNOWN400An event name outside the catalogue.See Webhooks for the list.
WEBHOOK_DELIVERY_NOT_FOUND404No delivery with that id on that endpoint.List with GET /webhooks/:id/deliveries.
WORKSPACE_NOT_FOUND404No managed workspace with that id under this key's workspace.List with GET /workspaces.
WORKSPACE_NOT_MANAGED403 (400 when malformed)x-overads-workspace names a workspace this key cannot act for, or is not a single uuid.Create the workspace over POST /workspaces, or drop the header.
WORKSPACE_HEADER_NOT_ALLOWED400x-overads-workspace was sent to a /workspaces/* route.Drop the header; those routes act on the agency.
CONNECT_TOKEN_INVALID404A hosted connect link that never existed or was rotated away.Mint a new one with POST /workspaces/:id/connect-url.
CONNECT_TOKEN_EXPIRED410A hosted connect link past its expiresAt.Mint a new one.
CONNECT_SESSION_INVALID401The 15-minute session behind a connect attempt is bad or its link is no longer live.Reload the connect page.

Channel-limit refusals

Every target of a post is checked against its channel's limits when the post is created or edited (POST /posts, PATCH /posts/:id, create_post, update_post). A failure answers 400 with the flat body { statusCode, code, message }; message names the channel and the cap, for example Bluesky takes either images or one video, not both in one post. Video size and duration are checked only when overads knows them from the file's media asset record; nothing is guessed from a URL.

CodeMeaningRecovery
TOO_LONGThe copy for one channel is over its character cap.Give that channel a shorter variants entry.
TOO_MANY_ITEMSMore images, videos or total items than the channel takes; or a second video on a channel that takes one.Trim the media list, or split the post.
MEDIA_UNSUPPORTEDImages and a video together on Bluesky or TikTok, or images on YouTube, which takes video only.Send one kind of media to that channel.
MEDIA_REQUIREDA scheduled post with no media for YouTube (one video), TikTok (one video or photos) or Pinterest (one image or one video). A draft is allowed to be incomplete.Attach media before scheduling.
MEDIA_TOO_LARGEA video over the channel's file cap (Bluesky 100 MB, Mastodon 99 MB, X 512 MB, LinkedIn 500 MB, Instagram, Facebook and Threads 1 GB, Pinterest 2 GB, TikTok 4 GB).Shrink it, or drop that channel.
MEDIA_TOO_LONGA video over the channel's duration cap (X 140 seconds, Bluesky 3 minutes, Threads 5, TikTok 10, Instagram 15, Pinterest 15, Facebook 20, LinkedIn 30, YouTube 12 hours).Trim it, or drop that channel.
MEDIA_TOO_SHORTA Pinterest video under 4 seconds.Use a longer video.
BOARD_REQUIREDA Pinterest post scheduled without platformSettings.pinterest.boardId.Pass boardId; the app's channel settings list the account's boards.

Per-account failures are not errors

A post to three accounts that lands on two answers 200. The account that failed is in targets with status: "failed" and the platform's error text verbatim in error. Read targets, not just the HTTP status, and subscribe to post.failed to be told.