G-SMS.

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

ChampTypeObligatoireDescription
tostringOuiNumé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.
textstringOuiContenu du message, de 1 à 1600 caractères. Un texte vide ou composé uniquement d'espaces est refusé.
clientReferencestringNonTon 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"
}
ChampTypeDescription
messageIduuidIdentifiant du message chez G-SMS. À conserver : c'est la clé de toutes les relectures.
tostringNuméro normalisé au format E.164, par exemple +237690000000.
operatorstringORANGE, MTN, CAMTEL, NEXTTEL, ou DEFAULT pour un préfixe hors plan national.
segmentsintegerNombre de segments facturés pour ce texte.
priceXafintegerMontant débité : tarif de l'opérateur × nombre de segments.
statusstringQUEUED à la création, puis SENT, DELIVERED ou FAILED.
clientReferencestringRenvoyé tel quel si la requête en contenait un. Absent sinon.
balanceAfterXafintegerSolde du compte après le débit. Présent uniquement sur le 202 de création.
createdAtdate-timeDate d'acceptation de l'envoi, en ISO-8601 UTC.
failureReasonstringCause de l'échec. Présent quand le statut vaut FAILED.
sentAtdate-timeDate de remise à l'opérateur. Présent à partir du statut SENT.
deliveredAtdate-timeDate de l'accusé de réception du terminal. Présent au statut DELIVERED.
Les champs sans valeur sont omis, jamais renvoyés à 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érateurPréfixes nationauxTarif par segment
Orange655 à 659, 686, 687, 6880 à 6888, 68890 à 68895, 688960 à 688964, 695 XAF
MTN650 à 653, 6540 à 6545, 65460, 65468, 65469, 6547 à 6549, 67, 680 à 6835 XAF
Camtel620, 621, 6220 à 62257 XAF
Nexttel667 XAF
DEFAULTtout préfixe non couvert par les règles ci-dessus10 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é.

  1. Le compte porteur de la clé existe et est actif.
  2. Le clientReference, s'il est fourni, n'a jamais servi. Sinon la réponse est un 409 qui contient le message existant — et rien n'est débité.
  3. 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).
  4. L'opérateur visé n'est pas suspendu. S'il l'est, 503 OPERATOR_DISABLED est renvoyé avant tout débit.
  5. Le quota journalier du compte n'est pas atteint.
  6. 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

HTTPCodeSignification
400TEXT_EMPTYtext est absent, vide ou ne contient que des espaces.
400TEXT_TOO_LONGtext dépasse 1600 caractères.
400INVALID_NUMBERto n'est pas un mobile camerounais valide (9 chiffres commençant par 6).
400PRICE_NOT_CONFIGUREDaucun tarif n'est configuré pour l'opérateur résolu. Cas anormal : signale-le-nous.
400VALIDATION_ERRORune contrainte de champ est violée, typiquement un clientReference de plus de 64 caractères.
400BAD_REQUESTle corps JSON est illisible ou mal typé.
401INVALID_API_KEYclé absente, inconnue ou révoquée.
403ACCOUNT_SUSPENDEDle 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.
429RATE_LIMITEDplus de 10 requêtes par seconde sur la même clé. En-tête Retry-After : 1.
429QUOTA_EXCEEDEDle quota journalier du compte est atteint (désactivé par défaut).
402INSUFFICIENT_BALANCEle solde ne couvre pas le prix de l'envoi.
503OPERATOR_DISABLEDles 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"
}
Le 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.

Créer un compte