Errors Overview
Every transmute error is RFC 9457 application/problem+json with a type URI from a closed catalog. The shape, the catalog, and how to handle errors.
The shape
Every error — regardless of your Accept header — is application/problem+json (RFC 9457):
{
"type": "https://docs.transmute.403fin.io/errors/invalid-input",
"title": "Invalid input",
"status": 400,
"detail": "envelope field "to" is required",
"instance": "/v1/convert",
"requestId": "req_01J9Z3A8B2C3D4E5F6G7H8J9K0",
"errors": [
{ "field": "to", "detail": "required" }
]
}
| Member | Meaning |
|---|---|
type | A URI from the closed catalog below — each resolves to its documentation page. Match on this, not on title or detail text. |
status | The HTTP status, repeated in the body |
detail | Human-readable specifics. Never contains message content. |
requestId | Same value as the X-Request-Id header — quote it to support |
errors[] | Optional field-level entries ({field, detail}) on validation-style failures |
The one exception: protocol errors from POST /v1/oauth/token follow RFC 6749 ({"error": "invalid_client"} plain JSON) so standard OAuth libraries interoperate.
The closed catalog
The type catalog is closed — no error outside this list will ever be emitted, so you can switch on the slug exhaustively.
| Slug | Status | Bills on live key? |
|---|---|---|
invalid-input | 400 | Yes |
unsupported-format | 422 | Yes |
unsupported-conversion | 422 | Yes |
charset | 422 | Yes |
message-too-large | 413 | No |
unauthorized | 401 | No |
forbidden-scope | 403 | No |
forbidden-ip | 403 | No |
rate-limited | 429 | No |
quota-exceeded | 429 | No |
idempotency-conflict | 409 | No |
not-found | 404 | No |
batch-unavailable | 503 | No |
connection-failed | 422 | Yes |
provider-error | 502 | No |
internal | 500 | No |
Portal-only slugs (browser flows, never metered): portal-email-rejected 422, portal-forbidden 403, portal-signup-rate 429, portal-unavailable 503.
Handling guidance
- Retry with backoff:
rate-limited(honorRetry-After),internal,provider-error,portal-unavailable. Combine with idempotency so retries are free and safe. - Fix the request, don't retry:
invalid-input,unsupported-*,charset,message-too-large,idempotency-conflict. - Fix credentials/config:
unauthorized,forbidden-scope,forbidden-ip. - Act on your account:
quota-exceeded(upgrade or wait for reset),connection-failed(re-connect the platform).
Remember the billing wrinkle: 400 and content-level 422s bill on live keys — the engine did real work to reject the message. Develop against test keys.