Freda Pay Issuing Freda Pay Issuing Documentation

Intégrez Freda Pay avec votre assistant IA

Ouvre Claude ou ChatGPT avec notre API déjà expliquée. Dites votre langage, il code avec vous.

Démarrage rapide

  1. Créez une cléTableau de bord → Clés API → Nouvelle clé test. La clé secrète sk_test_… ne s'affiche qu'une fois : copiez-la tout de suite.
  2. Créez un paiementPOST /v1/payments avec le montant, la méthode et votre référence.
  3. Redirigez le clientvers le checkout_url renvoyé. Il y paie avec MonCash ou Natcash.
  4. Confirmez côté serveurGET /v1/payments/:id jusqu'à succeeded, puis livrez. Ne livrez jamais sur la seule foi de la page de retour : un client peut la fermer avant la fin.

URL de base : https://api.fredapay.com

Spec OpenAPI Disponible sur /openapi.yaml. Importez-la dans Postman, Swagger UI, ou générez un client dans le langage de votre choix.

Authentification

En-tête Authorization: Bearer sk_test_… (ou sk_live_…) sur chaque requête. Les clés live s'activent après vérification du compte.

Sandbox et Live

Il y a une seule API pour tous les services. Le préfixe de la clé décide de l'environnement :

  • sk_test_… : Sandbox. Argent fictif, cartes de test. Aucun risque.
  • sk_live_… : Live. Vrais paiements, vraies cartes, vrai argent.

Les données des deux environnements sont complètement séparées : soldes, paiements, retraits, cartes, titulaires. Une carte ou un titulaire créé en Sandbox n'existe pas en Live. Vous pouvez revenir au Sandbox à tout moment depuis le tableau de bord.

Une clé sk_live_… peut être créée dès que la vérification KYC/KYB est approuvée : elle donne accès aux paiements (Gateway). L'émission de cartes en Live (/v1/cards) demande une autorisation séparée, à demander depuis le tableau de bord. Si elle est refusée, la même clé continue de fonctionner pour les paiements, et vous pouvez redemander l'accès plus tard. Le solde Live du Master Wallet est alimenté uniquement par de vrais dépôts.

Jamais côté client Aucune clé secrète dans le navigateur, une app mobile ou un dépôt public. Uniquement depuis votre serveur.
Besoin d'aide ?
En-têtes
Authorization: Bearer sk_test_VOTRE_CLE_SECRETE
Content-Type: application/json

Tester ma clé API et ma configuration

Avant d'écrire du code, vérifiez que Freda Pay accepte votre clé et que votre compte est bien configuré. L'outil appelle GET /v1/ping et POST /v1/webhooks/test, les mêmes appels que vous ferez depuis votre serveur.

Utilisez de préférence une clé test La clé est envoyée directement de votre navigateur à api.fredapay.com : elle n'est ni enregistrée ni transmise ailleurs, et disparaît quand vous fermez la page. Si vous testez une clé sk_live_…, supprimez-la ensuite du presse-papiers.

Que vérifie l'outil ?

  • La clé est reconnue (ni mal écrite, ni révoquée) et l'environnement : Sandbox ou Live.
  • Le compte est actif (non suspendu). Une clé Live demande aussi le Live activé après KYC/KYB.
  • L'émission de cartes est autorisée (en Live, par l'accès Issuing séparé).
  • Un endpoint webhook est enregistré. Le bouton « webhook de test » envoie un événement webhook.test signé et vous dit si votre serveur a répondu 2xx.

Si la vérification échoue

CodeSensQue faire
MISSING_API_KEYClé absente ou mal forméeElle doit ressembler à sk_test_… ou sk_live_…, sans espace.
INVALID_API_KEYClé inconnue ou révoquéeCréez une nouvelle clé dans Tableau de bord → Clés API.
ACCOUNT_SUSPENDEDCompte suspenduÉcrivez à issuing@fredapay.com.
LIVE_NOT_ENABLEDClé Live, Live pas encore activéTerminez la vérification KYC/KYB.
NO_WEBHOOK_ENDPOINTAucun webhook enregistréEnregistrez une URL avec POST /v1/webhooks (voir Notifications).
curl
curl https://api.fredapay.com/v1/ping \
  -H "Authorization: Bearer sk_test_VOTRE_CLE_SECRETE"

curl -X POST https://api.fredapay.com/v1/webhooks/test \
  -H "Authorization: Bearer sk_test_VOTRE_CLE_SECRETE"

Créer un paiement

POST/v1/payments

Crée une demande de paiement et renvoie l'adresse où votre client paiera.

Paramètres

ChampTypeDescription
amount requisnombreMontant. En HTG (MonCash, Natcash) : minimum 20. Par carte, en USD : minimum 0,50.
method requistextemoncash, natcash, moncash_ussd (le client reçoit une invite USSD sur son téléphone, sans page de paiement) ou all (le client choisit), ou carte : paiement par carte bancaire (Visa, Mastercard), toujours en USD. Voir Paiement par carte.
reference requistexteVotre identifiant unique, par exemple le numéro de commande. Si vous renvoyez une référence déjà utilisée, vous recevez le paiement existant au lieu d'en créer un second.
currency optionneltexteHTG par défaut pour MonCash et Natcash ; USD par défaut et obligatoire pour carte.
customer_name / customer_email optionneltextePour carte : le nom du client (et son email pour le reçu).
return_url optionneltextePour carte : adresse https où la page de paiement ramène le client après le paiement.
phone optionneltexteRequis seulement pour moncash_ussd. Format 509XXXXXXXX.

