Référence API NguePay v1

API REST · réponses JSON · enveloppe { success, data, message }.

# Base URL sandbox  https://api.sandbox.nguepay.com/api/v1
# Base URL prod     https://api.live.nguepay.com/api/v1
# Auth             Authorization: Bearer sk_live_...

Authentification

POST/auth/password/forgot

Demande un lien de réinitialisation. Réponse identique que le compte existe ou non. 5 appels/10 min.

POST/auth/password/reset

Applique le nouveau mot de passe et ferme toutes les sessions ouvertes.

POST/auth/register

Créer un compte marchand. Mot de passe : 10 caractères minimum, lettres et chiffres.

POST/auth/login

Se connecter et obtenir un token (valable 12 h) : { token, merchant, user, new_device }. Si la double authentification est active, aucun token : { two_factor_required: true, challenge }.

POST/auth/two-factor/challenge

Compléter la connexion avec { challenge, code } ou { challenge, recovery_code }. Défi valable 5 min, 5 essais. Échec : 401 NGP022.

POST/auth/two-factor/setup

Démarrer l'activation (session dashboard + current_password). Renvoie secret et otpauth_uri.

POST/auth/two-factor/confirm

Activer avec { code, current_password }. Ferme les autres sessions, renvoie une nouvelle session et 8 recovery_codes, affichés une seule fois.

POST/auth/two-factor/disable

Désactiver avec { code }.

POST/auth/logout

Révoquer le token courant.

GET/auth/me

Profil du marchand authentifié.

Paiements

POST/payments

Initier un paiement.

ParamètreTypeRequisDescription
providerstringouiorange (Orange Money CI) ou stripe (carte). wave, mtn et pawapay sont temporairement indisponibles : refusés avec NGP003 (422)
amountintegerouiMontant en XOF (entier, min 100, max 5 000 000)
currencystringnonXOF
referencestringouiRéférence unique marchand (idempotence)
phonestringselon providerNuméro client (mobile money)
webhook_urlstringnonDestination unique pour cette transaction — elle remplace vos destinations déclarées. Voir URL acceptées
return_urlstringnonOù ramener le client après paiement (≤ 500 caractères)
metadataobjectnonDonnées libres : 20 clés et 4 Ko au plus
# Succès (201)
{ "success": true, "data": { "uuid": "9d3f2b1a-7a10-4c2e-9f51-3b8d6e2c5678", "status": "pending", "checkout_url": "URL de la page de paiement Orange Money" } }

Extrait : la réponse complète est l'objet Transaction. checkout_url : page de l'opérateur à ouvrir (Orange Money, Stripe).

# Erreur (422)
{ "success": false, "message": "Montant invalide", "error_code": "NGP_VALIDATION" }
GET/payments/{uuid}

Statut d'un paiement : objet Transaction.

POST/transactions/{uuid}/cancel

Annuler un paiement pending. Refusé (422) si l'opérateur le dit payé, attend une validation sur le téléphone, est injoignable, ou ne peut pas fermer sa page de paiement — voir Créer un paiement.

POST/checkout/sessions

Ouvrir une session sur la page de paiement NguePay. Clé publique (domaine déclaré) ou clé secrète depuis un serveur. Avec la clé publique, return_url et metadata.return_url doivent avoir la même origine que le domaine de la clé. Une clé secrète envoyée depuis un navigateur est refusée (401 NGP001). Réponse : session de checkout.

Transactions

GET/transactions

Lister les transactions (paginé) : data.transactions, objets Transaction.

GET/transactions/{uuid}

Détail d'une transaction : objet Transaction avec, en plus, son journal logs.

POST/transactions/{uuid}/refund

Rembourser une transaction réussie. Renvoie la transaction mise à jour (200), pas le remboursement — utilisez POST /refunds pour obtenir l'objet Remboursement.

Solde et virements sortants

L'API décaisse aussi : votre solde encaissé finance des virements vers Mobile Money (Orange Money pour l'instant ; certains passent par une validation NguePay) — voir Virements sortants.

GET/balance

Solde disponible et retenu : objet Solde.

GET/ledger

Grand livre. Filtres : direction, source_type, dates. Écritures dans data.entries.

GET/payouts

Lister les virements.

POST/payouts

Envoyer un virement. 30 appels/min.

