ResourcesError Reference

Error Reference

All POPFAB API errors follow a consistent structure. Use the error code for programmatic handling and message for logging.

Error Response Format

Every error response has the same JSON structure regardless of the endpoint or error type.

Error response structurejson
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "details": [
      {
        "field": "amount",
        "message": "amount must be a positive integer"
      },
      {
        "field": "currency",
        "message": "currency must be one of: NGN, GHS, KES, ZAR, UGX, XOF, USD"
      }
    ]
  }
}
Always log the error.code and error.message fields. For validation errors, inspect error.details for per-field information.

HTTP Status Codes

HTTP StatusError CodeDescriptionAction
400VALIDATION_ERRORThe request body or query parameters failed validation.Check the details array for field-level errors. Fix your request.
401UNAUTHORIZEDNo API key provided or the key is invalid.Include a valid Bearer token in the Authorization header.
403FORBIDDENThe API key does not have permission for this operation.Check that your key's role allows the operation. Owner/Admin required for some endpoints.
404NOT_FOUNDThe requested resource does not exist or belongs to another merchant.Check the ID and ensure the resource belongs to your merchant account.
409IDEMPOTENCY_CONFLICTAn existing request with this Idempotency-Key was made with a different body.Generate a new Idempotency-Key for this payment attempt.
422UNPROCESSABLE_PAYMENTThe payment request is valid but cannot be processed (e.g. refund on a failed payment).Check the message field for the specific reason.
429RATE_LIMIT_EXCEEDEDThe API key has exceeded its rate limit.Back off and retry after the X-RateLimit-Reset timestamp.
503ALL_PROVIDERS_UNAVAILABLEAll providers for this payment type have open circuit breakers.Retry with exponential backoff. Monitor /v1/providers for recovery.
500INTERNAL_ERRORAn unexpected error occurred on POPFAB's servers.Retry with exponential backoff. Contact support if it persists.

Handling Errors in Code

Robust error handling — Node.jsjavascript
async function initiatePayment(payload) {
  const response = await fetch('https://api.popfab.io/v1/payments', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.POPFAB_API_KEY}`,
      'Content-Type': 'application/json',
      'Idempotency-Key': payload.idempotencyKey,
    },
    body: JSON.stringify(payload),
  });

  const data = await response.json();

  if (!response.ok) {
    const { error } = data;

    switch (error.code) {
      case 'VALIDATION_ERROR':
        // Fix the request — log details for debugging
        throw new Error(`Invalid request: ${JSON.stringify(error.details)}`);

      case 'IDEMPOTENCY_CONFLICT':
        // This key was used with different params — generate a new key
        throw new Error('Idempotency key reused with different body');

      case 'RATE_LIMIT_EXCEEDED':
        // Back off and retry
        await sleep(retryAfterMs(response.headers));
        return initiatePayment(payload);

      case 'ALL_PROVIDERS_UNAVAILABLE':
        // Transient — retry with backoff
        await sleep(5000);
        return initiatePayment(payload);

      default:
        console.error('POPFAB error', error.code, error.message);
        throw new Error(`Payment failed: ${error.message}`);
    }
  }

  return data;
}

function retryAfterMs(headers) {
  const reset = headers.get('X-RateLimit-Reset');
  return reset ? Math.max(0, parseInt(reset) * 1000 - Date.now()) : 1000;
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}
Robust error handling — Pythonpython
import os, time
import httpx

POPFAB_API_KEY = os.environ["POPFAB_API_KEY"]

def initiate_payment(payload: dict, idempotency_key: str) -> dict:
    with httpx.Client() as client:
        response = client.post(
            "https://api.popfab.io/v1/payments",
            headers={
                "Authorization": f"Bearer {POPFAB_API_KEY}",
                "Content-Type": "application/json",
                "Idempotency-Key": idempotency_key,
            },
            json=payload,
        )

    data = response.json()

    if response.status_code >= 400:
        error = data["error"]
        code = error["code"]

        if code == "VALIDATION_ERROR":
            raise ValueError(f"Invalid request: {error['details']}")

        elif code == "RATE_LIMIT_EXCEEDED":
            reset = int(response.headers.get("X-RateLimit-Reset", 0))
            wait = max(0, reset - time.time())
            time.sleep(wait or 1)
            return initiate_payment(payload, idempotency_key)

        elif code == "ALL_PROVIDERS_UNAVAILABLE":
            time.sleep(5)
            return initiate_payment(payload, idempotency_key)

        else:
            raise RuntimeError(
                f"POPFAB error {code}: {error['message']}"
            )

    return data

Idempotent Retries

Network failures can leave you uncertain whether a request succeeded. Always retry with the same Idempotency-Key — POPFAB will return the original result without creating a duplicate charge.

If a request times out before you receive a response, retry it with the same Idempotency-Key. Do not generate a new key, as that would create a second payment if the first request actually succeeded.