Réponse

Code 201 avec le paiement. Gardez son id pour vérifier le statut, et redirigez le client vers checkout_url (il vaut null pour moncash_ussd).

Paiement par carte (USD)

Avec method: "carte" et currency: "USD", la réponse contient un checkout_url : une page de paiement sécurisée Freda Pay où le client entre sa carte (Visa, Mastercard). Redirigez simplement votre client vers cette adresse, comme pour MonCash. Les numéros de carte ne passent jamais par votre serveur.

Ajoutez return_url (adresse https de votre site) : après le paiement, la page ramène le client chez vous. Le retour du navigateur ne prouve pas le paiement : confirmez toujours par le webhook payment.succeeded ou par GET /v1/payments/:id.

Un paiement par carte réussi crédite votre solde Gateway USD, frais déduits (fee, net_amount). Un paiement refusé ou expiré passe à failed et déclenche payment.failed. Montant minimum : 0,50 USD. En Live, le paiement par carte demande une activation : sans elle, l'API répond 403 CARD_NOT_AVAILABLE. Les plugins WooCommerce et Wix proposent déjà la carte.

Un lien de paiement unique accepte MonCash, Natcash et carte : le prix est dans la devise du lien, et le client est débité dans la devise du moyen choisi (USD pour la carte, HTG pour MonCash et Natcash), converti au taux fixe de la plateforme.

Intégrer cet appel avec
curl -X POST https://api.fredapay.com/v1/payments \
  -H "Authorization: Bearer sk_test_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 1500,
    "method": "moncash",
    "reference": "CMD-1001"
  }'
Réponse 201
{
  "id": "pay_8f2a91c4d0e1b7a3",
  "reference": "CMD-1001",
  "amount": 1500,
  "currency": "HTG",
  "method": "moncash",
  "status": "pending",
  "fee": null,
  "net_amount": null,
  "checkout_url": "https://…",
  "created_at": "2026-09-24T14:02:11Z"
}

Vérifier un paiement

GET/v1/payments/:id

Renvoie l'état actuel du paiement. À chaque appel, Freda Pay interroge le réseau MonCash ou Natcash pour obtenir le statut à jour.

Statuts possibles

StatutSignificationQue faire
pendingLe client n'a pas encore payé.Vérifiez à nouveau dans 3 à 5 secondes.
succeededPaiement confirmé. fee et net_amount sont remplis.Livrez la commande.
failedLe paiement n'a pas abouti.Proposez au client de réessayer avec une nouvelle référence.

Arrêtez de vérifier après environ 10 minutes sans confirmation : le client a probablement abandonné.

Intégrer cet appel avec
curl https://api.fredapay.com/v1/payments/pay_8f2a91c4d0e1b7a3 \
  -H "Authorization: Bearer sk_test_VOTRE_CLE"

Notifications (webhooks)

Plutôt que d'interroger GET /v1/payments/:id en boucle, enregistrez une URL sur votre serveur : Freda Pay lui envoie un POST signé dès qu'un événement se produit.

Le polling continue de fonctionner Les webhooks sont un complément, pas une obligation. Sans URL enregistrée, tout fonctionne exactement comme avant (voir Démarrage rapide).

Enregistrer votre URL

POST/dashboard/webhooks ou POST/v1/webhooks

{ "url": "https://votre-site.com/webhooks/freda" }, en https://. La réponse contient un secret (whsec_…) affiché une seule fois : copiez-le tout de suite.

GETRelit l'URL active (sans le secret).
POSTRemplace l'URL, génère un nouveau secret.
DELETEDésactive l'endpoint.
POST .../rotate-secretNouveau secret, même URL.

Événements disponibles

ÉvénementDéclenché quand…
payment.succeededUn paiement MonCash/Natcash/carte est confirmé.
payment.failedUn paiement par carte a été refusé ou a expiré (voir data.failure_reason).
payout.succeededUn retrait est arrivé chez le destinataire.
payout.failedUn retrait a échoué (voir data.failure_reason).
card.createdUne carte demandée est active (remplace le polling de création).
card.failedLa création d'une carte a échoué.
card.frozen / card.unfrozenUne carte a été gelée / dégelée.
card.terminatedUne carte a été supprimée définitivement.
card.fundedUne carte a été rechargée. Les données contiennent amount, fee, total_debited (montant + frais prélevés sur le Master Wallet) et le nouveau balance.
card.withdrawnDes fonds ont été retirés d'une carte vers le Master Wallet. Les données contiennent amount, fee, net_credited et le nouveau balance.
webhook.testÉvénement de test envoyé à votre demande (Tester ma clé API ou POST /v1/webhooks/test).
card.transactionUn mouvement a eu lieu sur une carte (achat, remboursement, refus). Les données contiennent card_id, type, amount, merchant_name, jamais le numéro de carte.

