Requests and responses
The gateway speaks JSON over HTTPS. Every request and response uses
application/json unless explicitly noted (the only exception today
is /payments/card/3ds-callback, which also accepts
application/x-www-form-urlencoded because Mastercard posts that
way).
Request shape
Section titled “Request shape”POST /api/v1/payments/collect HTTP/1.1Host: app.api.gtwy.pdirect.comAuthorization: Bearer sess_AbCdEfGhIjKlMnOpQrStUvWxYz0123456789Content-Type: application/jsonAccept: application/jsonAccept-Language: enIdempotency-Key: 8d3f0e2b-5c7d-4e9f-aab2-2e4f3d8c0b6eX-Device-Fingerprint: 9f8e7d6c5b4a3928176054321aabbccdd11ee
{ "payment_method": "wallet", "user_info": "full", ... }| Header | When |
|---|---|
Authorization: Bearer … | Every authenticated request. <key_id>:<merchant_secret> for /internal/sessions/create, <session_token> for everything else. |
Content-Type: application/json | Required on every POST body. |
Accept: application/json | Implied; safe to send anyway. |
Accept-Language | Optional. Drives translated message fields in errors. Default fr. |
Idempotency-Key | Optional but recommended on every POST. UUID v4. See Idempotency. |
X-Device-Fingerprint | Optional. SHA-256 of stable device attributes. The Flutter SDK attaches this automatically. |
Response shape
Section titled “Response shape”Successful responses return 200, 201, or 202 with a
JSON body whose top-level shape varies per endpoint. See the
API reference for exact schemas.
Pattern: most response objects have a success: true field, a
status string, and a transaction_id you can use for follow-up
queries. Status codes:
| Status | Meaning |
|---|---|
200 OK | Sync success or sync failure (check the body’s success field for hosted-session card flows). |
201 Created | Returned only by /internal/sessions/create — emphasises that a new session row was created. |
202 Accepted | Card payment is in flight (3DS challenge). The body contains a redirect_url you must navigate the customer to. |
4xx / 5xx | Error envelope — see Errors. |
Encoding
Section titled “Encoding”- Strings are UTF-8.
- Amounts are decimal strings (
"12.50", not12.50or1250). The session binds the exact string —"1.00"and"1"are not equivalent at rebind time. - Currencies are lowercase ISO 4217 (
usd,cdf,xof). - Phone numbers are E.164 (
"+243998857000"). - Dates and timestamps are ISO 8601 / RFC 3339 (
"2026-05-05T10:15:00Z"). - UUIDs are lowercase, hyphenated.
Why string-encoded amounts
Section titled “Why string-encoded amounts”Decimal arithmetic in IEEE 754 floats has rounding edge cases that
matter at the cent level. The gateway uses Decimal throughout and
serialises to a string to avoid 12.50 → 12.5 lossy round-trips
through JSON parsers. Treat amounts as opaque strings; do not parse
them into floats unless you’re displaying.
Errors
Section titled “Errors”Every error response follows the same envelope:
{ "error": true, "error_code": "1101", "message": "Invalid payment amount", "timestamp": "2026-05-05T10:15:00Z", "request_id": "req_8f3a4c2e9b1d7a6f", "retryable": false}Branch on error_code. See Errors for the
groups, the full table, and SDK-level mapping.
Localisation
Section titled “Localisation”Send Accept-Language: en (or fr, es, ru, zh, ln) and
the gateway will translate message fields in error responses.
error_code values are not localised — they are stable identifiers,
not display text.
The SDKs accept acceptLanguage per checkout and propagate it to
every request. See
Localization and currencies.
See also
Section titled “See also”- Errors
- Idempotency
- Versioning — how the response shape evolves