API V2 (recommended)
Authentication and response format
The API token in the Authorization header, the rate limit, the JSON format of payments and errors, and V2 HTTP status codes.
The API token#
Each merchant account has a live token (real money) and a test key (test_…, sandbox). Both are in the merchant area, under My API keys. Send either one in the Authorization header:
HTTP header, or SDK client (which sends it for you)
<?php
use Labyrinthe\LabPay\Client;
$labpay = new Client('test_xxxxxxxxxxxxxxxx'); // ou Client::fromEnv() (LABPAY_API_KEY)- The
X-Api-Token: <token>header is also accepted. - Unlike V1, the token is never read from the request body or the URL (it would end up in logs).
- A key starting with
test_puts the request in test mode; any other value is looked up among live tokens. - Every authenticated response carries the
X-Labyrinthe-Mode: liveortestheader.
Missing or unknown token: 401 response with the UNAUTHENTICATED code.
{
"message": "Token API manquant ou invalide. Envoyez-le dans l'en-tête Authorization: Bearer <token>.",
"code": "UNAUTHENTICATED"
}Requests#
- Base URL:
https://api.labyrinthe-rdc.com/api/V2(uppercaseV). - JSON body (
Content-Type: application/json). V2 always answers in JSON, even without anAcceptheader. - Amounts: string or number, at most 2 decimals (
"1500","12.50",1500). In responses, always strings with 2 decimals ("1500.00"). - Dates: ISO 8601 (
2026-11-11T10:00:00+00:00).
Rate limit#
120 requests per minute per token. Beyond that, the API answers 429 with a Retry-After header (seconds to wait).
Payment format#
Every operation (deposit, payout, refund) returns the same object, in data. It is also the content of notifications.
| Field | Description |
|---|---|
id | LabPay identifier: DEP-… (deposit), PAY-… (payout), RFD-… (refund). |
reference | Your reference (optional, unique per token). |
operation | deposit | payout | refund |
livemode | false for a test payment (sandbox). |
status | pending, then completed or failed (see Tracking). |
gateway_status | Raw LabPay status, informational (e.g. ACCEPTED, ENQUEUED). |
amount | Deposit: amount credited to the merchant. Payout: amount received by the recipient. Refund: amount refunded. |
fee | LabPay fees. |
total_amount | Deposit: paid by the customer. Payout: debited from the merchant balance. |
currency | Currency, 3-letter code (e.g. CDF, USD): one of the country's. |
country | Phone number's country (ISO 3166-1 alpha-3, e.g. COD). |
phone | Phone number, in the country's local format. |
provider | Provider code (e.g. VODACOM_MPESA_COD). |
provider_transaction_id | Provider reference (the one in the customer's SMS), or null. |
failure | { "code", "message" } on failure, otherwise null. |
metadata | Your free-form data, returned as is. |
created_at, completed_at | Creation; final status (null while pending). |
refunded_amount | Deposits only: amount already refunded or being refunded. |
refunded_payment_id | Refunds only: id of the refunded deposit. |
Error format#
An error has the shape { "message", "code" }, plus errors (per-field messages) for a validation error. The message is in French; rely on the code (full list in Errors).
The SDKs turn these responses into typed errors (ValidationException, InsufficientBalanceError…) carrying the code, the HTTP status and the per-field errors.
{
"message": "Le montant doit être un nombre positif avec au plus 2 décimales (ex. \"1500\" ou \"12.50\").",
"code": "VALIDATION_ERROR",
"errors": {
"amount": [
"Le montant doit être un nombre positif avec au plus 2 décimales (ex. \"1500\" ou \"12.50\")."
]
}
}HTTP status codes#
| Code | Meaning |
|---|---|
201 | Payment accepted, awaiting its final status. |
202 | Unknown LabPay network response: the payment stays pending, its status is re-read automatically. |
200 | Read, or resend of an already completed reference. |
401 | Missing or invalid token. |
403 | Blocked token, KYC required, live token required… |
404 | Payment or notification not found. |
409 | Conflict (refund already in progress, payout not cancellable…). |
422 | Invalid data, or payment refused (data.status = failed). |
429 | Rate limit or daily limit reached. |
503 | Maintenance, suspended version or operation, provider closed, LabPay service unavailable (see Suspended features). |
A payment refused at initiation answers 422 with the payment in data (status: "failed", failure set): it was recorded, with its id.