Docs/Getting started
View Markdown

Errors and retries

Relay returns stable JSON envelopes for application errors. Use the HTTP status for transport behavior, error.code for program logic, and meta.requestId for investigation.

json
{
  "success": false,
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "One or more fields are invalid.",
    "details": [
      {
        "field": "useremail",
        "code": "isEmail"
      }
    ]
  },
  "meta": {
    "requestId": "ab16ed04-fceb-4415-a609-967bc77972fa"
  }
}

HTTP status codes#

StatusDefault codeMeaningWhat to do
400INVALID_REQUEST or VALIDATION_FAILEDThe body, query, asset, amount, or workflow rule is invalidCorrect the request; do not retry unchanged
401UNAUTHORIZEDThe accesskey is missing, invalid, or inactiveFix or rotate credentials
403FORBIDDENThe authenticated actor is not allowed to perform the operationReview account access; do not retry unchanged
404NOT_FOUND or a resource codeThe tenant-scoped resource does not existCheck the resource identifier
409CONFLICTThe request conflicts with current resource stateRetrieve current state before deciding whether to retry
422VALIDATION_FAILEDSemantically invalid input on endpoints that use this statusCorrect the fields shown in details
429RATE_LIMITEDA plan or protection limit was reachedRespect Retry-After when present and back off
500INTERNAL_ERRORRelay encountered an unexpected errorRetry with backoff; contact Relay if it persists

Relay does not publish one global request quota. Limits may vary by environment or commercial plan. Build clients that can safely handle 429 without assuming a fixed requests-per-minute value.

Business validation errors#

Some 400 responses have a message specific to the business rule. Common examples include:

Message or codeCause
Invalid Crypto TokenThe network/token pair is not active for payment intents
INVALID_PAYMENT_AMOUNTThe amount is non-positive or cannot fit the token precision
External Wallet Address is Requiredexternal_wallet was selected without a destination
Asset is unavailableA customer-wallet asset is inactive or missing its contract configuration
Customer wallet fees are not configured for this assetRelay has not configured a fee policy for the organization and asset
PAYMENT_NOT_FOUNDNo payment intent with that txId belongs to the organization

Messages add context but may become more specific. Prefer branching on error.code where a dedicated code exists.

Retry policy#

Retry only errors that can reasonably recover without changing the request:

  • network failures before you receive a response;
  • 429 responses, honoring a bounded Retry-After; and
  • 500 responses with exponential backoff and jitter.

Do not automatically retry 400, 401, 403, 404, or ordinary 409 responses.

javascript
const retryable = response.status === 429 || response.status >= 500;
const retryAfter = Number(response.headers.get("retry-after") ?? 0) * 1000;
const delay = Math.max(retryAfter, Math.min(30_000, 500 * 2 ** attempt));

Creation and idempotency#

txRef is your merchant reference; it is not an HTTP idempotency key. The current payment-intent creation routes do not accept a documented idempotency header. A repeated creation request can create another intent.

For ambiguous timeouts:

  1. record whether Relay returned a txId before retrying;
  2. use a durable local state machine around each order;
  3. avoid blindly sending the same create request again; and
  4. reconcile with your stored Relay resource or contact Relay using the request ID when the outcome is uncertain.

Read endpoints are safe to retry. Webhook deliveries are explicitly at-least-once and keep the same event ID across retries.

Relay Finance APIServer-to-server financial infrastructure.