GET/payouts/providers

Opérateurs capables de décaisser et plafonds en vigueur — format.

GET/payouts/{uuid}

Détail d'un virement : objet Virement.

POST/payouts/{uuid}/cancel

Annuler un virement encore pending, y compris pendant la validation NguePay (review renseigné). 422 s'il est déjà parti.

Destinations webhook

Un compte peut déclarer jusqu'à cinq destinations. Chacune a son secret de signature et sa liste d'événements — voir Webhooks.

GET/webhook-endpoints

Lister les destinations, le catalogue d'événements et le quota.

POST/webhook-endpoints

Déclarer une destination. Le secret est renvoyé une seule fois.

GET/webhook-endpoints/{uuid}

Détail d'une destination (sans le secret).

PUT/webhook-endpoints/{uuid}

Mise à jour partielle : URL, libellé, événements, activation.

DELETE/webhook-endpoints/{uuid}

Supprimer une destination.

POST/webhook-endpoints/{uuid}/rotate-secret

Régénérer le secret. L'ancien cesse immédiatement de signer.

POST/webhook-endpoints/{uuid}/test

Envoyer une livraison webhook.test. 10 appels/min.

Journal des livraisons

GET/webhooks

Lister les livraisons. Filtres : event, status, endpoint, from_date, to_date.

GET/webhooks/{uuid}

Détail d'une livraison : objet Livraison.

POST/webhooks/{uuid}/retry

Rejouer une livraison échouée, vers la même destination.

Clés API

GET/keys

Lister les clés API.

POST/keys

Créer une clé API. La clé secrète n'est renvoyée qu'ici — format.

PUT/keys/{uuid}/origin

Déclarer (ou retirer, avec une valeur vide) le domaine allowed_origin de la clé publique.

DELETE/keys/{uuid}

Révoquer une clé API.

Préférences de notification

GET/notifications/preferences

Lire les préférences de notification.

PUT/notifications/preferences

Modifier les préférences. notification_email et notification_phone ne se modifient que depuis le dashboard : avec une clé API, 403 NGP023.

Rapports

GET/reports/overview

Vue d'ensemble (volumes, taux de succès).

GET/reports/by-provider

Répartition par provider.

GET/reports/time-series

Évolution dans le temps.

GET/reports/top-transactions

Plus gros montants de la période.

GET/reports/export/csv

Export CSV des transactions.

GET/reports/export/pdf

Export PDF de la période.

Tous les rapports exigent from_date et to_date — formats de réponse dans Autres réponses.

Objets renvoyés

Chaque champ que l'API renvoie, tel que le code le produit. Un champ absent de ces tables n'est pas renvoyé.

Conventions communes

SujetRègle
EnveloppeSuccès : { success: true, data, message } — message peut être une chaîne vide. Erreur : { success: false, message, error_code }, plus legacy_code et errors selon le cas, sans data — voir Codes d'erreur.
Montantsamount, available, pending, balance_after : centimes XOF. Tout champ suffixé _xof : XOF entiers. Jamais de décimales.
DatesDans les objets de l'API : ISO 8601 en UTC avec microsecondes, 2026-10-14T10:00:00.000000Z. Dans la session de checkout et les webhooks : 2026-10-14T10:00:00+00:00.
ListesLes éléments sont sous une clé nommée d'après la ressource (transactions, refunds, payouts, entries, webhooks, alerts, blacklist, keys), à côté de pagination: { current_page, per_page, last_page, total } (GET /keys : sans per_page). GET /webhook-endpoints n'est pas paginé.
SuppressionsDELETE et révocations renvoient data: null.
En-têtes de réponseSur les routes appelables avec la clé secrète (y compris quand elles sont appelées depuis le dashboard) : X-RateLimit-Limit (requêtes autorisées par minute, 100 par défaut) et X-RateLimit-Remaining. Sur un 429 : Retry-After, en secondes.

Transaction

Renvoyée par POST /payments, GET /payments/{uuid}, GET /transactions (liste data.transactions), GET /transactions/{uuid}, POST /transactions/{uuid}/cancel, POST /transactions/{uuid}/refund et GET /reports/top-transactions.

