Skip to main content
Paymnt Cloud returns meaningful HTTP statuses and a JSON error body with a stable shape. Always log the request_id for correlation.

Response format

{
  "error": {
    "type": "validation_error | authentication_error | authorization_error | not_found | conflict | unprocessable_entity | rate_limit | internal_error | connector_error",
    "code": "CE_001",
    "message": "Human-readable description",
    "request_id": "req_9f2a1b",
    "status": 400,
    "details": {
      "field": "amount",
      "reason": "must be >= 1"
    }
  }
}
  • type — broad category for programmatic handling
  • code — short, stable identifier (useful for analytics/alerting)
  • message — concise text, safe to show to logs/ops dashboards
  • request_id — include in support requests
  • status — HTTP status mirrored in the body
  • details — optional object with extra context (e.g., invalid fields)

Common categories

  • validation_error (400) — malformed or out-of-range parameters
  • authentication_error (401) — missing/invalid api-key
  • authorization_error (403) — action not permitted for this key/profile
  • not_found (404) — resource does not exist (e.g., pay_…, cus_…)
  • conflict (409) — conflicting state (e.g., duplicate operation)
  • unprocessable_entity (422) — semantically invalid request
  • rate_limit (429) — too many requests, see Rate Limits
  • internal_error (500) — unexpected server error; safe to retry with backoff
  • connector_error (502/503/504) — upstream PSP/network issue; retry with backoff
See also: Retries & BackoffIdempotency

Examples

400 — validation_error

{
  "error": {
    "type": "validation_error",
    "code": "CE_AMOUNT_INVALID",
    "message": "amount must be a positive integer",
    "request_id": "req_01H9K6N9ZQ7D2",
    "status": 400,
    "details": { "field": "amount" }
  }
}

401 — authentication_error

{
  "error": {
    "type": "authentication_error",
    "code": "CE_AUTH_MISSING",
    "message": "Missing or invalid api-key",
    "request_id": "req_01H9K6QE8Z5D4",
    "status": 401
  }
}

403 — authorization_error

{
  "error": {
    "type": "authorization_error",
    "code": "CE_FORBIDDEN",
    "message": "Not allowed for this key/profile",
    "request_id": "req_01H9K6XCM3V9S",
    "status": 403
  }
}

404 — not_found

{
  "error": {
    "type": "not_found",
    "code": "CE_RESOURCE_MISSING",
    "message": "payment not found",
    "request_id": "req_01H9K70Q8E9C0",
    "status": 404
  }
}

409 — conflict

{
  "error": {
    "type": "conflict",
    "code": "CE_STATE_CONFLICT",
    "message": "payment already captured",
    "request_id": "req_01H9K72Z4B1A7",
    "status": 409
  }
}

422 — unprocessable_entity

{
  "error": {
    "type": "unprocessable_entity",
    "code": "CE_RULE_VIOLATION",
    "message": "profile routing rule blocked this operation",
    "request_id": "req_01H9K75S5E3QF",
    "status": 422
  }
}

429 — rate_limit

{
  "error": {
    "type": "rate_limit",
    "code": "CE_RL_429",
    "message": "Too many requests",
    "request_id": "req_01H9K78W6P9M2",
    "status": 429
  }
}

5xx — internal/connector

{
  "error": {
    "type": "connector_error",
    "code": "CE_CONNECTOR_TIMEOUT",
    "message": "Upstream connector timed out",
    "request_id": "req_01H9K7B8S4GJ9",
    "status": 504
  }
}

Client-side handling (Node example)

async function call(path, opts = {}) {
  const base = process.env.PAYMNT_BASE_URL || "https://sandbox.paymnt.cloud";
  const res = await fetch(`${base}${path}`, {
    ...opts,
    headers: {
      "Accept": "application/json",
      "api-key": process.env.PAYMNT_API_KEY,
      ...(opts.headers || {})
    }
  });

  const text = await res.text();
  const data = text ? JSON.parse(text) : null;

  if (!res.ok) {
    const err = data?.error || { status: res.status, message: "Unknown error" };
    // Log for observability
    console.error("Paymnt error", {
      status: err.status ?? res.status,
      code: err.code,
      type: err.type,
      request_id: err.request_id
    });
    throw new Error(`${err.type || "error"}: ${err.code || res.status} (${err.request_id || "no-request-id"})`);
  }

  return data;
}

Best practices

  • Always log request_id. Include it in support requests
  • Don’t parse by message text. Use type/code for logic and metrics
  • Implement retries with exponential backoff + jitter on 5xx/connector errors and safe 409/422 cases where applicable
  • Use idempotency on mutating requests to avoid duplicates during retries
  • Handle 429 by respecting Retry-After. See Rate Limits
  • Surface actionable context to operators (status, code, type, request_id); keep PII out of logs

Need help?

If you cannot resolve an error, contact Support and include:
  • request_id
  • endpoint and HTTP method
  • approximate timestamp (UTC)
  • high-level description of what you attempted