Errors

Every endpoint on this surface returns RFC 9457 Problem Details as application/problem+json, with one narrow exception — see Status and diagnostics.

{
  "type": "https://lutraid.com/problems/offer-expired",
  "title": "Credential offer expired",
  "status": 404,
  "code": "OFFER_EXPIRED",
  "requestId": "…"
}
  • Name
    type
    Type
    uri
    Description

    A stable URI identifying the problem type. Not necessarily dereferenceable.

  • Name
    title
    Type
    string
    Description

    Stable interface text for developers, not localized UI copy — build your own user-facing messages from code.

  • Name
    status
    Type
    integer
    Description

    The HTTP status code, repeated in the body.

  • Name
    code
    Type
    string
    Description

    A stable, uppercase identifier — this is what you should branch on. The set of codes grows as the API does; treat one you don't recognize as opaque rather than assuming it can't happen.

  • Name
    requestId
    Type
    string
    Description

    Quote this when you report a problem.

Validation errors

A 422 uses the same envelope with code: "VALIDATION_ERROR" plus a field-level errors array:

{
  "type": "https://lutraid.com/problems/validation-error",
  "title": "Request validation failed",
  "status": 422,
  "code": "VALIDATION_ERROR",
  "requestId": "…",
  "errors": [
    {
      "code": "REQUIRED",
      "location": "body",
      "pointer": "/metadata/redirectUri"
    }
  ]
}

errors[].code is one of INVALID, INVALID_FORMAT, INVALID_TYPE, INVALID_VALUE, REQUIRED, TOO_BIG, TOO_SMALL, UNRECOGNIZED_KEYS; location is body, cookie, header, path or query; pointer is a JSON Pointer into the request body. No free-text detail field is included by design.

Codes you will meet

StatusCodeMeaning
401INVALID_API_KEYmissing key, malformed key, wrong secret, revoked key
401API_KEY_EXPIREDkey had a configured expiry that has passed
403PERMISSION_DENIEDkey scoped to a different issuer, verifier or organization
404RESOURCE_NOT_FOUNDissuer, verifier, profile or record not found
404OFFER_NOT_FOUNDcredential offer ID doesn't exist on this issuer
404OFFER_EXPIREDoffer's window closed before the wallet claimed it
409OFFER_ALREADY_CLAIMEDthe wallet already exchanged this offer
404VERIFICATION_REQUEST_NOT_FOUNDverification ID doesn't exist on this verifier
404VERIFICATION_REQUEST_EXPIREDrequest's window closed before the wallet responded
422VALIDATION_ERRORrequest body failed schema validation — see errors[] above
500INTERNAL_ERRORunexpected server error — the message is generic in production

Wallet protocol endpoints

The token, credential, nonce and PAR endpoints — called by wallets, not by you — follow OAuth 2.0 error conventions instead:

{
  "error": "invalid_proof",
  "error_description": "Proof JWT signature did not verify"
}

These are returned to the wallet, not to you — they surface in the diagnostics timeline rather than in a response you handle directly.

Was this page helpful?