Bac à sable — aucun argent réel

akewa développeurs

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.

HTTPerror.codeOùQue faire
401not_authenticatedToutesVérifiez l'en-tête Authorization : Partner <key_id>.<secret>, et que la clé n'est pas révoquée.
401wrong_environment_keyToutesClé sbx_ en bac à sable, live_ en production.
400idempotency_key_requiredPOST collections, payoutsGénérez une clé, enregistrez-la avec la commande avant le premier envoi.
409idempotency_key_reusedPOST collections, payoutsUne commande, une clé. Pour une autre commande, une nouvelle clé.
422validation_failedToutesLisez details.fields : le champ et la règle enfreinte.
422mobile_money_operator_unknownholder, collectionsDemandez au payeur de vérifier son numéro (9 chiffres).
422mobile_money_operator_unavailableholder, collections, payouts, quoteProposez un opérateur de GET /partner/operators.
422test_number_requiredholder, collectionsUtilisez votre numéro de test ; jamais un vrai numéro en bac à sable.
422collection_below_feecollectionsdetails.fee donne les frais ; demandez un montant supérieur.
404not_foundGET {id}, reversementsIdentifiant inconnu, hors de votre portée, ou reversements non ouverts pour votre clé.
404beneficiary_not_foundpayouts, quotePrenez l'identifiant dans GET /partner/payout-beneficiaries.
409insufficient_fundspayoutsConsultez GET /partner/balance : un reversement par un opérateur ne prend que ce que cet opérateur a encaissé.
409fees_changedpayoutsdetails.fee donne les nouveaux frais : redemandez un devis et faites-le confirmer.
409beneficiary_cooling_periodpayoutsAttendez details.until : 72 heures après tout changement de numéro.
405method_not_allowedToutesVérifiez la méthode HTTP.
415unsupported_media_typePOSTEnvoyez Content-Type: application/json.
429throttledToutesAttendez Retry-After secondes, puis rejouez avec la même clé.
500internal_errorToutesRejouez avec la même Idempotency-Key ; transmettez le request_id.
502core_refusedcollections, payoutsRelisez la ressource par GET avant toute autre action.
503core_unavailableToutesRejouez 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.

FinScénarioCe qui se passeCe que votre application doit faire
00SuccèsOrdre accepté, verdict SUCCESS après quelques secondesLivrer après lecture de succeeded
01Refus du payeurVerdict FAILEDAfficher l'échec, proposer de réessayer
02Solde insuffisantVerdict FAILED, même failure_code que 01Afficher l'échec sans en deviner la raison
03Payeur muetRien tant que le téléphone virtuel ne répond pas ; expire sinonAttendre, puis traiter expired
04Délai dépasséRépond au-delà de votre délai, mais crée le paiementRenvoyer avec la même Idempotency-Key
05Erreur 500 du fournisseurRien n'est créé chez l'opérateur ; issue inconnueSuivre par GET, ne jamais renvoyer un nouvel ordre
06Réponse illisibleCorps non JSON chez le fournisseur ; issue inconnueMême traitement que 05
07Verdict en doubleDeux notifications SUCCESS identiquesRien : une seule écriture côté Akewa
08Verdicts dans le désordreSUCCESS, puis PENDING en retardRien : reste succeeded
09Verdict contradictoireSUCCESS, puis FAILEDRien : reste succeeded, anomalie tracée
10Notification absenteAucune notification ; statut disponibleRien : conclu par vérification active
11Montant différentNotification avec 1 FCFA de plusRien : le statut confirmé fait foi
12Notification orphelineNotification d'une transaction inconnueRien : rejetée, aucun mouvement
13Statut ambiguAMBIGUOUS pendant un temps, puis SUCCESSRester en attente sans abandonner
14Titulaire inconnuLa 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

  • Authorization string requis

    Partner <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

  • Authorization string requis

    Partner <key_id>.<secret>. Votre clé sbx_ en bac à sable, live_ en production.

  • Content-Type string requis

    application/json

Corps de la requête

  • phone string requis

    Numé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

  • Authorization string requis

    Partner <key_id>.<secret>. Votre clé sbx_ en bac à sable, live_ en production.

  • Idempotency-Key string requis

    Un 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-Type string requis

    application/json

Corps de la requête

  • phone string requis

    Numéro du payeur. L'opérateur s'en déduit : vous ne le choisissez pas.

  • amount object requis

    Le montant, toujours un objet.

    Champs

    • amount integer requis

      Entier strictement positif, en francs CFA. Jamais de décimale.

    • currency string requis

      XAF

  • partner_reference string requis

    Votre référence de commande : 1 à 64 caractères parmi lettres, chiffres et _ . : -. Elle revient dans chaque réponse.

  • label string

    Libellé 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_id string (uuid) requis

    Le champ id de la réponse à la demande.

En-têtes

  • Authorization string requis

    Partner <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

  • Authorization string requis

    Partner <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

  • Authorization string requis

    Partner <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_id string (uuid) requis

    Identifiant de GET /partner/payout-beneficiaries.

  • amount integer requis

    Montant demandé, en francs CFA, au moins 1.

En-têtes

  • Authorization string requis

    Partner <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

  • Authorization string requis

    Partner <key_id>.<secret>. Votre clé sbx_ en bac à sable, live_ en production.

  • Idempotency-Key string requis

    Un 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-Type string requis

    application/json

Corps de la requête

  • beneficiary_id string (uuid) requis

    Identifiant d'un bénéficiaire enregistré. Vous ne transmettez jamais de numéro.

  • amount object requis

    Le montant, toujours un objet.

    Champs

    • amount integer requis

      Entier strictement positif, en francs CFA. Jamais de décimale.

    • currency string requis

      XAF

  • partner_reference string requis

    Votre référence de retrait : 1 à 64 caractères parmi lettres, chiffres et _ . : -.

  • requested_by string

    Qui, chez vous, a demandé ce retrait (254 caractères au plus). Journalisé avec le reversement.

  • expected_fee integer

    Les 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_fee integer

    Les 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_id string (uuid) requis

    Le champ id de la réponse au reversement.

En-têtes

  • Authorization string requis

    Partner <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