Base URL: https://api.quatapay.com/api/v1
This is the single base URL for both test and live. Test vs live is chosen by the API key prefix and the path: live keys (qpay_live_…) use /gateway/payments; test keys (qpay_test_…) use /gateway/sandbox/payments. The sandbox path mirrors the live API exactly.
All requests must include Content-Type: application/json. All responses follow the envelope format:
{ "success": true, "data": { … }, "error": null }
Error responses:
{ "success": false, "data": null, "error": "Validation failed", "code": "VALIDATION_ERROR", "details": { … } }
Authentication
API key (merchant gateway)
Pass your secret API key as a Bearer token:
Authorization: Bearer qpay_live_…
User JWT (checkout flows)
After a user logs in, pass their access token:
Authorization: Bearer eyJ…
Gateway payments
Create a payment intent
POST /gateway/payments
Auth: API key
Body:
Live keys (qpay_live_…) use this path. Test keys (qpay_test_…) use POST /gateway/sandbox/payments, which mirrors this endpoint exactly.
Response: 201 Created
{
"success": true,
"data": {
"payment": { "id": "…", "slug": "…", "status": "requested", "amount": 5000, "currency": "XAF" },
"checkout_url": "https://quatapay.com/checkout/…"
},
"error": null
}
Get a payment intent
GET /gateway/payments/{payment_id}
Auth: API key (must be the creating merchant's key)
Besides the fields you sent, the payment object includes id, slug, status, expires_at, completed_at, completed_transaction_id and:
List payment intents
GET /gateway/payments?status=succeeded&limit=50
Auth: API key
Hosted checkout — preview
GET /gateway/checkout/{slug}
Auth: None (public)
Returns the intent summary for the checkout UI to display.
Hosted checkout — pay
POST /gateway/checkout/{slug}/pay
Auth: User JWT
Body:
A refusal about this customer (not enough money, a frozen wallet, a limit or risk check) answers its own 4xx (for example 402) and leaves the checkout requested, so they can top up, use another account or pay by Mobile Money.
Hosted checkout — cancel
POST /gateway/checkout/{slug}/cancel
Auth: None
Open a payment link on the hosted checkout
POST /payment-links/{slug}/checkout
Auth: None (public)
What the link's page (https://quatapay.com/pay/{slug}) and the "Pay with QuataPay" button (https://quatapay.com/payment-gateway/btn-pay?slug={slug}&amount=…) call. The customer then pays on the hosted checkout by QuataPay wallet or Mobile Money, with or without an account.
Response: 201 Created with {"slug", "checkout_url"}, or 200 when the same customer's open checkout of that amount is reused. The link's amount rules, expiry and single use apply: a single-use link has one open checkout at a time and is used up once that checkout is paid. 404 for an unknown link, 400 for a deactivated, used or expired one, 409 for a non-XAF link.
Payment intent statuses
Idempotency
Supply idempotency_key on mutating requests. Replaying the same key within 24 hours returns the cached response instead of creating a duplicate transaction.
Rate limits
Exceeding limits returns 429 Too Many Requests.