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
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." }'
const auth = Buffer.from(`${process.env.FM_API_USER}:${process.env.FM_API_SECRET}`).toString('base64'); const response = await fetch('https://api-next.fastermessage.com/v2/sms/send', { method: 'POST', headers: { Authorization: `Basic ${auth}`, 'X-Api-Key': process.env.FM_API_KEY, 'Content-Type': 'application/json', }, body: JSON.stringify({ "to": "+22960000000", "from": "FASTERMSG", "content": "Votre code est 480912. Valable 5 min." }), }); const data = await response.json(); console.log(response.status, data);
<?php $ch = curl_init('https://api-next.fastermessage.com/v2/sms/send'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_USERPWD => getenv('FM_API_USER') . ':' . getenv('FM_API_SECRET'), CURLOPT_HTTPHEADER => [ 'X-Api-Key: ' . getenv('FM_API_KEY'), 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => json_encode([ 'to' => '+22960000000', 'from' => 'FASTERMSG', 'content' => 'Votre code est 480912. Valable 5 min.', ]), ]); $response = curl_exec($ch); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); echo $status . PHP_EOL . $response . PHP_EOL;
import os import requests response = requests.post( "https://api-next.fastermessage.com/v2/sms/send", auth=(os.environ["FM_API_USER"], os.environ["FM_API_SECRET"]), headers={"X-Api-Key": os.environ["FM_API_KEY"]}, json={ "to": "+22960000000", "from": "FASTERMSG", "content": "Votre code est 480912. Valable 5 min.", }, timeout=15, ) print(response.status_code, response.json())
{
"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.
| Identifiant | Transmis par | Désigne |
|---|---|---|
| Basic | Authorization | Le compte à défalquer : c'est lui qui paie l'envoi. |
| Clé d’API | X-Api-Key | Le 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.
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
| Environnement | Préfixe de clé | Comportement |
|---|---|---|
| test | fm_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. |
| production | fm_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é
deliveredaussitôt ; un envoi programmé restependingà 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, ou0000selon la longueur demandée). Aucun SMS ne partant, c'est ce qui permet d'éprouverPOST /v2/otp/verifyde 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.
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
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
| Champ | Type | Description |
|---|---|---|
| torequis | string | Destinataire au format E.164 (8 à 15 chiffres, + facultatif). |
| fromrequis | string | Sender 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. |
| contentrequis | string | Texte du message. Au-delà de 160 caractères GSM (70 en UCS-2), il est découpé en segments facturés à l’unité. |
| scheduled_atoptionnel | string | Date d’envoi en ISO 8601 UTC. Une date illisible est refusée, jamais ignorée. |
| callback_urloptionnel | string | URL de rappel propre à ce message — prime sur l’URL configurée sur le compte. |
| client_referenceoptionnel | string | Référence libre, reprise telle quelle dans la réponse et dans les webhooks. |
| fallbackoptionnel | array | Cascade 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
| Champ | Type | Description |
|---|---|---|
| Idempotency-Keyoptionnel | string | Clé 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. |
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" }'
const auth = Buffer.from(`${process.env.FM_API_USER}:${process.env.FM_API_SECRET}`).toString('base64'); const response = await fetch('https://api-next.fastermessage.com/v2/sms/send', { method: 'POST', headers: { Authorization: `Basic ${auth}`, 'X-Api-Key': process.env.FM_API_KEY, 'Content-Type': 'application/json', }, body: JSON.stringify({ "to": "+22960000000", "from": "FASTERMSG", "content": "Votre commande cmd-42 est prête.", "client_reference": "cmd-42", "callback_url": "https://exemple.test/webhooks/fastermessage" }), }); const data = await response.json(); console.log(response.status, data);
<?php $ch = curl_init('https://api-next.fastermessage.com/v2/sms/send'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_USERPWD => getenv('FM_API_USER') . ':' . getenv('FM_API_SECRET'), CURLOPT_HTTPHEADER => [ 'X-Api-Key: ' . getenv('FM_API_KEY'), 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => json_encode([ 'to' => '+22960000000', 'from' => 'FASTERMSG', 'content' => 'Votre commande cmd-42 est prête.', 'client_reference' => 'cmd-42', 'callback_url' => 'https://exemple.test/webhooks/fastermessage', ]), ]); $response = curl_exec($ch); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); echo $status . PHP_EOL . $response . PHP_EOL;
import os import requests response = requests.post( "https://api-next.fastermessage.com/v2/sms/send", auth=(os.environ["FM_API_USER"], os.environ["FM_API_SECRET"]), headers={"X-Api-Key": os.environ["FM_API_KEY"]}, json={ "to": "+22960000000", "from": "FASTERMSG", "content": "Votre commande cmd-42 est prête.", "client_reference": "cmd-42", "callback_url": "https://exemple.test/webhooks/fastermessage", }, timeout=15, ) print(response.status_code, response.json())
{
"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"
}{
"error": {
"type": "business_error",
"code": "insufficient_balance",
"message": "Insufficient balance to process the request.",
"request_id": "2f1c9d4e-8a6b-4c1e-9f2a-7d3b5e0c1a84"
}
}Envoi en masse
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
| Champ | Type | Description |
|---|---|---|
| messagesrequis | array | Lignes du lot, 500 au maximum. Chaque ligne accepte les champs de l’envoi simple. |
| fromoptionnel | string | Expéditeur du lot, hérité par les lignes qui n’en déclarent pas. Obligatoire — au lot ou sur chaque ligne. |
| contentoptionnel | string | Texte du lot, hérité de la même façon. |
| scheduled_atoptionnel | string | Date d’envoi commune, en ISO 8601 UTC. |
| callback_urloptionnel | string | URL de rappel commune à toutes les lignes. |
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" } ] }'
const auth = Buffer.from(`${process.env.FM_API_USER}:${process.env.FM_API_SECRET}`).toString('base64'); const response = await fetch('https://api-next.fastermessage.com/v2/sms/batch', { method: 'POST', headers: { Authorization: `Basic ${auth}`, 'X-Api-Key': process.env.FM_API_KEY, 'Content-Type': 'application/json', }, body: JSON.stringify({ "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" } ] }), }); const data = await response.json(); console.log(response.status, data);
<?php $ch = curl_init('https://api-next.fastermessage.com/v2/sms/batch'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_USERPWD => getenv('FM_API_USER') . ':' . getenv('FM_API_SECRET'), CURLOPT_HTTPHEADER => [ 'X-Api-Key: ' . getenv('FM_API_KEY'), 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => json_encode([ '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', ], ], ]), ]); $response = curl_exec($ch); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); echo $status . PHP_EOL . $response . PHP_EOL;
import os import requests response = requests.post( "https://api-next.fastermessage.com/v2/sms/batch", auth=(os.environ["FM_API_USER"], os.environ["FM_API_SECRET"]), headers={"X-Api-Key": os.environ["FM_API_KEY"]}, json={ "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", }, ], }, timeout=15, ) print(response.status_code, response.json())
{
"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
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
| Statut | Terminal | Signification |
|---|---|---|
| scheduled | non | Programmé, en attente de sa date d’envoi. |
| queued | non | Accepté et mis en file, pas encore remis au transporteur. |
| sent | non | Remis au transporteur, accusé de livraison non reçu. |
| delivered | oui | Remis au destinataire, confirmé par l’opérateur. |
| undelivered | oui | Non remis — numéro éteint, hors service, boîte pleine. |
| failed | oui | Échec avant remise : rejet opérateur, contenu refusé, erreur de passerelle. |
curl https://api-next.fastermessage.com/v2/messages/msg_2f1c9d4e8a6b \ -u "$FM_API_USER:$FM_API_SECRET" \ -H "X-Api-Key: $FM_API_KEY"
const auth = Buffer.from(`${process.env.FM_API_USER}:${process.env.FM_API_SECRET}`).toString('base64'); const response = await fetch('https://api-next.fastermessage.com/v2/messages/msg_2f1c9d4e8a6b', { method: 'GET', headers: { Authorization: `Basic ${auth}`, 'X-Api-Key': process.env.FM_API_KEY, }, }); const data = await response.json(); console.log(response.status, data);
<?php $ch = curl_init('https://api-next.fastermessage.com/v2/messages/msg_2f1c9d4e8a6b'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_USERPWD => getenv('FM_API_USER') . ':' . getenv('FM_API_SECRET'), CURLOPT_HTTPHEADER => [ 'X-Api-Key: ' . getenv('FM_API_KEY'), ], ]); $response = curl_exec($ch); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); echo $status . PHP_EOL . $response . PHP_EOL;
import os import requests response = requests.get( "https://api-next.fastermessage.com/v2/messages/msg_2f1c9d4e8a6b", auth=(os.environ["FM_API_USER"], os.environ["FM_API_SECRET"]), headers={"X-Api-Key": os.environ["FM_API_KEY"]}, timeout=15, ) print(response.status_code, response.json())
{
"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
Paramètres de requête
| Champ | Type | Description |
|---|---|---|
| channeloptionnel | string | Filtre sur un canal : sms, whatsapp, email, voice. |
| statusoptionnel | string | Filtre sur l’un des six statuts. |
| limitoptionnel | integer | Taille de page. |
| cursoroptionnel | string | Curseur de pagination — reprendre la valeur next_cursor de la page précédente. |
{
"data": [ { "id": "msg_2f1c9d4e8a6b", "status": "delivered" } ],
"next_cursor": "eyJvIjoxfQ",
"has_more": true
}Solde
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.
curl https://api-next.fastermessage.com/v2/balance \ -u "$FM_API_USER:$FM_API_SECRET" \ -H "X-Api-Key: $FM_API_KEY"
const auth = Buffer.from(`${process.env.FM_API_USER}:${process.env.FM_API_SECRET}`).toString('base64'); const response = await fetch('https://api-next.fastermessage.com/v2/balance', { method: 'GET', headers: { Authorization: `Basic ${auth}`, 'X-Api-Key': process.env.FM_API_KEY, }, }); const data = await response.json(); console.log(response.status, data);
<?php $ch = curl_init('https://api-next.fastermessage.com/v2/balance'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_USERPWD => getenv('FM_API_USER') . ':' . getenv('FM_API_SECRET'), CURLOPT_HTTPHEADER => [ 'X-Api-Key: ' . getenv('FM_API_KEY'), ], ]); $response = curl_exec($ch); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); echo $status . PHP_EOL . $response . PHP_EOL;
import os import requests response = requests.get( "https://api-next.fastermessage.com/v2/balance", auth=(os.environ["FM_API_USER"], os.environ["FM_API_SECRET"]), headers={"X-Api-Key": os.environ["FM_API_KEY"]}, timeout=15, ) print(response.status_code, response.json())
{
"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
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.
| Situation | Comportement |
|---|---|
from absent | Refus 400 missing_parameter, avant tout contrôle de solde. |
from inconnu ou non approuvé | Refus 403. Rien n’est débité. |
| Compte en mode test | Le 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éthode | Chemin | Objet |
|---|---|---|
| POST | /v2/messages | Envoi unifié — le canal est un paramètre du corps. |
| POST | /v2/whatsapp/send | Envoi WhatsApp par modèle approuvé par Meta. |
| POST | /v2/email/send | Envoi d’e-mail — content est un objet à subject. |
| POST | /v2/voice/send | Appel vocal — texte lu ou audio_url pré-enregistré. |
| POST | /v2/otp | Émission d’un code de vérification. |
| POST | /v2/otp/verify | Vérification d’un code. |
| GET | /v2/contacts | Carnet d’adresses — lecture, création, mise à jour, suppression. |
| GET | /v2/contact-groups | Groupes de contacts, mêmes opérations. |
| GET | /v2 | Découverte du contrat, sans identifiants. |
| GET | /v2/ping | Test 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.
{
"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
2xxen 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
200dès réception et traitez ensuite : un traitement lent finit toujours par être rejoué. - Les rappels ne sont pas ordonnés. Un
sentpeut arriver après undelivered— 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.
{
"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
| type | HTTP | Comment réagir |
|---|---|---|
| invalid_request | 400 · 404 · 409 | Corriger l’appel. Ne pas réessayer à l’identique. |
| authentication_error | 401 | Identifiants absents, faux ou révoqués. |
| permission_error | 403 | Authentifié, mais la clé n’a pas la portée requise. |
| business_error | 402 · 403 | Refus métier : ni l’appel ni les identifiants ne sont en cause. |
| rate_limit_error | 429 | Réessayer après le délai de Retry-After. |
| api_error | 500 · 501 · 503 | Défaillance de notre côté. Réessayable, et jamais facturée. |
Codes courants
| code | HTTP | Cause | Résolution |
|---|---|---|---|
| missing_parameter | 400 | Un champ obligatoire manque ; param le nomme. | Ajouter le champ signalé. |
| invalid_parameter | 400 | Champ présent mais illisible : date non ISO, canal inconnu, contenu du mauvais type. | Corriger le format ; voir l’exemple du canal. |
| invalid_number | 400 | Destinataire hors format E.164 ou non routable. | Normaliser en E.164 (8 à 15 chiffres). |
| unauthenticated | 401 | Basic 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_scope | 403 | La clé n’a pas le droit d’effectuer cette action. | Employer une clé du compte concerné. |
| insufficient_balance | 402 | Solde disponible insuffisant, réservations en cours déduites. | Recharger le compte payeur. |
| recipient_opted_out | 403 | Le 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_allowed | 403 | Sender ID inconnu du compte ou non approuvé. | Déclarer et faire approuver l’expéditeur. |
| resource_not_found | 404 | Identifiant inconnu — ou appartenant à un autre compte. | Vérifier l’id et le compte de la clé. |
| idempotency_conflict | 409 | La même Idempotency-Key a servi pour une charge différente. | Employer une nouvelle clé. |
| idempotency_in_progress | 409 | Une requête portant cette clé est encore en cours. | Attendre puis rejouer à l’identique. |
| rate_limit_exceeded | 429 | Plafond de débit atteint. | Attendre Retry-After, puis réessayer. |
| not_implemented | 501 | Endpoint déclaré au contrat, pas encore servi. | Rien à corriger — nous suivre pour sa mise en service. |
| service_unavailable | 503 | Indisponibilité temporaire. | Réessayer avec un intervalle croissant. |
| internal_error | 500 | Dé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ête | Contenu |
|---|---|
| X-RateLimit-Limit | Plafond de la fenêtre courante. |
| X-RateLimit-Remaining | Appels restants avant refus. |
| X-RateLimit-Reset | Horodatage Unix (secondes) de réarmement de la fenêtre. |
| Retry-After | Dé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.