Actuelle

Référence API · v2

Documentation de l'API

Le contrat REST FasterMessage : envoyez des SMS, des messages WhatsApp, des e-mails et des appels vocaux, suivez chaque remise en temps réel et pilotez votre solde depuis une seule interface.

Démarrage rapide

L'API FasterMessage envoie des messages sur quatre canaux — SMS, WhatsApp, e-mail et voix — depuis un contrat REST unique. Trois étapes séparent un compte créé d'un premier message remis.

1. Récupérer ses identifiants

Depuis le backoffice, ouvrez Intégration & API. Vous y trouvez votre identifiant de compte, et vous y générez deux clés : une clé test et une clé live. La clé n'est affichée qu'une fois — elle est stockée hachée et ne peut pas être relue.

2. Déclarer un expéditeur

Le SMS exige un Sender ID approuvé : il n'existe aucun expéditeur par défaut de compte, et un envoi sans from est refusé avant même d'être tarifé. La déclaration se fait dans le backoffice, onglet Sender IDs.

3. Envoyer

Premier envoi
curl -X POST https://api-next.fastermessage.com/v2/sms/send \
  -u "$FM_API_USER:$FM_API_SECRET" \
  -H "X-Api-Key: $FM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "to": "+22960000000",
  "from": "FASTERMSG",
  "content": "Votre code est 480912. Valable 5 min."
}'
Réponse — 202 Accepted
{
  "id": "msg_2f1c9d4e8a6b",
  "channel": "sms",
  "status": "queued",
  "to": "22960000000",
  "from": "FASTERMSG",
  "units": 1,
  "price": null,
  "currency": "XOF",
  "refund": null,
  "client_reference": null,
  "fallback_of": null,
  "error": null,
  "created_at": "2026-09-07T10:00:00.000Z",
  "scheduled_at": null,
  "sent_at": null,
  "delivered_at": null
}

Authentification

Chaque appel porte deux identifiants, tous deux obligatoires : une authentification Basic et une clé d'API. Ce n'est pas une redondance — les deux ne désignent pas la même chose.

IdentifiantTransmis parDésigne
BasicAuthorizationLe compte à défalquer : c'est lui qui paie l'envoi.
Clé d’APIX-Api-KeyLe compte à l'origine de la requête — lui-même, ou l'un de ses sous-comptes.

Le rapprochement des deux est vérifié à chaque appel : une clé qui n'appartient ni au compte authentifié ni à l'un de ses sous-comptes est refusée. C'est ce couple qui permet à un revendeur d'émettre pour le compte de ses clients sans leur confier son mot de passe.

En-têtes
Authorization: Basic bW9uLWNvbXB0ZTpzZWNyZXQ=
X-Api-Key: fm_live_xxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json

Conservez les deux côté serveur. Une clé exposée dans un client web ou mobile est une clé publiée : révoquez-la depuis le backoffice et générez-en une nouvelle.

Environnements

EnvironnementPréfixe de cléComportement
testfm_test_…Les appels traversent exactement les mêmes contrôles qu’en production et rendent les mêmes codes d’erreur, mais rien n’est remis à l’opérateur, rien n’est débité, et aucune URL de rappel n’est appelée.
productionfm_live_…Envois réels, débités du solde, remis au transporteur, DLR authentiques.

Ce qui change en test

  • Aucune remise opérateur : le destinataire ne reçoit rien, quelle que soit la validité de son numéro.
  • Aucun débit — et le solde n'est même pas exigé : vous pouvez écrire toute votre intégration avant le moindre achat.
  • Les Sender IDs de votre marque sont empruntables même sans approbation nominative — ce qui n'est jamais le cas en production.
  • Un envoi immédiat est marqué delivered aussitôt ; un envoi programmé reste pending à sa date. Aucun webhook n'est appelé : joindre votre URL de rappel serait déjà une livraison.
  • Les deux environnements sont cloisonnés : une clé de test ne relit que ses propres envois simulés (GET /v2/messages), jamais votre trafic réel — et inversement. Les simulations sont conservées 30 jours, puis effacées.
  • Vérification par code (OTP) : le code est toujours une suite de zéros (000000, ou 0000 selon la longueur demandée). Aucun SMS ne partant, c'est ce qui permet d'éprouver POST /v2/otp/verify de bout en bout.