Vérifier la signature

Chaque livraison inclut X-Freda-Signature: sha256=…, un HMAC-SHA256 du corps brut signé avec votre secret. Recalculez-le et comparez en temps constant.

Intégrer avec
Exemple de livraison
POST /webhooks/freda HTTP/1.1
X-Freda-Signature: sha256=5f4d...b2a1
X-Freda-Event-Id: evt_8f2a91c4d0e1
X-Freda-Delivery-Attempt: 1
Content-Type: application/json

{
  "id": "evt_8f2a91c4d0e1",
  "event": "payment.succeeded",
  "created_at": "2026-09-27T10:15:00Z",
  "data": {
    "id": "pay_7c1a...",
    "reference": "CMD-001",
    "amount": 1000,
    "currency": "HTG",
    "net_amount": 930
  }
}
Vérification (Node.js)
const crypto = require("crypto");

function isValid(rawBody, signatureHeader, secret) {
  const expected = crypto.createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");
  const received = signatureHeader.replace("sha256=", "");
  return crypto.timingSafeEqual(
    Buffer.from(expected), Buffer.from(received)
  );
}
// Utilisez rawBody AVANT tout JSON.parse : un corps
// re-sérialisé ne redonne pas les mêmes octets.
Politique de nouvelle tentative Toute réponse hors 2xx, ou aucune réponse sous 8s : nouvelle tentative à +5s puis +30s. Abandon après 3 échecs. Répondez 200 vite, traitez l'événement après.
Gérez les doublons Une nouvelle tentative peut livrer le même événement deux fois. Utilisez data.id (ou id de l'événement) comme clé d'idempotence de votre côté.

Créer un retrait

POST/v1/payouts

Envoie de l'argent de votre solde Gateway vers un numéro MonCash ou Natcash. Utile pour retirer vos gains ou payer un fournisseur, un livreur ou un employé. Frais : 5 % du montant, en plus du montant envoyé. Le virement bancaire ou Zelle (frais fixes de 100 HTG, minimum 1 000 HTG) se demande depuis le tableau de bord, section Retraits, vers le compte que vous choisissez parmi ceux que vous avez enregistrés (la devise dépend du pays : HTG ou USD en Haïti, USD ailleurs ; Zelle est payé en USD) : il est traité à la main par notre équipe et n'est pas disponible par l'API.

Paramètres

ChampTypeDescription
amount requisnombreMontant en HTG.
method requistextemoncash ou natcash.
recipient requistexteNuméro MonCash ou Natcash du bénéficiaire (réseaux d'Haïti), format 509XXXXXXXX sans espaces ni +.
reference requistexteVotre identifiant unique pour ce retrait. Renvoyer la même référence renvoie le retrait existant, ce qui vous protège contre un double envoi.

Réponse

Code 202 : le retrait est accepté et passe en file d'attente avec le statut pending. Les retraits sont traités un par un, avec un intervalle de sécurité d'environ deux minutes entre chaque envoi. Suivez le résultat avec Vérifier un retrait.

Solde insuffisant Si votre solde Gateway ne couvre pas le montant, l'API répond 400 INSUFFICIENT_BALANCE et rien n'est envoyé. Les frais de retrait (5 % du montant pour MonCash et Natcash) sont ajoutés au montant débité : il faut donc montant + frais sur le solde. Cette somme est mise de côté dès l'acceptation de la demande ; si le retrait échoue, elle vous est rendue en entier.
Intégrer cet appel avec
curl -X POST https://api.fredapay.com/v1/payouts \
  -H "Authorization: Bearer sk_test_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 500,
    "method": "natcash",
    "recipient": "50937001234",
    "reference": "RET-2026-001"
  }'

Vérifier un retrait

GET/v1/payouts/:id

Renvoie l'état du retrait.

StatutSignification
pendingEn file d'attente ou en cours d'envoi.
successL'argent a été envoyé. fee contient les frais du réseau.
failedL'envoi a échoué et votre solde n'a pas été débité. failure_reason explique pourquoi, par exemple un numéro invalide.
Intégrer cet appel avec
Requête
curl https://api.fredapay.com/v1/payouts/po_3c9e71aa04b2d58f \
  -H "Authorization: Bearer sk_test_VOTRE_CLE"
Réponse 200
{
  "id": "po_3c9e71aa04b2d58f",
  "reference": "RET-2026-001",
  "amount": 500,
  "method": "natcash",
  "recipient": "50937001234",
  "status": "success",
  "fee": 12.5,
  "failure_reason": null
}

Consulter le solde

GET/v1/balance

Renvoie vos soldes :

  • gateway (HTG) : l'argent reçu par MonCash et Natcash, frais déduits. C'est ce solde que vous retirez avec les retraits.
  • gateway_usd (USD) : l'argent reçu par carte bancaire, frais déduits. Il se retire par virement bancaire ou Zelle depuis le tableau de bord. gateway_balances regroupe les deux : { "HTG": ..., "USD": ... }.
  • master_wallet (USD) : les fonds que vous avez déposés pour émettre des cartes et payer votre abonnement.
Intégrer cet appel avec
Requête et réponse
curl https://api.fredapay.com/v1/balance \
  -H "Authorization: Bearer sk_test_VOTRE_CLE"

{
  "gateway": { "available": 12400, "currency": "HTG" },
  "gateway_usd": { "available": 182.3, "currency": "USD" },
  "master_wallet": { "available": 95.96, "currency": "USD" }
}

Émettre des cartes virtuelles

Créez des cartes Visa ou Mastercard virtuelles, préfinancées depuis votre Master Wallet (USD), pour un usage individuel ou une carte d'entreprise.

Le préfinancement vient du Master Wallet, pas du solde Gateway Déposez des USD sur votre Master Wallet (tableau de bord → Master Wallet) avant de créer une carte : MonCash/Natcash s'y créditent automatiquement, ou Zelle/virement bancaire (les moyens que nous conseillons) après vérification. Les frais de financement de votre plan (4,5 % en Startup, 3 % en Pro, 2,5 % en Premium) sont retenus sur le montant reçu.
Deux façons d'appeler les endpoints Cartes Chaque endpoint existe en deux versions : /dashboard/cards/… (session, pour le tableau de bord) et /v1/cards/… (clé API sk_test_…/sk_live_…, pour votre backend). Même corps de requête, même réponse. Utilisez /v1/cards pour intégrer.

Titulaires de carte

POST/dashboard/cards/holders ou POST/v1/cards/holders

Un titulaire est requis pour chaque carte individuelle (pas pour une carte entreprise). Vous pouvez en créer plusieurs, par exemple un par employé, puis choisir lequel reçoit la carte avec holder_id.

GET /cards/holdersListe de vos titulaires de l'environnement de la clé (du plus récent au plus ancien).
GET /cards/holders/:idUn titulaire (404 NOT_FOUND s'il n'appartient pas à votre compte).
POST /cards/holdersCrée un titulaire (champs ci-dessous).

L'ancien POST /cards/holder-profile fonctionne toujours et crée simplement un titulaire de plus.

Identifiant, statuts et règles

  • Quel identifiant envoyer ? Le champ id du titulaire (forme mpc_…), tel que renvoyé par POST /cards/holders, GET /cards/holders et GET /cards/holders/:id. Envoyez-le dans holder_id de POST /cards. Toujours explicitement en production.
  • Statuts possibles : pending (inscription en cours), enrolled (prêt) et failed (échec, voir failure_reason). Seul un titulaire enrolled peut recevoir une carte. La photo (photo_url) est facultative : elle n'est jamais exigée.
  • Délai : la création est synchrone. Quand POST /cards/holders répond 201, le titulaire est déjà enrolled et utilisable tout de suite : aucune attente ni nouvelle tentative n'est nécessaire. Si la réponse est une erreur, le titulaire est enregistré en failed : corrigez la cause puis recréez-le.
  • Doublons : si un titulaire enrolled existe déjà avec le même e-mail dans le même environnement, POST /cards/holders ne crée rien de plus : il répond 200 avec "already_existed": true et l'id existant.
  • Sandbox et Live sont séparés : le mode vient du préfixe de la clé (sk_test_ ou sk_live_). Un titulaire créé avec une clé de test n'existe pas en Live, et inversement. Utiliser un titulaire de l'autre mode renvoie 409 HOLDER_MODE_MISMATCH. Le titulaire est rattaché à votre compte marchand, pas à une clé précise : deux clés du même mode voient les mêmes titulaires.
  • Sans holder_id : l'API prend le titulaire enrolled le plus récent de votre compte, dans l'environnement de la clé. Si plusieurs utilisateurs créent des titulaires en même temps avec la même clé, le « dernier » peut être celui d'un autre utilisateur : ne vous fiez pas à ce défaut, envoyez toujours holder_id et gardez l'id obtenu à la création.
ChampDescription
firstName, lastNameNom du titulaire.
emailAdresse email.
countryCode pays à 2 lettres (ISO 3166), ex. US, FR, NG.
dobDate de naissance, YYYY-MM-DD.
identificationNumberNuméro de pièce d'identité.
identityPièce d'identité officielle, requise pour émettre une carte : objet avec type (NIN, PASSPORT, VOTERS_CARD ou DRIVERS_LICENSE), number, imageUrl (image en Base64 : data:image/jpeg;base64,..., PNG, JPEG ou WebP, environ 700 Ko au maximum ; un lien https direct vers l'image est aussi accepté, nous la téléchargeons et la convertissons) et country (2 lettres, par défaut le pays du titulaire).
phoneShortCode, phoneNumberIndicatif et numéro, ex. +1 et 2025550123.
addressObjet avec street, city, state, postalCode et country : les 5 sont requis.
photoUrl optionnelPhoto ou logo pour votre tableau de bord (data URL png/jpeg/webp de moins de 200 Ko, ou URL https).
Intégrer avec
Requête
curl -X POST https://api.fredapay.com/v1/cards/holders \
  -H "Authorization: Bearer sk_test_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "Rose",
    "lastName": "Lakay",
    "email": "rose@example.com",
    "country": "US",
    "dob": "1990-05-12",
    "identificationNumber": "ID123456",
    "identity": { "type": "PASSPORT", "number": "P1234567", "imageUrl": "data:image/jpeg;base64,/9j/4AAQ...", "country": "US" },
    "phoneShortCode": "+1",
    "phoneNumber": "37001234",
    "address": { "street": "350 Fifth Avenue", "city": "New York", "state": "NY", "postalCode": "HT6110", "country": "US" }
  }'
