Error Handling
Error envelope, retry strategy, and distinguishing transient from permanent failures.
Error handling
Every non-2xx response from the Postato API carries a structured error envelope. Treat status code + error field as the pair you branch on; never parse the human message.
Envelope
{
"error": "INVALID_REQUEST",
"message": "Body failed validation",
"details": { "content": ["Required"] }
}error: stable machine code. Safe to switch on.message: human string. Can change at any time; don't match regex against it.details: optional, shape varies byerror. For validation errors it's{ field: string[] }.
Status codes
| Code | Meaning | Your response |
|---|---|---|
400 INVALID_REQUEST | Body failed schema validation. | Fix the payload. Do not retry with the same body. |
401 | Missing or invalid API key. | Surface a "reconnect" prompt; don't retry blindly. |
403 permission_denied | API key is valid but lacks the required permission. | Ask a tenant admin to adjust the agent's grants. |
403 plan_inactive | Tenant's subscription is not active. | Surface billing state, don't retry. |
403 policy_violation | Post violated a workspace policy (agent-specific facet). | facet and reason fields explain which rule. |
403 workspace_required | Caller has access to multiple workspaces and didn't pass an ID. | Re-issue with workspaceId from the response's available_workspaces. |
404 not_found | Resource doesn't exist (or caller can't see it). | Do not retry. |
409 | Resource state conflict (e.g. already revoked). | Do not retry; surface to user. |
422 VALIDATION_FAILED | Post failed platform-specific rules. | Read errors[] for specifics. Not retryable as-is. |
429 | Rate limit hit. | Respect Retry-After header. See Rate limits. |
500 / 502 / 503 / 504 | Transient server / upstream error. | Retry with exponential backoff. |
Retry strategy
Retry: 429 (after Retry-After), 500, 502, 503, 504, plus network-level timeouts.
Don't retry: 4xx except 429. The request is wrong; retrying yields the same error and wastes quota.
Always: use an Idempotency-Key for retried POST /posts so duplicates don't leak into the underlying network. See Idempotency.
Backoff shape we recommend: min(cap, base * 2^attempt + jitter) with base 500 ms, cap 30 s, 5 retries max. Anything longer than 5 min of retrying is probably a deeper problem.
Platform-origin errors
Posts that fail at the social network surface as post.failed with a structured error:
{
"postId": "pst_...",
"status": "failed",
"error": {
"origin": "platform",
"code": "MEDIA_TOO_LARGE",
"message": "Instagram rejected: file exceeds 100 MB",
"platformRawCode": "...",
"retryable": false
}
}retryable tells you whether resubmitting (with the same or different media) could succeed. Trust it: our post worker already did its own retry logic for transient platform errors before marking the post failed.
What to log
At minimum: status, error code, details or message, correlation ID from the X-Request-Id response header. That's enough to reproduce and debug. Skip logging raw response bodies long-term; validation error details can contain user content you shouldn't retain.