Essayer l’API

Composez un appel avec vos identifiants de test et exécutez-le depuis cette page. Le script équivalent se régénère en direct dans les quatre langages.

Environnement de test uniquement — n’utilisez jamais vos identifiants de production sur cette page. Les valeurs saisies restent dans votre navigateur : elles ne sont ni enregistrées, ni transmises à un tiers.

Identifiants de test

Le compte à défalquer — moitié « Basic » de l’authentification.

Seconde moitié du « Basic ». Jamais affiché ni recopié dans les exemples.

En-tête X-Api-Key. Le préfixe fm_test_ garantit qu’aucun envoi réel n’est déclenché.

Message

11 caractères au maximum. Obligatoire — aucun expéditeur par défaut.

Format E.164 : indicatif pays puis numéro, 8 à 15 chiffres.

37 caractères1 segment facturéGSM-7

curl -X POST https://api-next.fastermessage.com/v2/sms/send \
  -u "$FM_API_USER:$FM_API_SECRET" \
  -H "X-Api-Key: $FM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "to": "",
  "from": "FASTERMSG",
  "content": "Votre code est 480912. Valable 5 min."
}'

Renseignez les champs requis pour lancer l’appel.

Envoi simple

Méthode HTTP POSThttps://api-next.fastermessage.com/v2/sms/send

Envoie un SMS à un destinataire. Renvoie 202 Accepted et la ressource message : la remise, elle, est asynchrone et se suit par webhook ou par GET /v2/messages/{id}.

Corps de la requête

ChampTypeDescription
torequisstringDestinataire au format E.164 (8 à 15 chiffres, + facultatif).
fromrequisstringSender ID approuvé sur le compte. Aucun expéditeur par défaut n'est appliqué ; un compte en mode test peut employer le Sender ID de sa marque.
contentrequisstringTexte du message. Au-delà de 160 caractères GSM (70 en UCS-2), il est découpé en segments facturés à l’unité.
scheduled_atoptionnelstringDate d’envoi en ISO 8601 UTC. Une date illisible est refusée, jamais ignorée.
callback_urloptionnelstringURL de rappel propre à ce message — prime sur l’URL configurée sur le compte.
client_referenceoptionnelstringRéférence libre, reprise telle quelle dans la réponse et dans les webhooks.
fallbackoptionnelarrayCascade ordonnée de canaux de repli, chaque étape n’étant tentée que si la précédente n’a pas abouti.

En-têtes facultatifs

ChampTypeDescription
Idempotency-KeyoptionnelstringClé unique de votre choix. Un rejeu avec la même clé renvoie la réponse mémorisée au lieu de renvoyer le message. Conservée 24 h.
Requête
curl -X POST https://api-next.fastermessage.com/v2/sms/send \
  -u "$FM_API_USER:$FM_API_SECRET" \
  -H "X-Api-Key: $FM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "to": "+22960000000",
  "from": "FASTERMSG",
  "content": "Votre commande cmd-42 est prête.",
  "client_reference": "cmd-42",
  "callback_url": "https://exemple.test/webhooks/fastermessage"
}'
Succès — 202 Accepted
{
  "id": "msg_2f1c9d4e8a6b",
  "channel": "sms",
  "status": "queued",
  "to": "22960000000",
  "from": "FASTERMSG",
  "units": 1,
  "price": null,
  "currency": "XOF",
  "refund": null,
  "client_reference": "cmd-42",
  "created_at": "2026-09-07T10:00:00.000Z"
}
Erreur — 402 Payment Required
{
  "error": {
    "type": "business_error",
    "code": "insufficient_balance",
    "message": "Insufficient balance to process the request.",
    "request_id": "2f1c9d4e-8a6b-4c1e-9f2a-7d3b5e0c1a84"
  }
}

Envoi en masse

Méthode HTTP POSThttps://api-next.fastermessage.com/v2/sms/batch