Réponse 201
{
  "id": "mpc_1a2b3c4d5e6f7a8b",
  "already_existed": false,
  "holder": {
    "id": "mpc_1a2b3c4d5e6f7a8b",
    "mode": "sandbox",
    "status": "enrolled",
    "first_name": "Rose",
    "last_name": "Lakay",
    "email": "rose@example.com",
    "country": "US",
    "photo_url": null,
    "dob": "1990-05-12",
    "phone": "+137001234",
    "address": { "street": "350 Fifth Avenue", "city": "New York", "state": "NY", "postalCode": "HT6110", "country": "US" },
    "identification_number_last4": "3456",
    "failure_reason": null,
    "created_at": "2026-10-11T10:00:00.000Z"
  },
  "profile": { /* même objet que holder (ancien nom, conservé) */ }
}
GET /v1/cards/holders
{ "holders": [
    { "id": "mpc_1a2b3c4d5e6f7a8b", "mode": "sandbox", "status": "enrolled",
      "first_name": "Rose", "last_name": "Lakay", "email": "rose@example.com",
      "country": "US", "photo_url": null, "failure_reason": null,
      "created_at": "2026-10-11T10:00:00.000Z" }
] }
// GET /v1/cards/holders/mpc_1a2b… renvoie { "holder": { …même objet… } }
Erreurs de POST /cards/holders
400 MISSING_FIELDS     champ ou adresse incomplète (le message liste les champs)
400 INVALID_PHOTO      photoUrl invalide ou trop lourde
400 INVALID_JSON       corps non JSON
401 MISSING_API_KEY / INVALID_API_KEY
403 LIVE_NOT_ENABLED   clé live avant vérification du compte
502 HOLDER_CREATION_FAILED  le service de cartes n'a pas créé le titulaire
502 CARD_SERVICE_ERROR / 503 CARD_PROVIDER_AUTH  service de cartes en erreur
(un titulaire en échec est conservé avec status "failed" + failure_reason)

