Two layers of errors
Requests pass through an API gateway before they reach a service. The two layers fail differently, and only the service layer returns JSON.Gateway errors (plain text)
A missing token yields
403, not 401. Check the response Content-Type before parsing, because gateway errors are text/plain.
Service error format
Once a request reaches a service, errors follow the JSON:API error format, returned as an array in theerrors field:
Only
detail is guaranteed to be present. All other fields are optional.
The pointer is relative to the request body root (
/email), not the JSON:API document root
(/data/attributes/email).Validation errors vary by service
Two different shapes are in use. Handle both. me-creds and me-wallet emit one error object per failed field, withcode: "VALIDATION_ERROR" and a source.pointer. That is the shape above.
consumer-onboarding and tenant provisioning emit a single BAD_REQUEST with the field paths under meta.issues:
HTTP status codes
Error codes
Not every service emits every code. The
PRESENTATION_REQUEST_* codes come from the wallet API, and only some services emit FORBIDDEN, UNPROCESSABLE_ENTITY, and SERVICE_UNAVAILABLE.
Common errors
403 and 401: token missing, malformed, or expired
403 and 401: token missing, malformed, or expired
This is the most common failure. The gateway rejects it, so the body is plain text:A missing
Authorization header gives 403, not 401. A malformed token gives 401 Jwt header is an invalid JSON. A token for the wrong client gives 403 Audiences in Jwt are not allowed.Fix. Request a new access token from the authentication endpoint. Tokens expire after 300 seconds (five minutes).The wallet API returns the service-layer form instead:400 Bad Request
400 Bad Request
The request body contains invalid data. Check On me-creds and me-wallet, the service returns one error object per failed field, so a request with three bad fields returns three entries in
source.pointer, or meta.issues[].path depending on the service, to identify the field.errors. On consumer-onboarding and tenant provisioning you get a single BAD_REQUEST object and the per-field detail sits in meta.issues[]. Read both. Iterate errors[] and, for each entry, check source.pointer first, falling back to meta.issues[].path.404 Not Found
404 Not Found
The resource you referenced doesn’t exist. This typically means a template ID, user ID, or presentation ID is incorrect or has been deleted.
409 Conflict
409 Conflict
The resource you are trying to create already exists.
410 Gone: presentation request expired
410 Gone: presentation request expired
The holder tried to answer a presentation request past its
expiresAt. Requests expire 15 minutes after creation, so generate them at the moment you’re ready to show them rather than in advance.Fix. Create a new presentation request and deliver it again. You cannot revive the expired one.422 Unprocessable Entity: request unsatisfiable
422 Unprocessable Entity: request unsatisfiable
The holder has no credential that satisfies the request. Either they don’t hold the credential type you asked for, or the credentials they hold don’t come from an issuer you accept.Fix. This is not a client bug. Check the preset’s trusted issuers against what the holder has.
500 Internal Server Error
500 Internal Server Error
An unexpected error occurred on the server. Your request did not cause it. Retry after a short delay, and contact support if the problem persists.