Jusqu'à 500 SMS par appel, en une seule requête — et une seule unité au sens des plafonds de débit. Les valeurs posées au niveau du lot (from, content, scheduled_at, callback_url) sont héritées par toute ligne qui n'en porte pas : la même entrée sert la diffusion (un texte, N numéros) et l'envoi transactionnel groupé (un texte par ligne).

Corps de la requête

ChampTypeDescription
messagesrequisarrayLignes du lot, 500 au maximum. Chaque ligne accepte les champs de l’envoi simple.
fromoptionnelstringExpéditeur du lot, hérité par les lignes qui n’en déclarent pas. Obligatoire — au lot ou sur chaque ligne.
contentoptionnelstringTexte du lot, hérité de la même façon.
scheduled_atoptionnelstringDate d’envoi commune, en ISO 8601 UTC.
callback_urloptionnelstringURL de rappel commune à toutes les lignes.
Requête
curl -X POST https://api-next.fastermessage.com/v2/sms/batch \
  -u "$FM_API_USER:$FM_API_SECRET" \
  -H "X-Api-Key: $FM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "from": "FASTERMSG",
  "content": "Votre livraison arrive demain entre 9h et 12h.",
  "messages": [
    {
      "to": "+22960000000",
      "client_reference": "cmd-42"
    },
    {
      "to": "+22961000001",
      "client_reference": "cmd-43"
    },
    {
      "to": "+22962000002",
      "content": "Votre livraison arrive aujourd’hui.",
      "client_reference": "cmd-44"
    }
  ]
}'
Succès — 200 OK (bilan)
{
  "total": 3,
  "accepted": 2,
  "rejected": 1,
  "data": [
    { "index": 0, "id": "msg_2f1c9d4e8a6b", "status": "queued", "to": "22960000000" },
    { "index": 1, "id": "msg_7d3b5e0c1a84", "status": "queued", "to": "22961000001" }
  ],
  "errors": [
    {
      "index": 2,
      "code": "invalid_number",
      "message": "The recipient number is not valid.",
      "param": "to",
      "to": "2296"
    }
  ]
}

Statut d’un message

Méthode HTTP GEThttps://api-next.fastermessage.com/v2/messages/{id}

Renvoie la ressource message, dans la forme exacte que rend l'envoi : un seul désérialiseur suffit des deux côtés.

Statuts possibles

StatutTerminalSignification
schedulednonProgrammé, en attente de sa date d’envoi.
queuednonAccepté et mis en file, pas encore remis au transporteur.
sentnonRemis au transporteur, accusé de livraison non reçu.
deliveredouiRemis au destinataire, confirmé par l’opérateur.
undeliveredouiNon remis — numéro éteint, hors service, boîte pleine.
failedouiÉchec avant remise : rejet opérateur, contenu refusé, erreur de passerelle.
Requête
curl https://api-next.fastermessage.com/v2/messages/msg_2f1c9d4e8a6b \
  -u "$FM_API_USER:$FM_API_SECRET" \
  -H "X-Api-Key: $FM_API_KEY"
Succès — 200 OK
{
  "id": "msg_2f1c9d4e8a6b",
  "channel": "sms",
  "status": "delivered",
  "to": "22960000000",
  "from": "FASTERMSG",
  "units": 1,
  "price": 12,
  "currency": "XOF",
  "refund": null,
  "client_reference": "cmd-42",
  "fallback_of": null,
  "error": null,
  "created_at": "2026-09-07T10:00:00.000Z",
  "scheduled_at": null,
  "sent_at": "2026-09-07T10:00:01.420Z",
  "delivered_at": "2026-09-07T10:00:06.900Z"
}

Lister les messages

Méthode HTTP GEThttps://api-next.fastermessage.com/v2/messages

Paramètres de requête

ChampTypeDescription
channeloptionnelstringFiltre sur un canal : sms, whatsapp, email, voice.
statusoptionnelstringFiltre sur l’un des six statuts.
limitoptionnelintegerTaille de page.
cursoroptionnelstringCurseur de pagination — reprendre la valeur next_cursor de la page précédente.
Succès — 200 OK
{
  "data": [ { "id": "msg_2f1c9d4e8a6b", "status": "delivered" } ],
  "next_cursor": "eyJvIjoxfQ",
  "has_more": true
}

Solde

