Errors
Every 4xx and 5xx response from the gateway uses the same JSON
envelope. Use the error_code for programmatic handling — it’s
stable across releases. The HTTP status indicates the broad category;
the error_code pinpoints the cause.
The envelope
Section titled “The envelope”{ "error": true, "error_code": "1101", "message": "Invalid payment amount", "details": [ { "field": "amount", "message": "Amount must be greater than 0", "code": "AMOUNT_TOO_SMALL" } ], "timestamp": "2026-05-05T10:15:00Z", "request_id": "req_8f3a4c2e9b1d7a6f", "support_reference": "sup_987654321", "transaction_id": "txn_8f3a4c2e9b1d7a6f5c0e8d", "retryable": false, "retry_after": null, "documentation_url": "https://docs.pdirect.com/errors/1101"}| Field | Type | Notes |
|---|---|---|
error | bool | Always true for error envelopes — easy guard for if (body.error) { ... }. |
error_code | string | The four-digit, stable code. Branch on this. |
message | string | Human-readable, localised by Accept-Language. Show to support, not end users. |
details | array | null | Field-level breakdown for 1110 validation errors. |
timestamp | ISO 8601 | Server time the error was generated. |
request_id | string | null | Echoes the request — quote it when contacting support. |
support_reference | string | null | Internal correlation ID shared with the support team. |
transaction_id | string | null | Present when the error relates to a known transaction. |
retryable | bool | true only for transient failures. Never auto-retry on false. |
retry_after | int | null | Seconds to wait before retrying when retryable: true. |
documentation_url | string | null | Deep link into this site for the specific code. |
Code groups at a glance
Section titled “Code groups at a glance”The leading digit groups the cause:
| Range | Group | Example codes |
|---|---|---|
1001–1005 | Authentication | 1001 INVALID_APP_KEY, 1004 APP_SUSPENDED |
1101–1110 | Request validation | 1101 INVALID_AMOUNT, 1106 INVALID_CARD_NUMBER, 1109 MISSING_REQUIRED_FIELD |
1201–1207 | Business logic | 1201 INSUFFICIENT_FUNDS, 1203 DUPLICATE_TRANSACTION |
1301–1305 | Security & rate-limiting | 1301 RATE_LIMIT_EXCEEDED, 1304 INVALID_SIGNATURE |
1401–1405 | Server / system | 1401 INTERNAL_SERVER_ERROR, 1402 SERVICE_UNAVAILABLE |
1501–1505 | Payment processor | 1502 PAYMENT_DECLINED, 1503 PAYMENT_TIMEOUT |
The full catalogue with remediation is at Errors → catalogue.
Branching on error_code
Section titled “Branching on error_code”Always branch on the code, not the message — messages are translated and may change for clarity.
function handlePdirectError(body: { error_code: string; message: string; retryable: boolean }) { switch (body.error_code) { case "1001": // INVALID_APP_KEY case "1004": // APP_SUSPENDED // Stop. Don't retry. Surface to operations. throw new AuthenticationFailed(body.message);
case "1101": // INVALID_AMOUNT case "1102": // INVALID_CURRENCY // Body drifted from session binding. Mint a new session. throw new SessionDriftError(body.message);
case "1301": // RATE_LIMIT_EXCEEDED // Honour retry_after if present. throw new RateLimited(body.retry_after ?? 60);
case "1402": // SERVICE_UNAVAILABLE case "1502": // PAYMENT_DECLINED — note: not retryable from same source case "1503": // PAYMENT_TIMEOUT if (body.retryable) { return scheduleRetry(body); } throw new ProcessorFailure(body.message);
default: throw new UnknownGatewayError(body.error_code, body.message); }}Retry semantics
Section titled “Retry semantics”- Never auto-retry on
retryable: false. Doing so on an authentication or validation error wastes attempts and can trip rate limits. - Use
Idempotency-Keyfor any retry. The gateway will replay the cached response if the same(key, body_hash)arrives within 24 h, and return409if the body changed. See Idempotency. - Honour
retry_afteron429. The headerRetry-After: <seconds>is also set.
SDK error mapping
Section titled “SDK error mapping”Both SDKs surface a typed error object. The string errorCode field
mirrors the gateway’s error_code (note: SDKs use the LOK000–
LOK009 summary classification; the underlying gateway code is on
the same response).
// FlutterPdirectPayCheckout( // ... onError: (PdirectPayOnError e) { print('${e.errorCode} ${e.message}'); // e.errorCode is one of LOK000..LOK009 (summary codes) },);// AngularonError(e: PdirectPayOnError) { console.warn(e.errorCode, e.message); // e.errorCode is PdirectPayApiResponseCode (LOK000..LOK009)}When you need the underlying four-digit gateway code, read it off the
HTTP response when calling endpoints directly via
PdirectHttpClient. The widget-level callbacks deliberately abstract
to the simpler nine-state classification for UI display.
See also
Section titled “See also”- Errors → catalogue — full list of every
error_codewith remediation - Rate limits —
1301in context - Idempotency — retry-safety for
1401/1402/1503