Créer une carte

POST/dashboard/cards ou POST/v1/cards

La création est asynchrone : réponse 202, statut pending. Quelques instants plus tard, le statut passe à active ou failed. Interrogez Gérer une carte, ou recevez un webhook.

Conservez le champ id de la réponse C'est le seul identifiant dont vous avez besoin. Le même id (ex. card_8f2a91c4d0e1) sert pour tous les autres appels : voir la carte, son historique, l'alimenter, la geler. Enregistrez-le dès la création. Aucun autre identifiant n'est nécessaire.

Paramètres

ChampTypeDescription
reference requistexteVotre identifiant unique. Rejoué, renvoie la carte existante.
kind optionneltexteindividual (défaut) ou business.
brand optionneltexteVISA ou MASTERCARD.
amount requisnombreMontant à préfinancer, en dollars (pas en centimes). Débité du Master Wallet immédiatement.
holder_id optionnelCarte individuelle : id du titulaire (mpc_…, voir Titulaires). Recommandé : toujours l'envoyer. Sans lui, l'API prend le titulaire enrolled le plus récent de votre compte dans l'environnement de la clé.
business_nametexteRequis si kind = business. Pas de profil titulaire nécessaire dans ce cas.
Solde insuffisant ou échec de création 400 BELOW_MINIMUM_BALANCE si le Master Wallet ne couvre pas le montant (préfinancement + frais de création). Si la création échoue après le débit, le montant est automatiquement recrédité.

Erreurs de POST /cards

HTTPCodeCause et solution
400MISSING_FIELDSreference manquante (ou business_name pour une carte entreprise).
400INVALID_BRANDbrand doit être VISA ou MASTERCARD.
400HOLDER_PROFILE_REQUIREDCarte individuelle, holder_id absent, et aucun titulaire n'existe dans l'environnement de la clé. Créez-en un (POST /cards/holders) avec une clé du même mode, puis envoyez son id.
409HOLDER_NOT_ENROLLEDLe titulaire (indiqué par holder_id, ou aucun titulaire prêt sans holder_id) n'a pas le statut enrolled. Le message donne le statut et la raison d'échec ; recréez le titulaire.
400HOLDER_PROFILE_REQUIREDCarte individuelle sans holder_id alors qu'aucun titulaire n'existe dans l'environnement de la clé. Voir Titulaires.
409HOLDER_NOT_ENROLLEDLe titulaire n'est pas au statut enrolled (pending ou failed).
409HOLDER_MODE_MISMATCHLe titulaire appartient à l'autre environnement (Sandbox/Live).
409HOLDER_PROFILE_INCOMPLETEAncien titulaire sans lien avec le service de cartes et non récupérable : recréez-le.
404NOT_FOUNDholder_id inconnu pour votre compte.
400CARD_LIMIT_REACHEDLimite de cartes du plan Issuing atteinte (par environnement).
400BELOW_MINIMUM_BALANCEMaster Wallet insuffisant.
403ISSUING_LIVE_NOT_ENABLEDClé live avant l'autorisation Issuing Live.
401 / 403MISSING_API_KEY, INVALID_API_KEY, LIVE_NOT_ENABLEDVoir Erreurs.
502 / 503CARD_SERVICE_ERROR, CARD_PROVIDER_AUTH, CARD_LIVE_NOT_CONFIGUREDService de cartes indisponible ; rien n'est débité (ou remboursé aussitôt). Réessayez plus tard.