Méthode HTTP GEThttps://api-next.fastermessage.com/v2/balance

Renvoie le solde disponible, canal par canal et dans la devise du compte. Tous les canaux sont rendus, y compris à zéro : un canal absent serait indiscernable d'un solde nul.

Requête
curl https://api-next.fastermessage.com/v2/balance \
  -u "$FM_API_USER:$FM_API_SECRET" \
  -H "X-Api-Key: $FM_API_KEY"
Succès — 200 OK
{
  "balances": [
    { "channel": "sms", "amount": 289009, "currency": "XOF" },
    { "channel": "whatsapp", "amount": 12000, "currency": "XOF" },
    { "channel": "email", "amount": 0, "currency": "XOF" },
    { "channel": "voice", "amount": 4500, "currency": "XOF" }
  ]
}

Mouvements de crédit

Méthode HTTP GEThttps://api-next.fastermessage.com/v2/credits

Journal des mouvements : credit (achat), debit (consommation) et refund, distinct du crédit — un remboursement système n'est pas un achat.

Expéditeurs (Sender ID)

Ce que l'API impose en revanche, à chaque envoi : from est obligatoire sur le canal SMS et doit correspondre à un Sender ID approuvé sur le compte. Il n'existe aucun expéditeur par défaut de compte.

SituationComportement
from absentRefus 400 missing_parameter, avant tout contrôle de solde.
from inconnu ou non approuvéRefus 403. Rien n’est débité.
Compte en mode testLe Sender ID par défaut de la marque est empruntable, sans approbation nominative.

La marche à suivre pour en déclarer un est détaillée dans la documentation du backoffice.

Reste de la surface

Les points d'entrée ci-dessous suivent les mêmes conventions d'authentification, d'erreur et de pagination.

MéthodeCheminObjet
POST/v2/messagesEnvoi unifié — le canal est un paramètre du corps.
POST/v2/whatsapp/sendEnvoi WhatsApp par modèle approuvé par Meta.
POST/v2/email/sendEnvoi d’e-mail — content est un objet à subject.
POST/v2/voice/sendAppel vocal — texte lu ou audio_url pré-enregistré.
POST/v2/otpÉmission d’un code de vérification.
POST/v2/otp/verifyVérification d’un code.
GET/v2/contactsCarnet d’adresses — lecture, création, mise à jour, suppression.
GET/v2/contact-groupsGroupes de contacts, mêmes opérations.
GET/v2Découverte du contrat, sans identifiants.
GET/v2/pingTest de connectivité, sans identifiants.

Webhooks et accusés de livraison

Le statut d'un message évolue après la réponse HTTP. Plutôt que d'interroger GET /v2/messages/{id} en boucle, déclarez une URL de rappel : elle est appelée en POST à chaque changement d'état.

L'URL se pose soit sur le compte (backoffice, Intégration & API), soit par message via callback_url — qui prime alors sur celle du compte.

Charge utile reçue
{
  "id": "msg_2f1c9d4e8a6b",
  "channel": "sms",
  "status": "delivered",
  "to": "22960000000",
  "from": "FASTERMSG",
  "units": 1,
  "price": 12,
  "currency": "XOF",
  "client_reference": "cmd-42",
  "error": null,
  "sent_at": "2026-09-07T10:00:01.420Z",
  "delivered_at": "2026-09-07T10:00:06.900Z"
}

Statuts notifiés

Les six mêmes que ceux de la ressource message. Seuls delivered, undelivered et failed sont terminaux : après eux, aucun autre rappel n'est à attendre pour ce message.

Politique de reprise

  • Votre point de terminaison doit répondre 2xx en moins de dix secondes. Toute autre réponse, ou un dépassement, vaut échec.
  • Un échec est rejoué avec un intervalle croissant. Répondez donc 200 dès réception et traitez ensuite : un traitement lent finit toujours par être rejoué.
  • Les rappels ne sont pas ordonnés. Un sent peut arriver après un delivered — fiez-vous aux horodatages, jamais à l'ordre d'arrivée.
  • Le même événement peut être livré deux fois. Rendez votre traitement idempotent en vous appuyant sur le couple id + status.

Sécurité

