Error Handling
HTTP error codes returned by the Chargeflow API, what causes each one, how to resolve them, and the health check endpoint for verifying your key.
This page explains the HTTP error codes returned by the Chargeflow API, which of them are safe to retry, and how to retry correctly.
Retry safety at a glance
| Status | Cause | Remedy | Retry-safe? |
|---|---|---|---|
400 | Malformed JSON, wrong types, missing fields | Fix the request body/params | No - fix first |
401 | Not returned by the Chargeflow API today: authentication failures return 403 | Treat as a credentials problem | No - fix first |
403 | Missing, invalid, or revoked x-api-key; malformed signature/headers; or a valid key without access to the resource | Diagnose with GET /health-check/access-key (below) | No - fix first |
404 | Wrong URL or ID, or resource deleted | Verify the URL and ID | No - fix first |
429 | Rate limit exceeded | Back off and retry; honor Retry-After if present | Yes - after waiting |
500 | Unexpected server error | Retry with backoff; contact support with requestId if persistent | Yes - with backoff |
502 | Temporary gateway/upstream issue | Retry with backoff; check the status page | Yes - with backoff |
GET requests are always safe to repeat. Before retrying a write (POST) request, check whether it succeeded; full idempotency semantics are not published yet - see Idempotency for status.
Request IDs
Every API response includes a requestId field with a unique identifier for that request. Include this requestId when contacting Chargeflow support; it allows the team to locate and diagnose your request quickly.
HTTP error codes
Retry with backoff
Paste-ready retry helpers for the retry-safe statuses (429, 500, 502). Both honor Retry-After when present and fall back to exponential backoff with jitter.
const RETRYABLE = new Set([429, 500, 502]);
async function fetchWithRetry(url: string, init: RequestInit = {}, maxAttempts = 5) {
for (let attempt = 1; ; attempt++) {
const res = await fetch(url, init);
if (!RETRYABLE.has(res.status) || attempt === maxAttempts) return res;
const retryAfter = Number(res.headers.get('retry-after'));
const backoff = 2 ** attempt * 500 + Math.random() * 500; // exponential + jitter
const waitMs = retryAfter > 0 ? retryAfter * 1000 : backoff;
await new Promise((r) => setTimeout(r, waitMs));
}
}Health check endpoint
Two endpoints, two different questions. Each answers exactly one:
| Question | Call | Answer |
|---|---|---|
| Is the API up? (availability) | GET /public/2025-04-01/health-check | 200 "OK" means the service is reachable. No API key required, and none is checked. |
| Is my key valid? (authentication) | GET /public/2025-04-01/health-check/access-key | 200 "OK" means the key is valid and active. 403 {"message":"Forbidden"} means it is missing, invalid, or revoked. |
| May I call this resource? (authorization) | the endpoint itself | A 403 from the endpoint while /health-check/access-key returns 200 is an entitlement or signature problem, not a credentials problem. |
# Availability: no credentials involved
curl -X GET https://api.chargeflow.io/public/2025-04-01/health-check
# Credentials: is this key valid and active?
curl -X GET https://api.chargeflow.io/public/2025-04-01/health-check/access-key \
-H "x-api-key: YOUR_API_KEY""OK"Both return the bare string "OK", so a 200 is the whole signal.