Documentation
Toutes les routes vivent sous https://api.sandbox.akiba-pro.com/v1. Authentifiez chaque requête avec Authorization: Partner sbx_<key_id>.<secret>. Une requête qui déplace de l’argent porte un Idempotency-Key. Les montants sont des entiers en francs CFA, toujours sous la forme {"amount": 5000, "currency": "XAF"}.
Codes HTTP
En cas d’erreur, le corps est toujours {"error": {"code", "message", "details", "request_id"}}. Le champ code est stable : c’est lui que votre programme doit lire.
| HTTP | error.code | Où | Que faire |
|---|---|---|---|
| 401 | not_authenticated | Toutes | Vérifiez l'en-tête Authorization : Partner <key_id>.<secret>, et que la clé n'est pas révoquée. |
| 401 | wrong_environment_key | Toutes | Clé sbx_ en bac à sable, live_ en production. |
| 400 | idempotency_key_required | POST collections, payouts | Générez une clé, enregistrez-la avec la commande avant le premier envoi. |
| 409 | idempotency_key_reused | POST collections, payouts | Une commande, une clé. Pour une autre commande, une nouvelle clé. |
| 422 | validation_failed | Toutes | Lisez details.fields : le champ et la règle enfreinte. |
| 422 | mobile_money_operator_unknown | holder, collections | Demandez au payeur de vérifier son numéro (9 chiffres). |
| 422 | mobile_money_operator_unavailable | holder, collections, payouts, quote | Proposez un opérateur de GET /partner/operators. |
| 422 | test_number_required | holder, collections | Utilisez votre numéro de test ; jamais un vrai numéro en bac à sable. |
| 422 | collection_below_fee | collections | details.fee donne les frais ; demandez un montant supérieur. |
| 404 | not_found | GET {id}, reversements | Identifiant inconnu, hors de votre portée, ou reversements non ouverts pour votre clé. |
| 404 | beneficiary_not_found | payouts, quote | Prenez l'identifiant dans GET /partner/payout-beneficiaries. |
| 409 | insufficient_funds | payouts | Consultez GET /partner/balance : un reversement par un opérateur ne prend que ce que cet opérateur a encaissé. |
| 409 | fees_changed | payouts | details.fee donne les nouveaux frais : redemandez un devis et faites-le confirmer. |
| 409 | beneficiary_cooling_period | payouts | Attendez details.until : 72 heures après tout changement de numéro. |
| 405 | method_not_allowed | Toutes | Vérifiez la méthode HTTP. |
| 415 | unsupported_media_type | POST | Envoyez Content-Type: application/json. |
| 429 | throttled | Toutes | Attendez Retry-After secondes, puis rejouez avec la même clé. |
| 500 | internal_error | Toutes | Rejouez avec la même Idempotency-Key ; transmettez le request_id. |
| 502 | core_refused | collections, payouts | Relisez la ressource par GET avant toute autre action. |
| 503 | core_unavailable | Toutes | Rejouez après quelques secondes, avec la même Idempotency-Key. |
Numéros de test
Le bac à sable n’accepte que 07700xxxx (Airtel) et 06600xxxx (Moov). Les deux derniers chiffres choisissent le scénario.
| Fin | Scénario | Ce qui se passe | Ce que votre application doit faire |
|---|---|---|---|
00 | Succès | Ordre accepté, verdict SUCCESS après quelques secondes | Livrer après lecture de succeeded |
01 | Refus du payeur | Verdict FAILED | Afficher l'échec, proposer de réessayer |
02 | Solde insuffisant | Verdict FAILED, même failure_code que 01 | Afficher l'échec sans en deviner la raison |
03 | Payeur muet | Rien tant que le téléphone virtuel ne répond pas ; expire sinon | Attendre, puis traiter expired |
04 | Délai dépassé | Répond au-delà de votre délai, mais crée le paiement | Renvoyer avec la même Idempotency-Key |
05 | Erreur 500 du fournisseur | Rien n'est créé chez l'opérateur ; issue inconnue | Suivre par GET, ne jamais renvoyer un nouvel ordre |
06 | Réponse illisible | Corps non JSON chez le fournisseur ; issue inconnue | Même traitement que 05 |
07 | Verdict en double | Deux notifications SUCCESS identiques | Rien : une seule écriture côté Akewa |
08 | Verdicts dans le désordre | SUCCESS, puis PENDING en retard | Rien : reste succeeded |
09 | Verdict contradictoire | SUCCESS, puis FAILED | Rien : reste succeeded, anomalie tracée |
10 | Notification absente | Aucune notification ; statut disponible | Rien : conclu par vérification active |
11 | Montant différent | Notification avec 1 FCFA de plus | Rien : le statut confirmé fait foi |
12 | Notification orpheline | Notification d'une transaction inconnue | Rien : rejetée, aucun mouvement |
13 | Statut ambigu | AMBIGUOUS pendant un temps, puis SUCCESS | Rester en attente sans abandonner |
14 | Titulaire inconnu | La recherche de titulaire répond « non trouvé » | Afficher l'écran adapté ; le paiement reste possible |
Référence
Opérateurs
Lister les opérateurs
GET https://api.sandbox.akiba-pro.com/v1/partner/operators
Les opérateurs Mobile Money que votre agence sert en direct. Un payeur ne peut payer que par l'un d'eux : affichez cette liste plutôt qu'une liste figée.
En-têtes
Authorizationstring requisPartner <key_id>.<secret>. Votre clé sbx_ en bac à sable, live_ en production.
Réponses
200 — Les opérateurs ouverts, triés par code.
{
"operators": [
{
"code": "AIRTEL_MONEY",
"label": "Airtel Money"
},
{
"code": "MOOV_MONEY",
"label": "Moov Money"
}
]
}401 — Clé absente, invalide ou révoquée.
{
"error": {
"code": "not_authenticated",
"message": "Authentification requise.",
"request_id": "01JB8Q4W3T7KX2M9"
}
}Erreurs fréquentes : not_authenticatedwrong_environment_key
Opérateurs
Rechercher le titulaire d'un numéro
POST https://api.sandbox.akiba-pro.com/v1/partner/holder
L'opérateur d'un numéro, lu du plan de numérotation, et son titulaire selon le KYC de l'opérateur. C'est un confort avant de payer, jamais une condition : si l'opérateur ne répond pas, vous recevez lookup: "unavailable" et le paiement reste possible.
Limite : 120 recherches par minute et par clé. Limitez vous-même vos visiteurs : ce plafond protège le KYC des opérateurs.
En-têtes
Authorizationstring requisPartner <key_id>.<secret>. Votre clé sbx_ en bac à sable, live_ en production.Content-Typestring requisapplication/json
Corps de la requête
phonestring requisNuméro du payeur : 9 chiffres nationaux (
077004700) ou international (+241 77 00 47 00).
Réponses
200 · trouvé — Le titulaire est connu de l'opérateur.
{
"operator_code": "AIRTEL_MONEY",
"operator_label": "Airtel Money",
"holder_name": "Titulaire Test 4700",
"lookup": "found"
}200 · inconnu — L'opérateur ne connaît pas de titulaire (scénario 14). Affichez « titulaire non vérifié » et laissez continuer.
{
"operator_code": "AIRTEL_MONEY",
"operator_label": "Airtel Money",
"holder_name": null,
"lookup": "unknown"
}422 — Le numéro n'appartient à aucun opérateur.
{
"error": {
"code": "mobile_money_operator_unknown",
"message": "Aucun opérateur mobile money ne correspond à ce numéro. Vérifiez-le, ou passez par un portefeuille GIMAC.",
"request_id": "01JB8Q4W3T7KX2M9"
}
}Erreurs fréquentes : mobile_money_operator_unknownmobile_money_operator_unavailabletest_number_requiredvalidation_failedthrottled
Encaissements
Demander un paiement
POST https://api.sandbox.akiba-pro.com/v1/partner/collections
Envoie une demande de paiement sur le téléphone du payeur. Le verdict vient plus tard : suivez le paiement par GET /partner/collections/{id} et ne livrez qu'à succeeded.
Limite : 60 demandes par minute et par clé.
Votre appel expire sans réponse ? La demande est peut-être partie. Renvoyez-la avec la même Idempotency-Key (scénario 04).
En-têtes
Authorizationstring requisPartner <key_id>.<secret>. Votre clé sbx_ en bac à sable, live_ en production.Idempotency-Keystring requisUn UUID convient. Générée par vous et conservée avec la commande. Même clé et même corps : la réponse d'origine est rejouée.
Content-Typestring requisapplication/json
Corps de la requête
phonestring requisNuméro du payeur. L'opérateur s'en déduit : vous ne le choisissez pas.
amountobject requisLe montant, toujours un objet.
Champs
amountinteger requisEntier strictement positif, en francs CFA. Jamais de décimale.
currencystring requisXAF
partner_referencestring requisVotre référence de commande : 1 à 64 caractères parmi lettres, chiffres et
_ . : -. Elle revient dans chaque réponse.labelstringLibellé libre, 60 caractères au plus.
Réponses
201 — Demande enregistrée et envoyée au téléphone. Une 201 peut déjà porter status: "failed" si l'opérateur a refusé avant l'envoi : lisez toujours status.
{
"id": "0b8e5d2c-41f7-4a39-8c6e-93d1a7f04b25",
"status": "pending",
"amount": {
"amount": 5000,
"currency": "XAF"
},
"fee": {
"amount": 75,
"currency": "XAF"
},
"operator_code": "AIRTEL_MONEY",
"partner_reference": "CMD-2026-0412",
"provider_reference": "SIM8F3A2C91D0",
"failure_code": "",
"created_at": "2026-10-30T09:14:02.118Z",
"settled_at": null
}200 — Rejeu : même Idempotency-Key, même corps. Le paiement d'origine, dans son état actuel ; aucune nouvelle demande au payeur.
{
"id": "0b8e5d2c-41f7-4a39-8c6e-93d1a7f04b25",
"status": "succeeded",
"amount": {
"amount": 5000,
"currency": "XAF"
},
"fee": {
"amount": 75,
"currency": "XAF"
},
"operator_code": "AIRTEL_MONEY",
"partner_reference": "CMD-2026-0412",
"provider_reference": "SIM8F3A2C91D0",
"failure_code": "",
"created_at": "2026-10-30T09:14:02.118Z",
"settled_at": "2026-10-30T09:14:09.402Z"
}409 — Même clé, autre corps.
{
"error": {
"code": "idempotency_key_reused",
"message": "Cette clé d'idempotence a déjà servi pour une autre opération.",
"request_id": "01JB8Q4W3T7KX2M9"
}
}422 — Les frais absorberaient tout le montant.
{
"error": {
"code": "collection_below_fee",
"message": "Montant trop faible : les frais l'absorberaient entièrement. Saisissez un montant plus élevé.",
"details": {
"fee": {
"amount": 75,
"currency": "XAF"
}
},
"request_id": "01JB8Q4W3T7KX2M9"
}
}503 — Processeur indisponible, rien n'a été créé. Rejouez avec la même clé.
{
"error": {
"code": "core_unavailable",
"message": "Le processeur de paiement est momentanément indisponible. Aucune donnée n'a été modifiée ; réessayez dans quelques instants.",
"request_id": "01JB8Q4W3T7KX2M9"
}
}Erreurs fréquentes : idempotency_key_requiredidempotency_key_reusedvalidation_failedmobile_money_operator_unknownmobile_money_operator_unavailabletest_number_requiredcollection_below_feethrottledcore_refusedcore_unavailable
Encaissements
Suivre un paiement
GET https://api.sandbox.akiba-pro.com/v1/partner/collections/{collection_id}
Où en est un paiement. Un paiement resté pending est revérifié auprès de l'opérateur pendant 24 heures, même si le payeur a fermé votre page.
Paramètres de chemin
collection_idstring (uuid) requisLe champ
idde la réponse à la demande.
En-têtes
Authorizationstring requisPartner <key_id>.<secret>. Votre clé sbx_ en bac à sable, live_ en production.
Réponses
200 · payé — Livrez la commande.
{
"id": "0b8e5d2c-41f7-4a39-8c6e-93d1a7f04b25",
"status": "succeeded",
"amount": {
"amount": 5000,
"currency": "XAF"
},
"fee": {
"amount": 75,
"currency": "XAF"
},
"operator_code": "AIRTEL_MONEY",
"partner_reference": "CMD-2026-0412",
"provider_reference": "SIM8F3A2C91D0",
"failure_code": "",
"created_at": "2026-10-30T09:14:02.118Z",
"settled_at": "2026-10-30T09:14:09.402Z"
}200 · échoué — failure_code vaut provider_failed si le fournisseur refuse, provider_expired si le paiement n'aboutit pas.
{
"id": "0b8e5d2c-41f7-4a39-8c6e-93d1a7f04b25",
"status": "failed",
"amount": {
"amount": 5000,
"currency": "XAF"
},
"fee": {
"amount": 75,
"currency": "XAF"
},
"operator_code": "AIRTEL_MONEY",
"partner_reference": "CMD-2026-0412",
"provider_reference": "SIM8F3A2C91D0",
"failure_code": "provider_expired",
"created_at": "2026-10-30T09:14:02.118Z",
"settled_at": "2026-10-30T10:14:02.000Z"
}404 — Identifiant inconnu ou hors de votre portée.
{
"error": {
"code": "not_found",
"message": "Cette ressource est introuvable.",
"request_id": "01JB8Q4W3T7KX2M9"
}
}Erreurs fréquentes : not_foundnot_authenticated
Reversements
Lister les bénéficiaires
GET https://api.sandbox.akiba-pro.com/v1/partner/payout-beneficiaries
Les bénéficiaires que la plateforme a enregistrés pour votre clé : votre numéro de règlement, vérifié dans la console. Le numéro n'est jamais renvoyé en entier.
Les reversements s’ouvrent une fois votre numéro de règlement confirmé.
En-têtes
Authorizationstring requisPartner <key_id>.<secret>. Votre clé sbx_ en bac à sable, live_ en production.
Réponses
200 — wallet : les quatre derniers chiffres.
{
"beneficiaries": [
{
"id": "7c1d2e3f-9a8b-4c5d-8e6f-1a2b3c4d5e6f",
"label": "Compte de règlement",
"operator_code": "AIRTEL_MONEY",
"operator_label": "Airtel Money",
"wallet": "4700"
}
]
}404 — Reversements non ouverts pour cette clé : la route ne se révèle pas.
{
"error": {
"code": "not_found",
"message": "Cette ressource est introuvable.",
"request_id": "01JB8Q4W3T7KX2M9"
}
}Erreurs fréquentes : not_foundnot_authenticated
Reversements
Consulter le solde
GET https://api.sandbox.akiba-pro.com/v1/partner/balance
Ce que votre compte marchand peut encore reverser, au total et par opérateur. Un reversement par un opérateur ne prend que ce que cet opérateur a encaissé.
Les reversements s’ouvrent une fois votre numéro de règlement confirmé.
En-têtes
Authorizationstring requisPartner <key_id>.<secret>. Votre clé sbx_ en bac à sable, live_ en production.
Réponses
200 — Montants en entiers de francs CFA.
{
"available": {
"amount": 48500,
"currency": "XAF"
},
"operators": [
{
"operator_code": "AIRTEL_MONEY",
"operator_label": "Airtel Money",
"collected": {
"amount": 60000,
"currency": "XAF"
},
"paid_out": {
"amount": 20200,
"currency": "XAF"
},
"available": {
"amount": 39800,
"currency": "XAF"
}
},
{
"operator_code": "MOOV_MONEY",
"operator_label": "Moov Money",
"collected": {
"amount": 8700,
"currency": "XAF"
},
"paid_out": {
"amount": 0,
"currency": "XAF"
},
"available": {
"amount": 8700,
"currency": "XAF"
}
}
]
}Erreurs fréquentes : not_foundnot_authenticated
Reversements
Chiffrer un reversement
GET https://api.sandbox.akiba-pro.com/v1/partner/payouts/quote
Ce qu'un reversement coûterait et apporterait, avant de le demander. N'écrit rien et n'exige pas d'Idempotency-Key. Montrez ces chiffres, puis renvoyez les frais affichés dans expected_fee.
Les reversements s’ouvrent une fois votre numéro de règlement confirmé.
Paramètres de requête
beneficiary_idstring (uuid) requisIdentifiant de
GET /partner/payout-beneficiaries.amountinteger requisMontant demandé, en francs CFA, au moins 1.
En-têtes
Authorizationstring requisPartner <key_id>.<secret>. Votre clé sbx_ en bac à sable, live_ en production.
Réponses
200 — total quitte votre compte ; received arrive au bénéficiaire, après les frais de l'opérateur (null s'ils ne sont pas connus).
{
"amount": {
"amount": 20000,
"currency": "XAF"
},
"fee": {
"amount": 200,
"currency": "XAF"
},
"total": {
"amount": 20200,
"currency": "XAF"
},
"provider_fee": {
"amount": 0,
"currency": "XAF"
},
"received": {
"amount": 20000,
"currency": "XAF"
},
"available": {
"amount": 39800,
"currency": "XAF"
}
}404 — Bénéficiaire inconnu de votre clé.
{
"error": {
"code": "beneficiary_not_found",
"message": "Ce bénéficiaire n'est pas enregistré pour ce partenaire.",
"request_id": "01JB8Q4W3T7KX2M9"
}
}Erreurs fréquentes : beneficiary_not_foundmobile_money_operator_unavailablevalidation_failednot_found
Reversements
Reverser vers votre numéro de règlement
POST https://api.sandbox.akiba-pro.com/v1/partner/payouts
Reverse une partie de votre solde vers un bénéficiaire enregistré. Les fonds sont bloqués immédiatement ; un échec les rend par une écriture inverse, une seule fois.
Les reversements s’ouvrent une fois votre numéro de règlement confirmé.
Limite : 10 reversements par minute et par clé.
Un reversement en attente est relancé pendant 24 heures.
En-têtes
Authorizationstring requisPartner <key_id>.<secret>. Votre clé sbx_ en bac à sable, live_ en production.Idempotency-Keystring requisUn UUID convient. Générée par vous et conservée avec la commande. Même clé et même corps : la réponse d'origine est rejouée.
Content-Typestring requisapplication/json
Corps de la requête
beneficiary_idstring (uuid) requisIdentifiant d'un bénéficiaire enregistré. Vous ne transmettez jamais de numéro.
amountobject requisLe montant, toujours un objet.
Champs
amountinteger requisEntier strictement positif, en francs CFA. Jamais de décimale.
currencystring requisXAF
partner_referencestring requisVotre référence de retrait : 1 à 64 caractères parmi lettres, chiffres et
_ . : -.requested_bystringQui, chez vous, a demandé ce retrait (254 caractères au plus). Journalisé avec le reversement.
expected_feeintegerLes frais Akewa affichés par le devis. S'ils ont changé depuis :
409 fees_changed, plutôt qu'un prix que personne n'a vu.expected_provider_feeintegerLes frais de l'opérateur affichés par le devis, même règle.
Réponses
201 — Reversement enregistré, fonds bloqués. Suivez-le par GET /partner/payouts/{id}.
{
"id": "5e4d3c2b-1a09-4f8e-b7d6-c5b4a3928170",
"status": "pending",
"amount": {
"amount": 20000,
"currency": "XAF"
},
"fee": {
"amount": 200,
"currency": "XAF"
},
"total": {
"amount": 20200,
"currency": "XAF"
},
"provider_fee": {
"amount": 0,
"currency": "XAF"
},
"received": {
"amount": 20000,
"currency": "XAF"
},
"beneficiary": {
"id": "7c1d2e3f-9a8b-4c5d-8e6f-1a2b3c4d5e6f",
"label": "Compte de règlement",
"operator_code": "AIRTEL_MONEY",
"operator_label": "Airtel Money",
"wallet": "4700"
},
"reference": "PO-8K2M4Q",
"partner_reference": "RET-2026-0031",
"requested_by": "comptable@okoume-boutique.ga",
"provider_reference": null,
"failure_code": "",
"created_at": "2026-10-30T10:02:44.530Z",
"settled_at": null
}200 — Rejeu idempotent : aucun second reversement.
{
"id": "5e4d3c2b-1a09-4f8e-b7d6-c5b4a3928170",
"status": "pending",
"amount": {
"amount": 20000,
"currency": "XAF"
},
"fee": {
"amount": 200,
"currency": "XAF"
},
"total": {
"amount": 20200,
"currency": "XAF"
},
"provider_fee": {
"amount": 0,
"currency": "XAF"
},
"received": {
"amount": 20000,
"currency": "XAF"
},
"beneficiary": {
"id": "7c1d2e3f-9a8b-4c5d-8e6f-1a2b3c4d5e6f",
"label": "Compte de règlement",
"operator_code": "AIRTEL_MONEY",
"operator_label": "Airtel Money",
"wallet": "4700"
},
"reference": "PO-8K2M4Q",
"partner_reference": "RET-2026-0031",
"requested_by": "comptable@okoume-boutique.ga",
"provider_reference": null,
"failure_code": "",
"created_at": "2026-10-30T10:02:44.530Z",
"settled_at": null
}409 · solde — Le solde de cet opérateur ne couvre pas le montant et les frais.
{
"error": {
"code": "insufficient_funds",
"message": "Solde insuffisant pour cette opération.",
"request_id": "01JB8Q4W3T7KX2M9"
}
}409 · frais — Les frais ont changé depuis le devis.
{
"error": {
"code": "fees_changed",
"message": "Les frais de ce transfert ont changé depuis leur affichage. Vérifiez le nouveau montant avant de confirmer.",
"details": {
"fee": {
"amount": 250,
"currency": "XAF"
}
},
"request_id": "01JB8Q4W3T7KX2M9"
}
}404 — Bénéficiaire inconnu de votre clé.
{
"error": {
"code": "beneficiary_not_found",
"message": "Ce bénéficiaire n'est pas enregistré pour ce partenaire.",
"request_id": "01JB8Q4W3T7KX2M9"
}
}Erreurs fréquentes : idempotency_key_requiredidempotency_key_reusedbeneficiary_not_foundinsufficient_fundsfees_changedbeneficiary_cooling_periodmobile_money_operator_unavailablevalidation_failedthrottledcore_unavailable
Reversements
Suivre un reversement
GET https://api.sandbox.akiba-pro.com/v1/partner/payouts/{payout_id}
Où en est un reversement : pending, succeeded ou failed. Un échec rend les fonds bloqués à votre solde.
Les reversements s’ouvrent une fois votre numéro de règlement confirmé.
Paramètres de chemin
payout_idstring (uuid) requisLe champ
idde la réponse au reversement.
En-têtes
Authorizationstring requisPartner <key_id>.<secret>. Votre clé sbx_ en bac à sable, live_ en production.
Réponses
200 — Versé au bénéficiaire.
{
"id": "5e4d3c2b-1a09-4f8e-b7d6-c5b4a3928170",
"status": "succeeded",
"amount": {
"amount": 20000,
"currency": "XAF"
},
"fee": {
"amount": 200,
"currency": "XAF"
},
"total": {
"amount": 20200,
"currency": "XAF"
},
"provider_fee": {
"amount": 0,
"currency": "XAF"
},
"received": {
"amount": 20000,
"currency": "XAF"
},
"beneficiary": {
"id": "7c1d2e3f-9a8b-4c5d-8e6f-1a2b3c4d5e6f",
"label": "Compte de règlement",
"operator_code": "AIRTEL_MONEY",
"operator_label": "Airtel Money",
"wallet": "4700"
},
"reference": "PO-8K2M4Q",
"partner_reference": "RET-2026-0031",
"requested_by": "comptable@okoume-boutique.ga",
"provider_reference": "SIMP2Q7R4T8W",
"failure_code": "",
"created_at": "2026-10-30T10:02:44.530Z",
"settled_at": "2026-10-30T10:02:51.004Z"
}404 — Identifiant inconnu ou hors de votre portée.
{
"error": {
"code": "not_found",
"message": "Cette ressource est introuvable.",
"request_id": "01JB8Q4W3T7KX2M9"
}
}Erreurs fréquentes : not_foundnot_authenticated