Xtopay Docs
Core Concepts

Error Handling

Xtopay error format and how to handle failures gracefully.

Error response format

All errors return a consistent JSON body:

{
  "error": {
    "code": "insufficient_funds",
    "message": "The customer's account does not have sufficient funds.",
    "param": null,
    "doc_url": "https://docs.xtopay.co/errors/insufficient_funds"
  }
}
FieldDescription
codeMachine-readable error code — use this in your code
messageHuman-readable explanation
paramThe request parameter that caused the error, if applicable
doc_urlLink to the relevant error documentation

HTTP status codes

StatusMeaning
200Success
201Resource created
400Bad request — malformed JSON or invalid parameter
401Unauthenticated — credentials missing or invalid
403Forbidden — insufficient scope or wrong environment
404Resource not found
409Conflict — e.g. duplicate idempotency key with different body
422Unprocessable — valid format but business logic error
429Rate limited
500Xtopay server error

Common error codes

CodeStatusMeaning
invalid_credentials401Client ID or Secret is wrong
insufficient_funds422Card or mobile money account has no balance
card_declined422Card issuer declined the charge
invalid_phone400Mobile money number is not valid
payment_not_found404Payment ID doesn't exist in this environment
already_refunded409Payment has already been fully refunded
subscription_cancelled409Action not allowed on a cancelled subscription
rate_limit_exceeded429Too many requests — back off and retry

Handling errors in code

import Xtopay, { XtopayError } from "@xtopay/node";

try {
  const payment = await xtopay.payments.create({ ... });
} catch (err) {
  if (err instanceof XtopayError) {
    switch (err.code) {
      case "insufficient_funds":
        return { error: "Please top up your account and try again." };
      case "card_declined":
        return { error: "Your card was declined. Please try a different payment method." };
      case "rate_limit_exceeded":
        // Exponential backoff
        await delay(2 ** attempt * 1000);
        break;
      default:
        // Log and surface a generic error
        logger.error("Xtopay error", { code: err.code, message: err.message });
        return { error: "Payment could not be processed. Please try again." };
    }
  }
  throw err; // re-throw unexpected errors
}

Rate limits

Endpoint typeLimit
Write operations (POST, PATCH, DELETE)100 requests / 10 seconds
Read operations (GET)300 requests / 10 seconds
Webhook delivery retriesNot counted against your limit

Rate limit headers are included in every response:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 97
X-RateLimit-Reset: 1716537610

On this page