ChampTypePrésenceDescription
transaction_iduuidtoujoursAlias historique de uuid, toujours identique. Conservé pour les intégrations existantes.
uuiduuidtoujoursIdentifiant NguePay de la transaction (= transaction_id des webhooks).
statuschaînetoujourspending, processing, succeeded, failed, expired ou canceled (un seul « l »).
providerchaînetoujours, peut valoir nullAlias historique de provider_code, toujours identique.
provider_codechaînetoujours, peut valoir nullorange ou stripe (seuls moyens actifs pour l'instant) ; wave, mtn et pawapay peuvent apparaître sur d'anciennes transactions. null pour une session de checkout dont le client n'a pas encore choisi son moyen de paiement.
amountentiertoujoursMontant en centimes XOF (unité de stockage) : 1500000 pour 15 000 XOF.
amount_xofentiertoujoursMontant en XOF entiers — celui que vous avez demandé. À afficher.
refunded_amount_xofentiertoujoursCumul déjà remboursé, en XOF entiers. 0 tant que rien n'a été remboursé.
currencychaînetoujoursXOF.
referencechaînetoujoursVotre référence de commande, telle qu'envoyée.
merchantobjetparfois absent{ uuid, merchant_code, name } de votre propre compte. Présent seulement quand NguePay a chargé le compte pendant le traitement — en pratique dans la réponse de POST /payments pour Stripe, ou pour un paiement signalé par l'anti-fraude avec notification. Absent partout ailleurs : ne vous y fiez pas.
phonechaînetoujours, peut valoir nullNuméro du payeur masqué : +225 07 *** ** 89 (deux premiers et deux derniers chiffres). *** si le numéro a moins de 4 chiffres, null s'il n'a pas été fourni.
checkout_urlURLtoujours, peut valoir nullPage de l'opérateur où le client paie (Orange Money, Stripe). null quand la validation se fait sur le téléphone (MTN, dépôt direct pawaPay — temporairement indisponibles), et pour une session de checkout tant que le client n'a pas choisi : ce n'est pas l'URL de la page NguePay (voir l'objet Session de checkout).
attemptsentiertoujoursNombre de tentatives refusées de façon récupérable sur la page de paiement NguePay (numéro refusé par l'opérateur, opérateur momentanément indisponible) : le paiement est resté ouvert et le client a pu réessayer. 0 pour un paiement créé par POST /payments.
metadataobjettoujoursVos métadonnées, telles qu'envoyées. Sans métadonnées, la valeur est un tableau vide [], pas {} — prévoyez-le si votre client JSON est typé.
failure_reasonchaînetoujours, peut valoir nullMotif d'échec, renseigné en failed / expired. Valeurs observées : expired, provider_failed, initiation_failed, insufficient_funds, payment_failed (Stripe)… Liste ouverte : traitez une valeur inconnue comme un échec générique.
paid_atdatetoujours, peut valoir nullDate d'encaissement. Renseignée quand status passe à succeeded.
expires_atdatetoujoursÉchéance du paiement : 30 minutes après la création.
created_atdatetoujoursCréation.
updated_atdatetoujoursDernière modification (changement de statut, remboursement…).
linksobjettoujours{ self, refund } : chemins relatifs, sans hôte (/api/v1/transactions/{uuid} et …/refund). refund est présent même quand la transaction n'est pas remboursable.
logstableauGET /transactions/{uuid} seulementJournal des changements de statut, du plus ancien au plus récent : { old_status, new_status, triggered_by, created_at }. old_status vaut null pour l'entrée de création ; triggered_by vaut api, checkout, webhook, system, merchant ou admin.

Session de checkout

Renvoyée par POST /checkout/sessions — 201 à la création, 200 si la même reference est rejouée à l'identique. Volontairement réduite : cette réponse est lisible par le visiteur de votre boutique.

ChampTypePrésenceDescription
uuiduuidtoujoursIdentifiant de la transaction créée — le même que celui de l'objet Transaction et des webhooks.
checkout_urlURLtoujoursPage de paiement NguePay : https://pay…nguepay.com/pay/{uuid}. À ouvrir (redirection ou SDK).
expires_atdatetoujoursÉchéance, 30 minutes après la création. Format 2026-10-14T10:30:00+00:00.
statuschaînetoujourspending à la création ; statut courant en cas de rejeu.

Remboursement

Renvoyé par POST /refunds (201), GET /refunds (liste data.refunds) et GET /refunds/{uuid}. POST /transactions/{uuid}/refund renvoie, lui, la transaction (200), pas le remboursement.

ChampTypePrésenceDescription
uuiduuidtoujoursIdentifiant du remboursement (= refund_id du webhook refund.succeeded).
statuschaînetoujourspending, processing, succeeded ou failed.
amount_xofentiertoujoursMontant remboursé, en XOF entiers. Il n'y a pas de champ amount en centimes sur cet objet.
reasonchaînetoujours, peut valoir nullLe reason que vous avez envoyé (POST /refunds ou POST /transactions/{uuid}/refund, 255 caractères au plus), renvoyé tel quel. Sans motif : requested_by_merchant. Remboursement fait depuis la console Stripe : stripe_charge_refunded.
failure_reasonchaînetoujours, peut valoir nullMotif du refus, en failed.
processed_atdatetoujours, peut valoir nullDate d'exécution par l'opérateur.
created_atdatetoujoursCréation.
transactionobjettoujours{ uuid, reference, provider_code } de la transaction remboursée.

Virement

Renvoyé par POST /payouts (201, 200 sur rejeu), GET /payouts (liste data.payouts), GET /payouts/{uuid} et POST /payouts/{uuid}/cancel.

ChampTypePrésenceDescription
uuiduuidtoujoursIdentifiant du virement (= payout_id des webhooks payout.*).
referencechaînetoujoursVotre référence.
statuschaînetoujourspending, processing, succeeded, failed ou canceled. Un virement en validation NguePay reste pending.
reviewchaînetoujours, peut valoir nullÉtape de validation NguePay, renseignée seulement en pending : awaiting_approval (un administrateur NguePay doit valider) ou awaiting_execution (validé, envoi en cours par l'équipe NguePay). null sinon. Les motifs de la validation ne sont pas exposés. Aussi présent dans les webhooks payout.*. Voir Validation par l'équipe NguePay.
amountentiertoujoursCentimes XOF.
amount_xofentiertoujoursXOF entiers.
currencychaînetoujoursXOF.
providerchaînetoujours, peut valoir nullConnecteur qui exécute le virement : orange (exécuté par l'équipe NguePay). wave et mtn : bientôt disponibles.
beneficiary.typechaînetoujoursmobile_money ou bank.
beneficiary.providerchaînetoujours, peut valoir nullOpérateur du bénéficiaire (orange).
beneficiary.namechaînetoujours, peut valoir nullNom du bénéficiaire.
beneficiary.numberchaînetoujoursNuméro masqué, préfixé de l'opérateur : orange — ************00.
beneficiary.payout_account_uuiduuidtoujours, peut valoir nullCompte de reversement utilisé ; null pour un versement à un tiers.
descriptionchaînetoujours, peut valoir nullLe motif que vous avez fourni.
metadataobjettoujoursVos métadonnées, telles qu'envoyées ([] si aucune). NguePay n'y ajoute rien. La clé _nguepay est réservée : envoyée, elle est ignorée et n'est jamais renvoyée. L'attente de fonds se lit dans awaiting_operator_funds.
failure_reasonchaînetoujours, peut valoir nullMotif d'échec : rejected_by_review (refusé lors de la validation NguePay), manual_execution_failed (échec de l'envoi par l'équipe NguePay), operator_float_insufficient, ou motif de l'opérateur. Liste ouverte.
awaiting_operator_fundsbooléentoujourstrue tant que le virement attend l'approvisionnement du compte NguePay chez l'opérateur du bénéficiaire.
attemptsentiertoujoursNombre de soumissions automatiques du virement à l'opérateur. 0 tant qu'il n'est pas parti, et pour un virement envoyé par l'équipe NguePay.
processed_atdatetoujours, peut valoir nullDate de conclusion (succès, échec ou annulation).
created_atdatetoujoursCréation.
linksobjettoujours{ self } : chemin relatif /api/v1/payouts/{uuid}.

Solde

Renvoyé par GET /balance.

ChampTypePrésenceDescription
availableentiertoujoursSolde engageable maintenant, en centimes.
available_xofentiertoujoursIdem en XOF entiers.
pendingentiertoujoursProvision retenue pour des virements en cours (y compris en validation NguePay), en centimes.
pending_xofentiertoujoursIdem en XOF entiers.
total_xofentiertoujoursavailable + pending, en XOF entiers.
currencychaînetoujoursXOF.
updated_atdatetoujoursDernier mouvement du solde (ou première consultation, pour un compte qui n'a encore rien encaissé).

Écriture du grand livre

Renvoyée par GET /ledger, dans data.entries.

ChampTypePrésenceDescription
uuiduuidtoujoursIdentifiant de l'écriture.
directionchaînetoujourscredit ou debit.
source_typechaînetoujourspayment, refund, payout, payout_reversal ou adjustment.
amountentiertoujoursCentimes, toujours positif.
amount_xofentiertoujoursXOF entiers, toujours positif.
signed_amount_xofentiertoujoursXOF entiers, négatif pour un débit : additionnez-les sans lire direction.
balance_afterentiertoujoursSolde disponible juste après l'écriture, en centimes.
balance_after_xofentiertoujoursIdem en XOF entiers.
currencychaînetoujoursXOF.
descriptionchaînetoujours, peut valoir nullLibellé, ex. Paiement cmd_10452, Virement vir_001 non abouti.
created_atdatetoujoursDate de l'écriture.

Opérateurs de virement

Renvoyé par GET /payouts/providers.

ChampTypePrésenceDescription
providerstableau de chaînestoujoursOpérateurs de bénéficiaire acceptés maintenant : ["orange"] pour l'instant (Wave et MTN MoMo : bientôt disponibles).
limits.min_xofentiertoujoursMontant minimum d'un virement (200).
limits.max_xofentiertoujoursMontant maximum d'un virement (2 000 000).
limits.daily_max_xofentiertoujoursPlafond journalier cumulé.

Destination webhook

Renvoyée par GET, PUT /webhook-endpoints/{uuid} et POST /webhook-endpoints/{uuid}/test (qui renvoie la destination, pas la livraison). POST /webhook-endpoints (201) et POST …/rotate-secret y ajoutent secret. DELETE renvoie data: null.

ChampTypePrésenceDescription
uuiduuidtoujoursIdentifiant de la destination (= data.endpoint du webhook.test).
labelchaînetoujoursNom lisible.
urlURLtoujoursURL appelée.
eventstableautoujours, peut valoir nullÉvénements abonnés ; null = tous, y compris les futurs.
is_activebooléentoujoursfalse : destination suspendue, rien ne lui est livré.
descriptionchaînetoujours, peut valoir nullVotre description.
consecutive_failuresentiertoujoursLivraisons définitivement échouées depuis le dernier succès (remis à 0 au premier succès). La destination n'est jamais désactivée automatiquement.
last_success_atdatetoujours, peut valoir nullDernière livraison réussie.
last_failure_atdatetoujours, peut valoir nullDernier échec définitif.
created_atdatetoujoursCréation.
secretchaînecréation et rotation seulementSecret de signature wh_…. Affiché une seule fois.

GET /webhook-endpoints renvoie { endpoints, available_events, max_endpoints } : la liste des destinations (sans pagination), le catalogue [{ event, description }] des événements auxquels s'abonner, et le nombre maximum de destinations (5).

Livraison webhook

Renvoyée par GET /webhooks (liste data.webhooks), GET /webhooks/{uuid} et POST /webhooks/{uuid}/retry. Le corps envoyé et la réponse de votre serveur ne sont pas exposés.

ChampTypePrésenceDescription
uuiduuidtoujoursIdentifiant de la livraison : c'est la valeur de l'en-tête X-NguePay-Delivery.
eventchaînetoujoursÉvénement livré.
statuschaînetoujourspending (en cours ou en attente de nouvelle tentative), delivered ou failed (définitif).
attemptsentiertoujoursTentatives du cycle en cours (3 au plus). Remis à 0 par un rejeu manuel.
urlURLtoujoursURL appelée, identifiants éventuels masqués (https://***:***@…).
response_statusentiertoujours, peut valoir nullDernier code HTTP renvoyé par votre serveur ; null si aucune réponse (timeout, erreur réseau) ou pas encore tenté.
last_attempt_atdatetoujours, peut valoir nullDernière tentative.
next_retry_atdatetoujours, peut valoir nullProchaine tentative automatique ; null s'il n'y en a pas.
created_atdatetoujoursCréation de la livraison.
endpointobjettoujours, peut valoir null{ uuid, label } de la destination ; null pour une livraison vers la webhook_url d'un paiement ou l'URL historique du compte.
transactionobjettoujours, peut valoir null{ uuid, reference, status } de la transaction concernée, avec son statut actuel (pas celui de l'événement) ; null pour payout.* et webhook.test.

Clé API

Renvoyée par GET /keys (liste data.keys, clés actives seulement) et PUT /keys/{uuid}/origin. Routes réservées au dashboard.

ChampTypePrésenceDescription
uuiduuidtoujoursIdentifiant de la clé.
labelchaînetoujours, peut valoir nullNom donné à la création.
public_keychaînetoujoursClé publique complète pk_live_… / pk_sandbox_….
modechaînetoujourslive ou sandbox.
environmentchaînetoujoursLibellé d'affichage : Live ou Sandbox.
is_livebooléentoujoursmode === 'live'.
secret_key_prefixchaînetoujoursPréfixe de mode de la clé secrète (sk_live_ ou sk_sandbox_) — il ne permet pas d'identifier une clé.
allowed_originchaînetoujours, peut valoir nullDomaine autorisé pour le SDK ; null = clé publique inutilisable dans un navigateur (NGP015).
api_base_urlURLtoujoursURL de base de l'API pour ce mode, …/api/v1.
last_used_atdatetoujours, peut valoir nullDernier appel authentifié avec cette clé.
revoked_atdatetoujours, peut valoir nullDate de révocation (toujours null dans la liste, qui n'affiche que les clés actives).
created_atdatetoujoursCréation.

POST /keys (201) renvoie { api_key, public_key, secret_key } : l'objet ci-dessus, la clé publique, et la clé secrète complète — affichée une seule fois. Une clé live demandée avant validation KYC est refusée en 403 avec error_code: "KYC_REQUIRED".

Autres réponses

ChampTypePrésenceDescription
GET /pingobjettoujours{ merchant, mode } : uuid de votre compte et mode de la clé (live / sandbox), message: "pong".
GET /healthobjettoujours{ status: "ok" } en production ; hors production, s'y ajoutent service et env. Non enveloppé (pas de success).
GET|PUT /notifications/preferencesobjettoujours{ notify_email, notify_sms, notification_email, notification_phone } — deux booléens et deux coordonnées (null si non renseignées).
GET /fraud/alertslistetoujoursdata.alerts : { uuid, risk_score, action_taken, details, resolved_at, resolved_by, created_at, transaction, rule }. action_taken : blocked, flagged ou notified ; details.triggered : règles déclenchées ; resolved_by : e-mail de qui l'a résolue ; transaction : { uuid, reference, amount_xof, provider } ou null ; rule : { name, type } ou null. 25 par page.
GET /fraud/blacklistlistetoujoursdata.blacklist : { uuid, type, value, reason, expires_at, created_at }, type = phone, ip ou email. 25 par page.
GET /reports/overviewobjettoujours{ period: { from, to }, total_transactions, succeeded_count, failed_count, cancelled_count, total_volume_xof, succeeded_volume_xof, success_rate, average_transaction_xof, refunded_count, refunded_volume_xof }. success_rate en pourcentage (2 décimales).
GET /reports/by-providerobjettoujoursUne clé par opérateur, toujours les cinq : orange, stripe (actifs), wave, mtn, pawapay (temporairement indisponibles : à zéro hors historique), chacune { count, volume_xof, success_rate, avg_xof } ; volume_xof et avg_xof ne comptent que les paiements réussis.
GET /reports/time-seriesobjettoujours{ granularity, points: [{ date, count, volume_xof, success_rate }] }, success_rate à 1 décimale, volume_xof sur les paiements réussis.
GET /reports/top-transactionsobjettoujours{ transactions } : objets Transaction, du plus gros montant au plus petit (limit, 10 par défaut, 50 au plus).
GET /reports/export/csvfichiertoujoursCSV non enveloppé, colonnes merchant_code, merchant_name, uuid, reference, provider, amount_xof, status, phone_masked, paid_at, created_at.
Besoin d'aide ?