Branch on
error.status and error.details[].reason. Never string-match error.message — its wording changes between releases.The envelope
Every error response is a JSON object with exactly one top-level key:error.
erroris always present.error.code,error.status,error.message, anderror.detailsare always present.error.detailsis always an array. It may be empty ([]), but it is nevernulland never missing.- The response
Content-Typeis alwaysapplication/json; charset=utf-8.
Field reference
error.status vs error.details[].reason
Both carry stable UPPER_SNAKE_CASE identifiers. The practical rule:
- For simple failures with a single cause (most cases),
error.statusanderror.details[0].reasoncarry the same value (e.g., bothWALLET_NOT_FOUND). - For validation failures with multiple field issues,
error.statusis the cross-cutting class (INVALID_ARGUMENT) anderror.details[]carries one entry per invalid field.
error.status is the coarse classifier; details[].reason is the specific cause.
Status to HTTP mapping
The mapping is fixed.
The full catalog of reason codes returned by this API lives in the Error catalog.
Validation errors (INVALID_ARGUMENT)
The most common error during integration is request validation. The platform formats these consistently across every endpoint.
error.statusis"INVALID_ARGUMENT".error.codeis400.error.messageis a generic summary, e.g."One or more fields have invalid values.".error.details[]contains one entry per failed constraint.
field vs param
The platform distinguishes body fields from query/path parameters.
Each detail carries exactly one of these. Use it to locate the offending input in your request.
Example — multiple invalid fields
Example — invalid query parameter
reason than INVALID_FIELD (e.g., INVALID_PAGE_SIZE, INVALID_FILTER, INVALID_ORDER_BY, INVALID_PAGE_TOKEN).
Sanitized 500s
Responses witherror.status: "INTERNAL" receive special treatment.
What sanitization means
When a service produces an internal error, the platform mutates the response before sending it:- The original
error.messageis replaced with a generic phrase such as"An internal error has occurred.". - Any original
details[]entries are dropped. - A single
details[]entry withreason: "ERROR_RECORDED"is appended (when the audit record was persisted successfully).
Anatomy of a recorded internal error
metadata.id (format exc_…) is the single most valuable piece of information you can forward to support. With it, a BlooBank engineer can locate the full audit trail in seconds.
What to do on INTERNAL
- Locate the detail with
reason === "ERROR_RECORDED". - Extract
metadata.id. - Log it with the URL, method, approximate UTC time, and your
X-Access-Request-Id. - Forward it when escalating.
ERROR_RECORDED is missing (rare — the audit-record persistence itself failed), send the approximate UTC time and your request id; engineers can still trace via access logs.
What is not in the envelope
Do not depend on these — they may appear or disappear between releases:- Top-level fields outside
error(timestamp,path,traceIdat the root). Treat as informational. - Nested fields inside
errorother than the four documented above. - Stack traces. Never returned, regardless of failure mode.
Next
Error catalog
Every reason code returned by the API, with remediation.
Handling errors
Branching patterns and retry strategy in code.