Errors

Error response format and status codes.

All error responses follow a consistent format.

Error Response Format

{
  "error": {
    "code": "USER_NOT_FOUND",
    "message": "User not found",
    "detail": null
  }
}
FieldTypeDescription
codestringMachine-readable error code for programmatic handling
messagestringHuman-readable error description
detailobject | nullAdditional context when available

HTTP Status Codes

StatusMeaning
400Bad Request. The request is malformed or the endpoint rejected it.
401Unauthorized. Invalid or missing API key.
403Forbidden. Valid API key but insufficient permissions, or the client IP is outside the key's allowlist (API_KEY_IP_NOT_ALLOWED).
404Not Found. Resource does not exist.
409Conflict. Resource already exists or state conflict.
422Unprocessable Entity. The request failed validation. See Validation errors.
429Too Many Requests. Rate limit exceeded. See Rate Limiting.
500Internal Server Error. Contact support if persistent.

Errors any endpoint can return

Each endpoint page in the API reference lists the errors specific to that endpoint. The errors below can come from any endpoint, so they are documented only here.

StatusCodeCause
400API_VERSION_HEADER_MISSINGThe Leeward-Version header is missing. See Versioning.
400API_VERSION_INVALIDThe Leeward-Version header is not a supported version.
400PARSE_ERRORThe request body could not be parsed.
400IDEMPOTENT_PARAMETER_MISMATCHA POST reused an Idempotency-Key with a different body. See Idempotency.
401API_KEY_REQUIREDThe Authorization header is missing. See Authentication.
401INVALID_AUTH_HEADERThe Authorization header is not a Bearer token.
401INVALID_API_KEYThe API key is unknown or revoked, or it belongs to another environment or region.
403API_KEY_IP_NOT_ALLOWEDThe request came from an IP address outside the key's allowlist.
403MODULE_NOT_ENABLEDThe project does not have the module the endpoint belongs to. detail.module names it.
403FEATURE_NOT_ENABLEDThe project does not have the feature the endpoint needs. detail.feature names it.
404ROUTE_NOT_FOUNDNo endpoint matches the method and path.
409IDEMPOTENCY_REQUEST_IN_PROGRESSAnother POST with the same Idempotency-Key is still running.
422VALIDATION_FAILEDThe request does not match the endpoint's schema. See Validation errors.
429RATE_LIMIT_EXCEEDEDThe project exceeded its rate limit. See Rate Limiting.
500INTERNAL_SERVER_ERRORThe request failed on our side.

Validation errors

A request that does not match the endpoint's schema returns 422 with code VALIDATION_FAILED. The detail object says what failed:

  • on names the part of the request that failed: body, query, params or headers.
  • errors lists each problem. Every entry has a path to the field and a message. Most entries also carry a code for the rule that failed.

For example, sending cardType instead of type to POST /cards returns:

{
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "Validation failed",
    "detail": {
      "on": "body",
      "errors": [
        {
          "path": "type",
          "message": "Invalid option: expected one of \"VIRTUAL\"|\"PHYSICAL\"",
          "code": "invalid_value"
        }
      ]
    }
  }
}

The API ignores fields it does not recognize. A misspelled field therefore shows up as an error on the required field it was meant to fill. Nested fields use dot-separated paths, such as config.maxAmount.

A few checks run after schema validation, such as decoding a pagination cursor. These also return VALIDATION_FAILED, with a shorter detail in place of errors.

Handling Errors

Use the code field for programmatic error handling. Branch on code rather than the HTTP status, because several errors can share a status. For example, a few endpoints use 422 for their own business-rule errors alongside VALIDATION_FAILED.

if (response.error.code === 'USER_NOT_FOUND') {
  // Handle missing user
} else if (response.error.code === 'PHONE_NUMBER_ALREADY_EXISTS') {
  // Handle duplicate phone
}

Retries

For 5xx errors, retry with exponential backoff. For 429, wait for the Retry-After header. For other 4xx errors, fix the request before retrying.