Envoyer un SMS : POST /v1/sms
Un seul appel suffit pour envoyer un message : G-SMS déduit l'opérateur du numéro, calcule le nombre de segments, débite le compte et met le message en file. La réponse arrive avant la remise effective — le suivi de livraison est un sujet distinct.
Requête
POST https://api.sms.gworldship.com/v1/sms, avec un en-tête Authorization: Bearer gsk_… et un corps JSON.
curl -X POST 'https://api.sms.gworldship.com/v1/sms' \
-H 'Authorization: Bearer gsk_votre_cle_api' \
-H 'Content-Type: application/json' \
-d '{"to":"+237690000000","text":"Votre code de confirmation est 4821."}'Le même envoi, muni d'une référence métier et d'un numéro saisi au format local : les espaces sont ignorés, le numéro est normalisé.
curl -X POST 'https://api.sms.gworldship.com/v1/sms' \
-H 'Authorization: Bearer gsk_votre_cle_api' \
-H 'Content-Type: application/json' \
-d '{
"to": "677 12 34 56",
"text": "Commande 10427 confirmee. Livraison prevue jeudi.",
"clientReference": "commande-10427-confirmation"
}'Paramètres
| Champ | Type | Obligatoire | Description |
|---|---|---|---|
to | string | Oui | Numéro du destinataire. Trois formats acceptés : +2376XXXXXXXX, 2376XXXXXXXX ou 6XXXXXXXX. Les espaces sont ignorés. La réponse renvoie toujours la forme E.164. |
text | string | Oui | Contenu du message, de 1 à 1600 caractères. Un texte vide ou composé uniquement d'espaces est refusé. |
clientReference | string | Non | Ton propre identifiant d'envoi, 64 caractères au plus. Unique par application : réutiliser une référence renvoie le message existant, sans second débit. |
Aucun autre champ n'est lu. Il n'existe pas de nom d'expéditeur personnalisé : le destinataire voit le numéro de la carte SIM émettrice.
Réponse : 202 Accepted
HTTP/1.1 202 Accepted
{
"messageId": "0d0a6f4e-1c3b-4f6a-9a1e-52a0f6d2b311",
"to": "+237677123456",
"operator": "MTN",
"segments": 1,
"priceXaf": 5,
"status": "QUEUED",
"clientReference": "commande-10427-confirmation",
"balanceAfterXaf": 4995,
"createdAt": "2026-09-12T10:15:00Z"
}| Champ | Type | Description |
|---|---|---|
messageId | uuid | Identifiant du message chez G-SMS. À conserver : c'est la clé de toutes les relectures. |
to | string | Numéro normalisé au format E.164, par exemple +237690000000. |
operator | string | ORANGE, MTN, CAMTEL, NEXTTEL, ou DEFAULT pour un préfixe hors plan national. |
segments | integer | Nombre de segments facturés pour ce texte. |
priceXaf | integer | Montant débité : tarif de l'opérateur × nombre de segments. |
status | string | QUEUED à la création, puis SENT, DELIVERED ou FAILED. |
clientReference | string | Renvoyé tel quel si la requête en contenait un. Absent sinon. |
balanceAfterXaf | integer | Solde du compte après le débit. Présent uniquement sur le 202 de création. |
createdAt | date-time | Date d'acceptation de l'envoi, en ISO-8601 UTC. |
failureReason | string | Cause de l'échec. Présent quand le statut vaut FAILED. |
sentAt | date-time | Date de remise à l'opérateur. Présent à partir du statut SENT. |
deliveredAt | date-time | Date de l'accusé de réception du terminal. Présent au statut DELIVERED. |
null. Un message tout juste créé n'a donc ni sentAt, ni deliveredAt, ni failureReason : leur absence est normale et ne doit pas être traitée comme une anomalie. La même structure est renvoyée par GET /v1/sms/{messageId} et par chaque entrée du journal GET /v1/sms, à ceci près que balanceAfterXaf n'apparaît que sur la création.
Détection de l'opérateur par le préfixe
Il n'y a rien à préciser dans la requête : l'opérateur est déduit du numéro, par correspondance du préfixe le plus long. Cette règle n'est pas un détail — les plages 654 et 688 sont découpées entre opérateurs, et une correspondance sur trois chiffres seulement enverrait le message sur le mauvais réseau.
| Opérateur | Préfixes nationaux | Tarif par segment |
|---|---|---|
| Orange | 655 à 659, 686, 687, 6880 à 6888, 68890 à 68895, 688960 à 688964, 69 | 5 XAF |
| MTN | 650 à 653, 6540 à 6545, 65460, 65468, 65469, 6547 à 6549, 67, 680 à 683 | 5 XAF |
| Camtel | 620, 621, 6220 à 6225 | 7 XAF |
| Nexttel | 66 | 7 XAF |
| DEFAULT | tout préfixe non couvert par les règles ci-dessus | 10 XAF |
Un numéro dont le préfixe n'est couvert par aucune règle n'est pas rejeté : il part par la route par défaut, tracée DEFAULT et facturée à son propre tarif. C'est le cas des sous-plages encore non attribuées au plan national.
Ces règles de préfixes vivent en base et suivent les attributions du régulateur : elles peuvent évoluer. Pour vérifier un numéro précis, l'outil opérateur d'un numéro camerounais applique exactement les mêmes règles.
Ordre des contrôles
Connaître cet ordre évite des heures de perplexité : quand deux problèmes coexistent, c'est toujours le premier de cette liste qui est signalé.
- Le compte porteur de la clé existe et est actif.
- Le
clientReference, s'il est fourni, n'a jamais servi. Sinon la réponse est un409qui contient le message existant — et rien n'est débité. - Le texte et le numéro sont valides, l'opérateur est résolu, le prix est calculé (
TEXT_EMPTY,TEXT_TOO_LONG,INVALID_NUMBER,PRICE_NOT_CONFIGURED). - L'opérateur visé n'est pas suspendu. S'il l'est,
503 OPERATOR_DISABLEDest renvoyé avant tout débit. - Le quota journalier du compte n'est pas atteint.
- Le solde couvre le prix : le débit et l'enregistrement du message se font dans la même transaction. Si le solde est insuffisant,
402 INSUFFICIENT_BALANCE.
Le débit du solde et la création du message sont indissociables : il n'existe pas de cas où un compte est débité sans message, ni de message sans débit.
Cas d'échec
| HTTP | Code | Signification |
|---|---|---|
| 400 | TEXT_EMPTY | text est absent, vide ou ne contient que des espaces. |
| 400 | TEXT_TOO_LONG | text dépasse 1600 caractères. |
| 400 | INVALID_NUMBER | to n'est pas un mobile camerounais valide (9 chiffres commençant par 6). |
| 400 | PRICE_NOT_CONFIGURED | aucun tarif n'est configuré pour l'opérateur résolu. Cas anormal : signale-le-nous. |
| 400 | VALIDATION_ERROR | une contrainte de champ est violée, typiquement un clientReference de plus de 64 caractères. |
| 400 | BAD_REQUEST | le corps JSON est illisible ou mal typé. |
| 401 | INVALID_API_KEY | clé absente, inconnue ou révoquée. |
| 403 | ACCOUNT_SUSPENDED | le compte porteur de la clé n'est pas actif. |
| 409 | — | le clientReference a déjà servi : le corps est le message existant, pas une erreur. |
| 429 | RATE_LIMITED | plus de 10 requêtes par seconde sur la même clé. En-tête Retry-After : 1. |
| 429 | QUOTA_EXCEEDED | le quota journalier du compte est atteint (désactivé par défaut). |
| 402 | INSUFFICIENT_BALANCE | le solde ne couvre pas le prix de l'envoi. |
| 503 | OPERATOR_DISABLED | les envois vers cet opérateur sont suspendus. En-tête Retry-After : 3600. Aucun débit n'a eu lieu. |
Une erreur a toujours la même forme : un code stable et un message explicatif.
HTTP/1.1 400 Bad Request
{
"code": "INVALID_NUMBER",
"message": "Numéro invalide : formats acceptés +2376XXXXXXXX, 2376XXXXXXXX, 6XXXXXXXX"
}409 est la seule réponse en 4xx qui ne suit pas cette forme : son corps est le message déjà envoyé, au format d'une réponse normale. Un client qui lit body.code sur tous les 4xx se casse exactement là — l'idempotence en détail. La liste complète des codes du serveur, classée par statut HTTP, est sur la page codes d'erreur.
Prêt à envoyer ton premier SMS ?
Crée un compte, reçois 100 F de crédit et une clé API immédiate.