Spécification complète et publique : https://api.fredapay.com/openapi.yaml.

Intégrer avec
curl -X POST https://api.fredapay.com/dashboard/cards \
  -H "Authorization: Bearer VOTRE_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "reference": "CARD-001",
    "brand": "VISA",
    "amount": 20
  }'
Réponse 202
{
  "id": "card_8f2a91c4d0e1",
  "reference": "CARD-001",
  "kind": "individual",
  "brand": "VISA",
  "status": "pending",
  "created_at": "2026-09-25T04:15:04Z"
}

Gérer une carte

Chaque action existe sous /dashboard/cards/… (session) et /v1/cards/… (clé API). Colonne ci-dessous : le chemin après ce préfixe.

ActionEndpointEffet
Voir une carteGET /cards/:idStatut, masked_pan, solde.
Lister les cartesGET /cardsToutes vos cartes.
Historique des transactionsGET /cards/:id/transactionsListe des achats/crédits + month_spend (dépensé ce mois-ci).
Ajouter des fondsPOST /cards/:id/fund{ "amount": 10, "reference": "FUND-001" }. Débite le Master Wallet (montant + frais de recharge). La réponse ajoute fee et total_debited. reference (ou l'en-tête Idempotency-Key) est facultative mais recommandée : rejouer la même clé après un timeout ne débite pas deux fois et renvoie idempotent_replay: true ; la même clé avec un autre montant renvoie 409 IDEMPOTENCY_MISMATCH.
Retirer des fondsPOST /cards/:id/withdraw{ "amount": 5, "reference": "WD-001" }. Recrédite le Master Wallet (montant moins les frais de retrait). La réponse ajoute fee et net_credited. Même règle d'idempotence que /fund. Solde insuffisant : 400 INSUFFICIENT_CARD_BALANCE.
GelerPOST /cards/:id/freezeBloque tout achat et tout ajout/retrait de fonds.
DégelerPOST /cards/:id/unfreezeRéactive la carte.
Fermer définitivementPOST /cards/:id/terminateIrréversible. La carte est fermée chez notre partenaire, son solde restant est reversé au Master Wallet, puis elle disparaît de vos listes.
Simuler une transaction SANDBOXPOST /cards/:id/simulate-transaction{ "amount": 12.5, "type": "DEBIT" } (ou CREDIT). Pour tester l'historique. Refusé en mode Live.
Voir le numéro completPOST /cards/:id/revealRéponse : { number, cvv, expiry_month, expiry_year, holder_name }. Dans le tableau de bord, seules les cartes business ont un bouton Détails : les détails d'une carte individuelle ne sont jamais visibles par vos équipes. Par l'API, cette route sert uniquement à afficher la carte à son titulaire ; ne stockez ni ne journalisez jamais le numéro ni le CVV.
Numéro complet et CVV : à usage strictement interne Cet endpoint donne accès aux données sensibles de la carte, réservées au titulaire du compte pour ses propres achats. Ne les affichez jamais à un tiers (client, employé sans autorisation) ; n'enregistrez jamais le résultat dans vos logs ou votre base de données.
Statuts possibles pending (création en cours) → active ou failed. Une carte active peut passer à disabled (gelée) puis revenir à active, ou à terminated (définitif).
Intégrer avec
Carte gelée 409 CARD_FROZEN sur /fund ou /withdraw si la carte est disabled. Dégelez-la d'abord.

Où la carte fonctionne

Les cartes sont des cartes virtuelles en USD. Elles sont acceptées chez les commerçants qui acceptent les cartes USD, surtout des commerçants américains (par exemple Meta, PayPal, Google, Canva). Certaines catégories de commerçants et certains pays sont refusés.

Catégories de commerçants refusées

Un commerçant est identifié par un code de catégorie (MCC). Les cartes sont refusées pour ceux-ci :

Code MCCCatégorie
4829Transferts d'argent
5962Services d'organisation de voyages
5966Télémarketing sortant
6051Institutions non financières et cryptomonnaies
6211Courtiers et négociants en valeurs mobilières
7273Services de rencontres et d'escorte
7297Salons de massage
7800Loteries d'État
7801Casinos en ligne agréés par un État
7802Courses de chevaux et de chiens agréées
7995Paris et jeux d'argent en ligne

Pays où la carte ne peut pas être utilisée

Des sanctions géographiques s'appliquent à ces pays et territoires :

Afghanistan, Algérie, Biélorussie, Burundi, République centrafricaine, Comores, Congo (République démocratique du), Congo (République du), Corée du Nord, Cuba, Gambie, Iran, Irak, Kirghizistan, Liban, Libéria, Libye, Maldives, Myanmar, Nicaragua, Palestine, Russie, Serbie, Somalie, Soudan, Soudan du Sud, Suriname, Svalbard et Jan Mayen, Syrie, Tadjikistan, Togo, Tokelau, Turkménistan, Ukraine, Ouzbékistan, Venezuela, Wallis-et-Futuna, Yémen, Zimbabwe.

Types de transaction

Le champ type de GET /cards/:id/transactions prend l'une de ces valeurs. Le champ mode vaut CREDIT ou DEBIT.

TypeSignification
AUTHORIZATIONLa carte est utilisée avec succès chez un commerçant.
SETTLEMENTLe règlement d'un achat est terminé.
FUNDINGLa carte est alimentée via /fund.
WITHDRAWALDes fonds sont retirés via /withdraw.
TERMINATIONLa carte est fermée.
DECLINEUne tentative d'utilisation est refusée (solde insuffisant, catégorie ou pays non autorisé, etc.).
REVERSALUn commerçant débite la carte puis rend aussitôt la somme.
REFUNDRemboursement d'un montant débité par erreur.
CROSS-BORDERAchat chez un commerçant hors des États-Unis, ou dans une devise autre que l'USD.

Webhooks de carte

La confirmation de création de carte arrive de façon asynchrone. Deux options, au choix :

Option recommandée : les webhooks sortants Enregistrez une URL (voir Notifications) et recevez card.created / card.failed automatiquement.

Sans URL enregistrée, interrogez simplement GET /dashboard/cards/:id jusqu'à ce que status quitte pending :

Exemple d'attente
async function waitForCard(id) {
  for (let i = 0; i < 40; i++) {
    const res = await fetch(`https://api.fredapay.com/dashboard/cards/${id}`, {
      headers: { Authorization: `Bearer ${sessionToken}` },
    });
    const card = await res.json();
    if (card.status !== "pending") return card;
    await new Promise(r => setTimeout(r, 3000));
  }
}

Comptez généralement quelques secondes entre la création et la confirmation.

Liens de paiement, sans une ligne de code

Pas de site ou pas de développeur ? Créez un lien depuis le tableau de bord et envoyez-le par WhatsApp, SMS, e-mail ou sur vos réseaux sociaux.

  1. Ouvrez « Liens de paiement »dans votre tableau de bord.
  2. Indiquez un montant, ou laissez videSans montant, votre client saisit lui-même la somme : pratique pour les dons ou les factures variables.
  3. Partagez le lienVotre client choisit MonCash ou Natcash et paie. L'argent arrive sur votre solde Gateway.

Bouton de paiement pour n'importe quel site

Collez deux lignes dans votre site pour afficher un bouton « Payer avec Freda Pay ». Il fonctionne sur Wix, Squarespace, Webflow, Shopify, Blogger ou une simple page HTML.

Le bouton utilise un lien de paiement : il ne contient aucune clé secrète et peut donc être placé sans risque dans une page publique.

  1. Créez un lien de paiement et copiez son identifiant (lnk_…).
  2. Ajoutez un bloc « HTML personnalisé » ou « Embed » dans votre éditeur de site.
  3. Collez le code ci-contre en remplaçant lnk_VOTRE_LIEN.
Code à coller
<div data-freda-link="lnk_VOTRE_LIEN"></div>
<script src="https://pay.fredapay.com/freda-button.js" async></script>

Options : data-label="Faire un don" change le texte du bouton, data-theme="light" affiche une version claire.

Plugins et intégrations

Choisissez l'outil avec lequel votre site est construit.

WordPress / WooCommerce

MonCash et Natcash à la page de paiement. Commandes confirmées automatiquement. Instructions dans le fichier du plugin.

Dans votre tableau de bord

Wix

Bouton sans code, ou intégration Velo pour un montant par commande. Voir LISEZMOI.md dans le kit.

Dans votre tableau de bord

Squarespace, Webflow, Blogger

Collez le bouton de paiement dans un bloc HTML/Embed.

Voir le bouton

Shopify

Page de paiement réservée aux prestataires agréés Shopify. Utilisez le bouton sur vos pages produit en attendant.

Voir le bouton

SDKs officiels

Client typé pour votre langage : mêmes endpoints que ci-dessus, sans écrire les appels HTTP vous-même.

Node.js

npm install @fredapay/issuing. Node 18+, zéro dépendance. Inclut la vérification de signature webhook.

Dans votre tableau de bord

PHP

composer require freda-pay/issuing. PHP 7.4+, extensions json et curl. Inclut la vérification de signature webhook.

Dans votre tableau de bord

Pas encore sur npm/Packagist : téléchargez le kit depuis la section Plugins et SDK de votre tableau de bord et lisez son README pour l'ajouter directement à votre projet, ou publiez-le vous-même sous votre propre nom de paquet.

Pagination

Tous les endpoints de liste (GET /v1/payments, /v1/payouts, /v1/cards, /dashboard/payment-links, etc.) acceptent deux paramètres de requête :

ParamètreDéfautDescription
page1Numéro de page, à partir de 1.
page_size20Résultats par page. Maximum 100.
Requête
curl "https://api.fredapay.com/v1/cards?page=2&page_size=50" \
  -H "Authorization: Bearer sk_test_VOTRE_CLE"

Avec le SDK Node.js : freda.payments.list({ page: 1, page_size: 20 }), freda.payouts.list() et freda.cards.list(). Chaque réponse inclut un objet pagination à côté des résultats, ainsi que le champ mode (sandbox ou live) pour les paiements, retraits et cartes :

Réponse
{
  "cards": [ /* … */ ],
  "pagination": { "page": 2, "page_size": 50, "has_more": false }
}

Pas de total. Utilisez has_more pour savoir s'il faut charger la page suivante.

Limites de requêtes

Chaque clé API (sk_test_… / sk_live_…) est limitée à 100 requêtes par minute sur les endpoints /v1/…. Trois en-têtes accompagnent chaque réponse :

En-têteSignification
X-RateLimit-LimitLimite par fenêtre d'une minute (100).
X-RateLimit-RemainingRequêtes restantes dans la fenêtre en cours.
Retry-AfterPrésent uniquement sur une réponse 429 : secondes à attendre.
429 RATE_LIMITED Au-delà de la limite, chaque requête reçoit 429 jusqu'à la fin de la fenêtre. Espacez vos appels ou regroupez-les ; il n'y a pas de palier supérieur disponible pour l'instant.

Les endpoints /dashboard/… (session du tableau de bord) ne sont pas soumis à cette limite spécifique, mais restent protégés contre les abus évidents (ex. tentatives de connexion).

Versions de l'API

Tous les endpoints publics vivent sous /v1/…. Politique :

  • Sans changement de version : nouveau champ optionnel, nouvel endpoint, nouveau paramètre optionnel. Ignorez les champs inconnus.
  • Avec nouvelle version (/v2/…) : champ renommé/supprimé, type changé, paramètre rendu obligatoire. /v1 reste actif en parallèle.
  • Préavis de dépréciation : au moins 90 jours annoncés par email à l'adresse du compte avant qu'une version ne soit retirée.

/v1 est la version actuelle et n'a pas de date de retrait prévue.

Erreurs

En cas de problème, l'API répond avec un code HTTP d'erreur et un objet au même format :

{ "error": { "code": "INVALID_AMOUNT", "message": "Le montant minimum est de 20 HTG." } }

Utilisez code dans votre programme, et message pour l'afficher ou le journaliser.

HTTPCodeCause et solution
400MISSING_FIELDSUn champ requis manque. Vérifiez le tableau des paramètres.
400INVALID_AMOUNTMontant absent, négatif ou inférieur à 20 HTG. Envoyez un nombre, pas du texte.
400INVALID_METHODMéthode inconnue. Paiements (POST /v1/payments) : moncash, moncash_ussd, natcash, carte ou all. Retraits (POST /v1/payouts) : moncash ou natcash uniquement.
400INVALID_RECIPIENTNuméro mal formé. Format attendu : 509XXXXXXXX.
400INSUFFICIENT_BALANCESolde Gateway trop faible pour ce retrait.
400INVALID_JSONLe corps n'est pas du JSON valide. Ajoutez Content-Type: application/json.
401MISSING_API_KEYEn-tête Authorization absent ou mal écrit. Il doit commencer par Bearer sk_.
401INVALID_API_KEYClé révoquée ou inexistante. Créez-en une nouvelle dans le tableau de bord.
403LIVE_NOT_ENABLEDClé live utilisée avant la vérification du compte. Terminez votre KYC.
403ISSUING_LIVE_NOT_ENABLEDClé live utilisée sur /v1/cards avant l'autorisation Issuing Live. Les paiements restent disponibles ; faites la demande depuis le tableau de bord.
400BELOW_MINIMUM_BALANCELe Master Wallet de l'environnement actuel est insuffisant. En Live, seul un vrai dépôt crédite le solde.
409HOLDER_MODE_MISMATCHLe titulaire a été créé dans l'autre environnement (Sandbox/Live). Créez-le dans l'environnement de la clé utilisée.
503CARD_PROVIDER_AUTHLe service d'émission de cartes est momentanément indisponible côté plateforme (ce n'est pas une erreur de votre requête). Le montant éventuellement débité est remboursé aussitôt. Réessayez plus tard.
503CARD_LIVE_NOT_CONFIGUREDL'émission de cartes Live n'est pas encore disponible sur la plateforme. Aucun montant n'est débité.
404NOT_FOUNDIdentifiant de paiement ou de retrait introuvable pour ce compte.
502PROVIDER_ERRORLe réseau MonCash ou Natcash n'a pas répondu. Réessayez dans quelques instants.

Bonnes pratiques

  • Une référence unique par tentative. Utilisez par exemple le numéro de commande suivi d'un compteur (CMD-1001-1, CMD-1001-2). Réutiliser une référence renvoie l'opération existante.
  • Confirmez côté serveur. Ne livrez qu'après status: "succeeded" obtenu par votre serveur.
  • Gardez la clé secrète dans une variable d'environnement, jamais dans le code source.
  • Enregistrez l'id Freda Pay avec votre commande pour pouvoir la vérifier plus tard.
  • Prévoyez les échecs. Affichez le message d'erreur et proposez de réessayer.

Une question ? Écrivez-nous à issuing@fredapay.com.