unauthorized | 401 | Missing, malformed, or invalid API key (or an expired dashboard session on browser-session routes). | Check the Authorization: Bearer header. Re-issue or rotate the key from the dashboard. Not retryable without fixing credentials. |
forbidden | 403 | The account is valid but not allowed to do this — an expired or banned account, or a feature not included in the current plan. For comments, every paid plan can write, so a 403 there is almost always an inactive or expired account rather than a plan question. For messaging, AutoDM and Google review replies it genuinely is the plan. | Don’t retry as-is. Read GET /v1/usage to see what the current plan includes, then check account status or upgrade. |
not_found | 404 | The route doesn’t exist, or a referenced resource (profile, etc.) doesn’t belong to your account. | Check the URL and IDs. Not retryable without changing the request. |
invalid_request | 400 | The request failed validation, either here or at the target platform — a required field is missing, a caption or title is over a platform’s character limit, an unsupported platform was listed, a URL isn’t a valid https:// link, the platform rejected the content. | Fix the request per message. Not retryable without changing the payload. |
quota_exceeded | 429 | A metered allowance is exhausted: the monthly FFmpeg job allowance or the daily DM cap. Publishing has no monthly cap on a paid plan, so on a publish call this only means the account has no active plan at all. A capability that was never enabled answers 403 forbidden instead — that one no amount of waiting will fix. | Not retryable until the allowance resets or the plan is activated/upgraded. Check Usage and surface the message to the user rather than looping retries. |
no_active_plan | 403 | You’re trying to connect a social account (mint a connect link) but the account has no active plan (zero profile allowance). | Activate a plan from the dashboard billing page first. Not retryable as-is. |
profile_limit_reached | 403 | Creating a new profile would exceed the current plan’s profile limit. | Upgrade the plan or delete an unused profile, then retry. |
rate_limited | 429 | You’ve exceeded the per-minute request rate limit (separate from the metered allowances — see Rate limits), or the destination platform rate-limited the call. | Back off and honour the Retry-After header (seconds) before retrying. |
idempotency_conflict | 409 | The Idempotency-Key you sent was already used for a request with different parameters. | Use a new key for a genuinely new request, or confirm the payload matches the original attempt. Not safely retryable with the same key + different body. |
idempotency_in_progress | 409 | A request with this Idempotency-Key is still being processed (a concurrent or overlapping retry landed while the first attempt hadn’t finished). | Wait briefly and retry the same request with the same key — it will pick up the stored result once the first attempt completes. |
page_pin_conflict | 409 | The request named a page or location (facebook_page_id, target_linkedin_page_id or gbp_location_id) but the Profile has a different page pinned. A pinned page overrides the page sent with an upload, so publishing would put the post on the pinned page and still report success. | Remove the field to publish to the pinned page, or unpin the page first — see Pin a Page to a profile. Retrying unchanged cannot succeed. |
page_pin_unverified | 503 | The request named a page, but we could not check whether the Profile has a page pinned. A pin would override the field, so publishing anyway risks putting the post on the wrong page. | Retry shortly — this is transient. Or remove the field to publish to whatever page the profile is pinned to. |
platform_not_startable | 400 | The platform is supported for publishing but cannot be started from the per-platform connect endpoint (Bluesky, which uses an app password rather than OAuth). | Self-serve connection is not currently offered for this platform. Contact support if you need it. |
link_not_found | 404 | A connect-link token that does not exist or has expired (links live 48 hours). One code covers both, so a token cannot be probed for existence. | Mint a fresh link with POST /v1/connections/start. |
platform_not_connected | 409 | The Profile has no account connected for a platform the request targeted, so the post can never go out as sent. | Connect the account under Kết nối in the dashboard, or drop that platform from platforms. Retrying the same request unchanged cannot succeed. If it keeps happening right after each successful post, the platform is restricting the account and reconnecting will not lift it — see the message. |
location_required | 400 | The Google account behind the Profile owns several Business locations, the request named none, and no default location was chosen when Google Business was connected. | Pass location_id (the message lists the available ids, and GET /v1/gbp/locations returns them with titles), or choose a default location when connecting Google Business. |
upstream_error | 502 | A genuine failure inside MADIAD Hub’s publishing pipeline: a network error, or a 5xx while delivering. Content and permission problems are not in here — those are 400/409. | Transient. Safe to retry the whole request only if you sent an Idempotency-Key, so a retry can’t duplicate a post. |
upstream_timeout | 502 | The publish took longer than MADIAD Hub waits, so the outcome is unknown — the post may already be live. Nothing was refunded and the Idempotency-Key you sent is closed against this answer, so replaying it returns this same verdict rather than publishing a second copy. | Do not retry blindly. Check history or the platform itself first; if you decide to send it again, use a NEW Idempotency-Key. |
internal_error | 500 (rarely 502) | An unhandled error inside MADIAD Hub (database error, unexpected exception). Rare, and not something you caused. A few operations may return 502 instead of 500. | Treat both as retryable — retry with backoff. If it persists, contact support with the request details. |