Resources

Error codes

Every V2 error code and V1 error response: meaning, what to do, whether to retry, suspended features and SDK error classes.

API V2#

A V2 error has the shape { "message", "code" } (plus errors for a validation). The message is in French and may change: base your logic on the code, and accept an unknown code.

Example
{
  "message": "Solde insuffisant : 1200.00 CDF disponibles, 5175.00 CDF demandés.",
  "code": "INSUFFICIENT_BALANCE"
}
CodeHTTPMeaningWhat to doRetry
UNAUTHENTICATED401API token (or test key) missing or invalid.All operationsSend Authorization: Bearer <token>; check the key in the merchant area.No
TOKEN_BLOCKED403The token is blocked by the platform.POST /deposits, POST /payouts, POST /refundsContact support.No
MERCHANT_INACTIVE403The merchant account linked to the token is inactive.POST /deposits, POST /payouts, POST /refundsContact support.No
KYC_REQUIRED403Account KYC verification required to collect or send live (never with the test key).POST /deposits, POST /payoutsComplete the account verification in the merchant area.No
LIVE_TOKEN_REQUIRED403Live token only: the test key cannot rotate secrets.POST /account/webhook-secret/rotate, POST /account/test-key/rotateCall again with the live token.No
PAYMENT_NOT_FOUND404No V2 payment of this token (and mode) has this identifier.GET /payments/{id}, POST /payments/{id}/sync, POST /payouts/{id}/cancelCheck the id (e.g. DEP-…) and the key used (test or live).No
DEPOSIT_NOT_FOUND404payment_id matches no V2 deposit of this token.POST /refundsPass the id of a deposit (DEP-…).No
NOTIFICATION_NOT_FOUND404Notification not found for this token.POST /notifications/{id}/resendTake the id from GET /notifications.No
NOT_A_PAYOUT409Only a payout can be cancelled.POST /payouts/{id}/cancelFor a completed deposit, use a refund.No
NOT_CANCELLABLE409The payout is not (or no longer) ENQUEUED, or the cancellation was refused.POST /payouts/{id}/cancelRead the payment again: it follows its normal course.No
DEPOSIT_NOT_COMPLETED409Only a completed deposit can be refunded.POST /refundsWait for the deposit's final status.No
REFUND_IN_PROGRESS409A refund of this deposit is already in progress (one at a time).POST /refundsWait for the current refund to finish.Later
DEPOSIT_ALREADY_REFUNDED409The deposit has already been fully refunded.POST /refundsNothing to do.No
VALIDATION_ERROR422Missing or invalid field; errors lists messages per field (in French).All operationsFix the request.No
INVALID_PHONE_NUMBER422Phone number not valid for the country (local or international).POST /deposits, POST /payouts, POST /predict-providerFix the number, or pass country.No
UNSUPPORTED_COUNTRY422Country not open to payments (the message lists available countries). 503 when no country is open.POST /deposits, POST /payouts, POST /predict-providerUse a country from Countries and providers.No
INVALID_CURRENCY422Currency not offered for this country or provider.POST /deposits, POST /payoutsUse a currency of the phone number's country (the message lists them; see Countries and providers).No
UNSUPPORTED_PROVIDER422Unknown provider for this country and operation (the message lists available providers).POST /deposits, POST /payoutsOmit provider (inferred from the number) or use a code from GET /providers.No
INVALID_AMOUNT422The provider does not accept decimals in this currency (e.g. Vodacom in CDF).POST /payouts, POST /refundsSend a whole amount.No
AMOUNT_TOO_SMALL422Amount below the platform or country minimum.POST /deposits, POST /payouts, POST /refundsIncrease the amount.No
AMOUNT_TOO_LARGE422Amount above the cap (country or platform), or above what is left to refund on a deposit.POST /deposits, POST /payouts, POST /refundsLower the amount (the message gives the maximum).No
AMOUNT_OUT_OF_BOUNDS422Amount outside the provider's own limits.POST /deposits, POST /payouts, POST /refundsCheck GET /providers (min_amount, max_amount).No
INSUFFICIENT_BALANCE422The account balance (in that currency) does not cover the amount and its fees: nothing is sent.POST /payouts, POST /refundsCollect first, or lower the amount (GET /balances).No
DAILY_LIMIT_REACHED429Daily transaction limit reached for a token not yet enabled for production (live_enabled: false).POST /depositsRetry the next day or ask support for activation.Later
MAINTENANCE503Platform under maintenance: nothing is done.POST /deposits, POST /payouts, POST /refundsRetry later, without a tight loop.Later
API_DISABLED503Payment API temporarily disabled by the platform.POST /deposits, POST /payouts, POST /refundsRetry later.Later
DEPOSITS_DISABLED503Deposits temporarily disabled by the platform.POST /depositsRetry later; the status is shown on the home page.Later
WITHDRAWALS_DISABLED503Payouts and refunds temporarily disabled by the platform.POST /payouts, POST /refundsRetry later.Later
FEATURE_DISABLED503Operation switched off from the LabPay console: refunds (feature: "operations.refunds") or payout cancellation (feature: "operations.cancellations"). Nothing is done.POST /refunds, POST /payouts/{id}/cancelRetry later, without a tight loop; message gives the reason, if any.Later
PROVIDER_TEMPORARILY_UNAVAILABLE503The provider is CLOSED for this operation.POST /deposits, POST /payouts, POST /refundsRetry later or offer another provider (GET /availability).Later
GATEWAY_UNAVAILABLE503The LabPay payment service does not respond: provider list, availability or cancellation temporarily impossible.GET /providers, GET /availability, POST /payouts/{id}/cancelRetry after a few seconds.Later
API_VERSION_DISABLED503The called API version is switched off from the LabPay console (feature: "api.v2", or "api.v1" for V1). Checked before authentication.All operationsRetry later; message gives the reason, if any.Later
V2_DISABLED503The V2 API is temporarily disabled (server configuration).All operationsRetry later.Later
TEST_MODE_DISABLED503Test mode is temporarily disabled: call made with a test_… key (feature: "operations.test_mode" on deposits, payouts, cancellations, refunds and sync). The live token is not affected.All operationsRetry later.Later
  • Payment refused by the provider: 422 response with the payment in data (status: "failed"); the reason is in data.failure.code and data.failure.message, passed on from LabPay.
  • Rate limit (429): { "message": "Too Many Attempts." } response without code, with the Retry-After header.
  • Invalid filters on GET /V2/providers, GET /V2/availability and GET /V2/notifications: 422 { "message", "errors" }, without code.

