Skip to content
API DocsDocs

Error Handling Guide

HTTP errors, payment failures, retry strategies, and debugging patterns

4 min readUpdated Sep 9, 2025

Exirom APIs return two distinct categories of errors: HTTP-level errors (transport/auth issues) and payment-level failures (declines, fraud). Each requires different handling.


#HTTP Status Codes

StatusMeaningHow to Handle
200 OKRequest succeeded — even failed payments return 200Check transactionStatus in the body
400 Bad RequestInvalid or missing parametersFix payload: check required fields, data types, amount format
401 UnauthorizedMissing or expired tokenRe-authenticate via /api/v1/auth, then retry
403 ForbiddenValid token but insufficient permissionsVerify MID, contact Exirom support
404 Not FoundWrong endpoint path or unknown transactionIdCheck URL and IDs against the API Reference
409 ConflictDuplicate requestId for a different requestGenerate a new unique requestId
422 UnprocessableStructurally valid JSON but business rule violatedReview field constraints (e.g., amount precision, unsupported currency)
500 Internal Server ErrorServer-side errorRetry with exponential backoff; contact support if persistent
503 Service UnavailableTemporary outageRetry after delay; check API status page

Key rule: A 200 response does not mean the payment succeeded. Always inspect transactionStatus — a declined payment returns 200 with transactionStatus: "FAILED".


#Payment-Level Failures

When transactionStatus is FAILED, check declineCode for the reason:

CategoryExamplesAction
Retry-eligibleInsufficient funds, temporary issuer unavailablePrompt customer to retry or use another card
Fix requiredExpired card, wrong CVV, invalid card numberAsk customer to update card details
Do not retryCard blocked, fraud flagged, suspected fraudDo not retry — show a generic error and offer alternatives
Contact supportMerchant configuration issuesContact Exirom support

See the Decline Codes Reference for the complete table with customer-friendly messages.


#Retry Strategy for HTTP Errors

For payment decline retry logic (which declineCode values to retry, backoff schedules, idempotency), see Error Recovery Patterns.

#For HTTP 5xx errors (server-side)

Use exponential backoff with jitter:

async function withRetry(fn, maxAttempts = 4) {
  let delay = 1000;
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await fn();
    } catch (err) {
      if (attempt === maxAttempts || err.status < 500) throw err;
      const jitter = Math.random() * 500;
      await new Promise(r => setTimeout(r, delay + jitter));
      delay *= 2; // 1s → 2s → 4s → 8s
    }
  }
}

#For payment retries

Always use the same requestId when retrying a failed payment — this prevents double charges if the first attempt actually succeeded. Only generate a new requestId when starting a brand-new payment for a different order. See Idempotency for the full pattern.


#Debugging Checklist

When a request fails unexpectedly:

  1. Log everything — status code, full response body, traceId (in error responses), requestId
  2. Check transactionStatus — don't assume from HTTP status alone
  3. Check the declineCode — maps to a specific failure reason
  4. Verify environment — sandbox tokens won't work in production and vice versa
  5. Validate your payload — amount format (string, not number), currency (ISO 4217), required fields
  6. Check token expiry — tokens expire after 30 days; a 401 often means a stale token

#Error Response Format

Exirom error responses (4xx/5xx) include a traceId for support escalation:

{
  "httpStatus": 401,
  "internalCode": "auth-4003",
  "errorMessage": "JWT token expiration error",
  "traceId": "b1abae42b85ce937dcc1db984d5fcc7a",
  "subErrors": []
}

Always include the traceId when contacting Exirom support.


Was this helpful?