ZyloPaydocs

Errors

One error shape, six types, stable codes. How to handle each class of error.

ZyloPay uses HTTP status codes: 2xx succeeded, 4xx the request cannot succeed as sent, 5xx something failed on ZyloPay's side. Every error has the same body:

{
  "error": {
    "type": "idempotency_error",
    "code": "idempotency_key_reused",
    "message": "This Idempotency-Key was already used with a different request.",
    "request_id": "6f1c2a9e-3b7d-4c55-9a0e-2d4b8f1e7c30"
  }
}
FieldMeaning
typeThe class of error. One of the six below.
codeA stable, machine-readable code. Branch on this.
messageA human-readable explanation. It may change: never parse it.
paramThe parameter or header at fault, when there is one.
request_idThe request's ID. Quote it to support.

The full list of codes is in Error codes. New codes may be added without a new API version: handle unknown codes by their type and HTTP status.

Types and what to do

typeHTTPWhat to do
invalid_request_error400, 404, 409Fix the request. 409 codes mean the object is in the wrong state (for example payment_intent_unexpected_state, refund_in_progress). Retrying unchanged will fail again, except refund_in_progress.
authentication_error401Check the key: missing, malformed, expired or revoked. Do not retry.
permission_error403The key lacks a scope, the IP is not allowed, or the account cannot do this (kyb_required, account_disabled, account_restricted). Do not retry.
idempotency_error400, 409Fix the key, or wait for the first request (idempotency_request_in_progress). See Idempotency.
rate_limit_error429Wait Retry-After seconds, then retry. See Rate limits.
api_error500, 503Retry with backoff and the same Idempotency-Key: after request_timeout it replays the real outcome once the work finished; after internal_error or service_unavailable it replays the same error, so check the object, then retry with a new key only if nothing happened.

Declined payments are not errors

A customer's tap that is declined (frozen pass, expired approval, risk decision…) does not produce an HTTP error in your integration. The payment intent reports it in last_payment_error and a payment_intent.payment_failed event is sent. See payment decline codes.

Validation errors

A body or query that fails validation answers 400 with one of:

  • parameter_missing: a required parameter is absent (param names it);
  • parameter_invalid: a value has the wrong type or is out of range;
  • parameter_unknown: the endpoint does not accept this parameter. The API rejects unknown fields instead of ignoring them, so a typo never goes unnoticed.

Handling errors with the Node SDK

@zylopay/node throws one class per error type. Every error extends ZyloPay.errors.ZyloPayError and carries .type, .code, .param, .message, .requestId, .statusCode and .headers.

ClassRaised for
AuthenticationError401 authentication_error
PermissionError403 permission_error
InvalidRequestError400 and 409 invalid_request_error
NotFoundError (subclass of InvalidRequestError)404 resource_missing
IdempotencyErroridempotency_error
RateLimitError429 rate_limit_error
APIError5xx and unknown types
ConnectionErrorNo response: network failure or timeout
SignatureVerificationErrorzp.webhooks.constructEvent rejected a webhook
import ZyloPay from '@zylopay/node';

try {
  await zp.refunds.create({ payment: 'pay_1042', amount: 1000000 }, { idempotencyKey: `refund-${returnId}` });
} catch (err) {
  if (err instanceof ZyloPay.errors.NotFoundError) {
    // resource_missing: wrong ID, or an object of the other mode
  } else if (err instanceof ZyloPay.errors.InvalidRequestError) {
    if (err.code === 'amount_too_large') return showRemainingAmount();
    if (err.code === 'refund_in_progress') return retryLater();
  } else if (err instanceof ZyloPay.errors.IdempotencyError) {
    // idempotency_request_in_progress: the first attempt is still running
  } else if (err instanceof ZyloPay.errors.PermissionError) {
    alertOps(`ZyloPay refused: ${err.code}`, err.requestId); // insufficient_scope, ip_not_allowed, kyb_required...
  } else if (err instanceof ZyloPay.errors.AuthenticationError) {
    alertOps('ZyloPay key rejected', err.requestId);
  } else if (err instanceof ZyloPay.errors.ConnectionError) {
    // no response: retry later with the SAME idempotency key
  }
  throw err;
}

The SDK retries network errors, 409 idempotency_request_in_progress, 429 and 5xx on its own before it throws (see Node SDK).

On this page