Skip to main content

The error object

Every failure returns the same object. There is no second error shape, and no endpoint that fails differently.

{
"error": {
"type": "invalid_request_error",
"code": "card_invalid_state",
"message": "Card card_01HX8ZQ2K4M7P9RSTV is not frozen.",
"param": "card_id",
"doc_url": "https://developer.aletaplanet.com/api-reference/errors/error-code-catalog#card_invalid_state",
"request_id": "req_01HX8ZQ2K4M7P9RSTV"
}
}

Fields​

FieldAlways presentPurpose
typeYesBroad category. Use to decide how to handle
codeYesStable, machine-readable identifier. Branch on this
messageYesHuman-readable. For logs and engineers, not end users
paramNoThe offending field, when one can be identified
doc_urlYesDeep link to this specific error
request_idYesMatches the Aleta-Request-Id header

Types​

typeHTTPMeaning
invalid_request_error400, 404, 409The request is malformed or contradicts current state
authentication_error401Credentials missing, invalid, or expired
permission_error403Credentials valid but not permitted
validation_error422Well-formed but fails a business rule
rate_limit_error429Too many requests
api_error500, 502, 503Something failed on our side

Branch on code, not message​

code is part of the contract and will not change. message is prose and may be reworded at any time for clarity.

// Correct
if (err.code === 'insufficient_funds') { promptTopUp(); }

// Wrong — breaks the moment the wording improves
if (err.message.includes('insufficient')) { promptTopUp(); }

Do not show message to end users​

message is written for the engineer reading the log. It names internal object IDs and assumes familiarity with the object model. Map code to your own user-facing copy, in your own voice and language.

Multiple validation failures​

When several fields fail at once, 422 returns them together in errors. The top-level code falls back to the generic invalid_parameter — with more than one failure there is no single code to promote, so errors[] carries the detail. A 422 with exactly one failure promotes that failure's own code instead:

{
"error": {
"type": "validation_error",
"code": "invalid_parameter",
"message": "The request failed validation.",
"doc_url": "https://developer.aletaplanet.com/api-reference/errors/error-code-catalog#invalid_parameter",
"request_id": "req_01HX8ZQ2K4M7P9RSTV",
"errors": [
{
"code": "invalid_parameter",
"param": "address.country",
"message": "address.country must be an uppercase ISO 3166-1 alpha-3 country code, for example SGP."
},
{
"code": "missing_parameter",
"param": "date_of_birth",
"message": "This field is required for individual cardholders."
}
]
}
}

What never appears in an error​

Errors from the platform never expose the internals of systems underneath it. You will not see a raw downstream status code, a downstream identifier, or a vendor name in any field.

Where an underlying system fails in a way we cannot map to something actionable, you get a clean 502 with code: "processor_unavailable" and a request_id. That request_id is enough for us to trace the whole call — quoting it in a support ticket is all that is needed.

This matters for your own error handling: the set of codes you must handle is finite, documented, and stable, rather than an open-ended passthrough of whatever a downstream system decided to emit.

Always log request_id​

Log it on every response, not just failures. It is the difference between "a card creation failed sometime yesterday afternoon" and a single lookup that returns the exact request, its body, and its response.

It is also mandatory in support tickets — see contacting support.