Error reference
Every error code the API returns, what causes it, and whether retrying helps.
Every failure from /v1 and /mcp uses one envelope. Branch on code; it is stable. Never branch on message, which is written for humans and will change.
{
"error": {
"code": "accounts_not_found",
"message": "None of the supplied accountIds matched a connected account in this profile.",
"docsUrl": "https://cutedyno.com/docs/errors#accounts_not_found",
"requestId": "8f14e45f-ea0c-4d7a-9f1a-2b3c4d5e6f70"
},
"message": "None of the supplied accountIds matched a connected account in this profile."
}Four fields carry the useful information:
code— the stable identifier to switch on.message— a human explanation, safe to log and usually safe to show an operator.docsUrl— a deep link to this page, anchored at the code.requestId— echoes theX-Request-Idheader. Quote it when reporting a problem and we can find the exact request.
Some codes add a details object. plan_limit carries entitlement, usage, and limit; insufficient_permission carries the required permission; daily_post_limit carries limit, used, and resetsAt.
Handling errors in code
import { CuteDynoError } from '@cutedyno/node';
try {
await cutedyno.posts.create(input);
} catch (error) {
if (!(error instanceof CuteDynoError)) throw error;
switch (error.code) {
case 'no_accounts_connected':
case 'accounts_not_found':
return promptUserToReconnect();
case 'plan_limit':
case 'trial_expired':
return showUpgradePrompt(error.details);
case 'rate_limit_exceeded':
return retryAfter(error.retryAfter ?? 60);
default:
if (error.isRetryable) return scheduleRetry();
log.error('CuteDyno rejected the post', {
code: error.code,
requestId: error.requestId,
});
throw error;
}
}The rule of thumb: 4xx is your bug or your user's state, 5xx and 429 are ours. Retry the second group with the same idempotency key; fix or surface the first.
Codes by status
400 — Bad request
| Code | What it means | Retry? |
|---|---|---|
invalid_requestInvalid request | The request body or query string failed validation. The message names the offending field. | No |
invalid_profile_idInvalid profile id | profileId was supplied but was not a string. | No |
invalid_scheduled_atInvalid scheduledAt | scheduledAt could not be parsed as an ISO 8601 datetime, or it is in the past. | No |
invalid_state_transitionInvalid state transition | The post is in a state that does not allow this operation, for example editing a post that already published. | No |
key_not_linked_to_userAPI key not linked to a user | Publishing records a human actor. Recreate the key from the dashboard so it carries an owner. | No |
401 — Unauthenticated
| Code | What it means | Retry? |
|---|---|---|
authentication_requiredAuthentication required | No Authorization header was sent, or it was not a Bearer token. | No |
invalid_api_keyInvalid API key | The key does not exist, was revoked, or is malformed. Keys start with cdyn_live_. | No |
api_key_expiredAPI key expired | The key passed its expiresAt date. Create a replacement key. | No |
402 — Payment required
| Code | What it means | Retry? |
|---|---|---|
trial_expiredTrial expired | The account trial ended. Billing affects every profile at once, so alert an operator instead of retrying. | No |
403 — Forbidden
| Code | What it means | Retry? |
|---|---|---|
insufficient_permissionInsufficient permission | The key is missing the scope this endpoint needs, for example posts:write on a readonly key. | No |
profile_not_accessibleProfile not accessible | The requested profile is outside this key’s scope, or belongs to another account. | No |
plan_limitPlan limit reached | A plan quota was exhausted. The response carries entitlement, usage, and limit so you can surface the reason. | No |
account_not_allowedAccount not allowed | The key is restricted to specific connected accounts and one of the supplied accountIds is not among them. | No |
platform_not_allowedPlatform not allowed | The profile policy restricts which platforms may be posted to, and one of the target accounts is on a platform outside that list. | No |
approval_requiredApproval required | This profile or key forces posts through review, and no approver could be determined. Supply approvers, or recreate the key so it carries an owner. | No |
daily_post_limitDaily post limit reached | The key has a maxPostsPerDay cap and it has been reached. The cap resets at midnight UTC. | No |
404 — Not found
| Code | What it means | Retry? |
|---|---|---|
not_foundNot found | The resource does not exist inside the resolved profile. | No |
no_accounts_connectedNo accounts connected | The profile has no connected social accounts yet. Run the connect flow first. | No |
accounts_not_foundAccounts not found | None of the supplied accountIds matched a connected account in this profile. | No |
409 — Conflict
| Code | What it means | Retry? |
|---|---|---|
idempotency_key_reusedIdempotency key reused | This Idempotency-Key was already used with a different request body. Use a fresh key. | No |
profile_has_connected_accountsProfile still has connected accounts | Disconnect the profile’s social accounts before deleting it, so tokens are revoked deliberately. | No |
415 — Error
| Code | What it means | Retry? |
|---|---|---|
unsupported_media_typeUnsupported media type | The fileType passed to POST /v1/media/upload is not an accepted image or video type. | No |
429 — Too many requests
| Code | What it means | Retry? |
|---|---|---|
rate_limit_exceededRate limit exceeded | Too many requests for this key. Honour the Retry-After header before retrying. | Yes |
500 — Server error
| Code | What it means | Retry? |
|---|---|---|
internal_errorInternal error | Something failed on our side. Retry with the same Idempotency-Key; report the requestId if it persists. | Yes |
Legacy error format
Before this envelope existed, errors were { "error": "some string" }. If you have a client written against that shape and cannot change it yet, send:
CuteDyno-Error-Format: legacyand responses revert to { "error": "<message>", "code": "<code>" }. This is a migration aid with a limited life. Prefer reading error.code from the structured shape, which every current response already includes.
MCP errors
Tool calls surface the same codes. A failing tool returns isError: true with the code, message, and request ID in the text, so an agent can explain what went wrong rather than retrying blindly. See MCP.