Error envelope
Every 2.1 response carries a numeric status — 0 for success, 1 for failure — with a short, machine-readable string identifying what went wrong. This page goes code by code: what it most likely means, and what to check first.
Comparing the two APIs?
Rate limits and retry safety, for both v3 and 2.1 together, are covered on the errors and retries concept page. This page is the 2.1 code-by-code lookup. Errors, retries and rate limits
The envelope
Success and failure are both reported through status, with the failing operation adding an error string:
{ "status": 0 }
{
"status": 1,
"error": "crid_unknown"
}
Codes
One row per code in the enumeration — what most likely caused it, and what to fix.
| Code | Likely cause | Fix |
|---|---|---|
| key_invalid | The key segment in the path does not match a known, active key. | Confirm the key segment — a typo, an expired key, or a revoked one all read this way. |
| forbidden | The key is valid but does not carry the scope this route requires. | Check whether the route needs client_v2 or user_v2, and confirm the key carries it. |
| auth_unavailable | The key could not be checked because the authentication dependency was unreachable. | Retry — this is a transient failure of the auth check, not a rejection of the key. |
| crid_unknown | The crid in the path does not exist. | Confirm the crid you sent — a typo or a stale reference is the usual cause. |
| data_invalid | The request body does not match the shape the operation expects. | Check the fields you sent against the operation's reference — retarget and modify are the usual sources. |
| no_message_for_this_crid | The container exists but no message has been recorded against it yet. | Wait for the recording to complete, or check whether this crid was shipped without a message — see the details case below. |
| message_missing | The operation requires a recorded message, and none exists. | Same root cause as no_message_for_this_crid, on an operation that cannot proceed without one. |
| no_language_found | No language could be resolved for the response. | Confirm a language was set at creation, or is otherwise resolvable for this container. |
| no_pdf_template | No PDF template is configured to render the card. | Confirm a card template exists for this platform before calling card. |
| card_renderer_unavailable | The card renderer service could not be reached. | Retry — this is a transient dependency failure, not a problem with the request. |
| card_renderer_failed | The card renderer was reached but failed to render the PDF. | Retry; if it persists, check the card content and template for this crid. |
| webhook_not_found | The webhookId segment in the path does not match a configured webhook. | Confirm the webhookId — it is assigned when the webhook is configured, not chosen by the caller. |
| internal_error | An unexpected failure occurred on the server. | Retry; if it persists, report it with the crid and timestamp. |
The two envelopes, side by side
This is the first mismatch anyone integrating both APIs hits: v3 nests a string code under error, 2.1 reports a numeric status and puts the code directly on error.
{
"error": {
"code": "NOT_FOUND",
"message": "Container not found"
}
}
{
"status": 1,
"error": "crid_unknown"
}
The details special case
The details operation does not fail cleanly when a message is simply missing — it distinguishes a container still awaiting one from a container that will never get one.
- When a message exists, details returns status: 3 with the message data.
- When the crid was marked shipped without ever getting a message, details returns status: 1 with error: "no_message_for_this_crid" and an additional shipped: true field.