N'exposez jamais d'action destructrice derrière l'URL de rappel, et servez-la exclusivement en HTTPS.

Codes d’erreur

Toute erreur suit la même forme, quel que soit l'endpoint : un objet unique sous error, que le client peut distinguer d'un succès sans rien connaître de l'appel.

Forme d’une erreur
{
  "error": {
    "type": "invalid_request",
    "code": "missing_parameter",
    "message": "A recipient is required.",
    "param": "to",
    "request_id": "2f1c9d4e-8a6b-4c1e-9f2a-7d3b5e0c1a84"
  }
}

type dit comment réagir, code dit ce qui s'est passé. Testez le code en dur : il est stable. Ne testez jamais message, qui est en anglais, destiné aux développeurs, et susceptible d'être reformulé.

Familles d’erreurs

typeHTTPComment réagir
invalid_request400 · 404 · 409Corriger l’appel. Ne pas réessayer à l’identique.
authentication_error401Identifiants absents, faux ou révoqués.
permission_error403Authentifié, mais la clé n’a pas la portée requise.
business_error402 · 403Refus métier : ni l’appel ni les identifiants ne sont en cause.
rate_limit_error429Réessayer après le délai de Retry-After.
api_error500 · 501 · 503Défaillance de notre côté. Réessayable, et jamais facturée.

Codes courants

codeHTTPCauseRésolution
missing_parameter400Un champ obligatoire manque ; param le nomme.Ajouter le champ signalé.
invalid_parameter400Champ présent mais illisible : date non ISO, canal inconnu, contenu du mauvais type.Corriger le format ; voir l’exemple du canal.
invalid_number400Destinataire hors format E.164 ou non routable.Normaliser en E.164 (8 à 15 chiffres).
unauthenticated401Basic ou X-Api-Key absent, faux, révoqué, ou les deux sans lien d’affiliation.Vérifier les deux identifiants ; régénérer la clé au besoin.
insufficient_scope403La clé n’a pas le droit d’effectuer cette action.Employer une clé du compte concerné.
insufficient_balance402Solde disponible insuffisant, réservations en cours déduites.Recharger le compte payeur.
recipient_opted_out403Le destinataire s’est retiré de la liste « Ne plus contacter » du canal.Ne plus le solliciter sur ce canal ; la liste est par canal.
sender_not_allowed403Sender ID inconnu du compte ou non approuvé.Déclarer et faire approuver l’expéditeur.
resource_not_found404Identifiant inconnu — ou appartenant à un autre compte.Vérifier l’id et le compte de la clé.
idempotency_conflict409La même Idempotency-Key a servi pour une charge différente.Employer une nouvelle clé.
idempotency_in_progress409Une requête portant cette clé est encore en cours.Attendre puis rejouer à l’identique.
rate_limit_exceeded429Plafond de débit atteint.Attendre Retry-After, puis réessayer.
not_implemented501Endpoint déclaré au contrat, pas encore servi.Rien à corriger — nous suivre pour sa mise en service.
service_unavailable503Indisponibilité temporaire.Réessayer avec un intervalle croissant.
internal_error500Défaillance inattendue de notre côté.Réessayer, puis nous citer le request_id.

Limites de débit

Le débit est plafonné par clé d'API, sur une fenêtre glissante. Le plafond par défaut est de 600 requêtes par minute ; il est relevable sur demande selon votre volume.

En-têtes de réponse

En-têteContenu
X-RateLimit-LimitPlafond de la fenêtre courante.
X-RateLimit-RemainingAppels restants avant refus.
X-RateLimit-ResetHorodatage Unix (secondes) de réarmement de la fenêtre.
Retry-AfterDélai en secondes avant de réessayer — présent uniquement sur un 429.

Un client bien élevé règle sa cadence sur X-RateLimit-Remaining plutôt que d'attendre le refus. En cas de 429, respectez Retry-After : réessayer plus tôt ne fait que prolonger le blocage.

Autres plafonds

  • 500 lignes par envoi en lot.
  • 24 h de conservation d'une Idempotency-Key ; au-delà, la clé est réutilisable et l'appel repart.
  • 10 s pour qu'un point de terminaison de webhook réponde.