A 202 response, a network error or a timeout on a payment request do not mean "failure": resend the same reference or read the payment (see Idempotency).

Suspended features (503)#

From its console, LabPay can suspend an API version or an operation (maintenance, provider incident…). The request is then refused before any processing: nothing is created or debited. The 503 response carries the code and, for console switches, the feature field (feature concerned); message gives the reason entered, if any.

503 response
{
  "message": "Les remboursements sont momentanément désactivés.",
  "code": "FEATURE_DISABLED",
  "feature": "operations.refunds"
}
Feature (feature)CodeRefused requests
api.v2API_VERSION_DISABLEDThe whole V2 merchant API (before authentication).
api.v1API_VERSION_DISABLED/V1/payment/* and /V1/user/* (the legacy callback stays open).
Collections (platform setting)DEPOSITS_DISABLEDPOST /V2/deposits; V1: POST /V1/payment/mobile.
Withdrawals (platform setting)WITHDRAWALS_DISABLEDPOST /V2/payouts and POST /V2/refunds; V1: POST /V1/payment/withdrawal.
operations.refundsFEATURE_DISABLEDPOST /V2/refunds
operations.cancellationsFEATURE_DISABLEDPOST /V2/payouts/{id}/cancel
operations.transfersFEATURE_DISABLEDPOST /V1/payment/inter-transfer
operations.test_modeTEST_MODE_DISABLEDWith a test_… key: POST /V2/deposits, /payouts, /payouts/{id}/cancel, /refunds, /payments/{id}/sync.
  • A switch applies within about thirty seconds. Notifications (webhooks) are never cut, and reads only are when the whole version is.
  • Do not retry in a loop: wait, or offer another payment method. The SDKs only retry these refusals after a Retry-After header.
  • The live status (open versions and operations, reasons) is shown on the documentation home page.

With the SDKs#

The SDKs raise an error typed by code (then by HTTP status); a payment refused at initiation (422 with the payment) is returned with status: failed, not raised. Codes newer than the SDK (e.g. API_VERSION_DISABLED, FEATURE_DISABLED) arrive with the class of their status (503 → service unavailable), code intact. Full mapping on each SDK page.

Handle errors

<?php
use Labyrinthe\LabPay\Client;
use Labyrinthe\LabPay\Exception\ApiException;
use Labyrinthe\LabPay\Exception\ConnectionException;
use Labyrinthe\LabPay\Exception\ServiceUnavailableException;
use Labyrinthe\LabPay\Exception\ValidationException;

$labpay = new Client('test_xxxxxxxxxxxxxxxx'); // ou Client::fromEnv() (LABPAY_API_KEY)

try {
    $payment = $labpay->deposits->create(amount: '1000', currency: 'CDF', phone: '0813456789', reference: 'ORDER-42');
    if ($payment->isFailed()) {
        // refusé dès l'initiation (422 avec le paiement) : renvoyé, pas levé
        echo $payment->failure->code;
    }
} catch (ValidationException $e) {
    $e->getErrors();                 // messages par champ
} catch (ServiceUnavailableException $e) {
    $e->getErrorCode();              // MAINTENANCE, DEPOSITS_DISABLED… ; $e->getRetryAfter()
} catch (ConnectionException $e) {
    // PAS un échec : renvoyer la même référence ou payments->findByReference('ORDER-42')
} catch (ApiException $e) {
    $e->getErrorCode();              // toujours le code, jamais le message
}

API V1 (legacy)#

V1 has no code field: an error has success: false, a readable message, results.status (status from the table below) and errors (list) for a validation. The table gives the response name and its HTTP status.

ResponseHTTPMeaningWhat to doRetry
failed400Request refused (currency, phone number, incomplete profile, insufficient balance, LabPay refusal…); message gives the reason.All operationsRead message and fix.No
not_allowed403Validation failed (unknown token, missing field…): errors lists the messages; results.status.name is failed.All operationsFix the fields listed in errors.No
excessive_amount403Amount above 10,000 USD or 20,000,000 CDF; results.status.name is failed.POST /payment/mobile, POST /payment/withdrawalLower the amount.No
limit exceeded403Daily transaction limit of a token not yet enabled for production.POST /payment/mobileRetry the next day or ask support for activation.Later
token blocked403Token blocked by the platform.All operationsContact support.No
internal error500Internal error (provider not recognised by LabPay, unexpected response…).All operationsRetry later or contact support.Later

Suspended features (V1)#

A suspended version or operation also answers 503 in V1, in the usual envelope (success: false, message…) with code and feature added: API_VERSION_DISABLED (api.v1), DEPOSITS_DISABLED (payment/mobile), WITHDRAWALS_DISABLED (payment/withdrawal), FEATURE_DISABLED (payment/inter-transfer).