Documentation de l'API SMS pour envoyer depuis votre propre code

Une petite API REST sur le même compte, les mêmes prix et le même solde que l'espace de travail : envoyez un message ou un lot de jusqu'à 5,000 numéros, estimez d'abord le coût, suivez chaque message et recevez les événements de livraison à votre URL webhook.

URL de base : https://sendsmsonline.cc/api/v1

Démarrage rapide

  1. 1. Créez une clé. Dans l'espace de travail, ouvrez l'API et les webhooks. Les clés sont disponibles après votre premier rechargement crédité ; chaque clé n'est affichée qu'une seule fois, alors copiez-la ou enregistrez-la dans un fichier texte.
  2. 2. Vérifiez le coût. POSTez le même corps à /costs pour voir les destinataires, les segments et le total sans envoyer.
  3. 3. Envoyez. POSTez-le à /sends. La réponse contient l'identifiant d'envoi ; les messages partent immédiatement ou à send_at.
curl https://sendsmsonline.cc/api/v1/sends \
  -H "Authorization: Bearer $SSO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "recipients": [ { "to": "+447700900461", "fields": { "first_name": "Tom" } }, { "to": "+12015550142", "fields": { "first_name": "Ava" } } ], "text": "Hi {{first_name}}, your table is ready at 7:30.", "name": "Friday bookings", "client_ref": "bookings-1107" }'

Authentification

Envoyez votre clé dans l'en-tête Authorization : Authorization: Bearer sso_live_.... Les clés font 49 caractères et commencent par sso_live_. Jusqu'à 5 clés par compte ; révoquez-en une dans l'espace de travail et elle cesse de fonctionner immédiatement. La liste des prix est publique et ne nécessite pas de clé.

Erreurs

Les erreurs utilisent des codes de statut HTTP et un corps JSON avec un code stable et une phrase que vous pouvez montrer à une personne. Rien n'est facturé lorsqu'une requête échoue.

HTTP/1.1 402 Payment Required
{ "error": { "code": "insufficient_credit", "message": "Short by $3.20. Add funds to send.", "total": "8.62480", "credit": "5.42000" } }
ChampSignification
400La requête nécessite une modification.
401Clé manquante, incorrecte ou révoquée.
402Crédit insuffisant pour le total.
404Aucun envoi, message, pays ou point de terminaison correspondant.
409L'envoi a déjà commencé.
429Plus de 240 requêtes par minute.

Limites

  • 240 requêtes par minute par clé, affichées dans X-RateLimit-Limit, X-RateLimit-Remaining et X-RateLimit-Reset (secondes unix).
  • 5,000 numéros par requête, 2,000 caractères par texte, 10 segments par message.
  • Les messages partent à un maximum de 300 par minute par compte.
  • send_at de 10 minutes à 30 jours à l'avance.

Prix

GET/api/v1/prices

GET/api/v1/prices/{country}

Le prix par message (un segment) pour chaque pays, en USD avec 5 décimales. Aucune clé nécessaire.

{ "price": { "country": "GB", "name": "United Kingdom", "dial_code": "+44", "price_usd": "0.00868" }, "updated": "2026-11-07" }

Crédit du compte

GET/api/v1/account

{ "account": "SO8K2M4Q7X9TBA", "credit_usd": "41.27660", "currency": "USD" }

Estimer un coût

POST/api/v1/costs

Prend le même corps qu'un envoi et retourne ce que cela coûterait, sans envoyer ni facturer. Le texte fusionné de chaque destinataire détermine ses segments, donc le total est exact.

{
  "recipients": 2, "segments_min": 1, "segments_max": 1, "encoding": "gsm7", "messages_billed": 2,
  "total_usd": "0.01426", "credit_usd": "41.27660", "enough_credit": true,
  "countries": [ { "country": "GB", "recipients": 1, "price_usd": "0.00868", "subtotal_usd": "0.00868" },
                 { "country": "US", "recipients": 1, "price_usd": "0.00558", "subtotal_usd": "0.00558" } ],
  "dropped": [], "too_long": null
}

Créer un envoi

POST/api/v1/sends

Un texte à un ou plusieurs numéros. Le crédit pour l'ensemble de l'envoi est débité lorsqu'il est accepté ; les messages échoués sont remboursés un par un.

ChampSignification
toNuméros au format international (+447700900461). Utilisez ceci ou destinataires.
recipientsObjets avec to et fields, pour les champs de fusion : { "to": "+1...", "fields": { "first_name": "Ava" } }.
textLe message, jusqu'à 2,000 caractères. {{field}} est remplacé par destinataire ; les valeurs manquantes deviennent vides.
send_atHeure ISO 8601 facultative avec décalage ou Z. Sans cela, l'envoi commence immédiatement.
nameÉtiquette facultative affichée dans l'espace de travail (80 caractères).
client_refRéférence facultative retournée dans les webhooks (64 caractères).
opt_outAjoute la ligne de désabonnement enregistrée dans les Paramètres à la fin du texte.
default_countryCode ISO utilisé pour les numéros écrits sans indicatif pays.
HTTP/1.1 201 Created
{
  "send": { "id": "CA7Q29XMAB4DFK", "status": "queued", "source": "api", "recipients": 2, "segments_max": 1,
            "encoding": "gsm7", "cost_usd": "0.01426", "refunded_usd": "0.00000", "client_ref": "bookings-1107",
            "send_at": null, "created_at": "2026-11-07T10:12:03.120Z", "started_at": null, "finished_at": null },
  "dropped": [ { "value": "+4412", "code": "too_short", "message": "Too short" } ]
}

Les numéros qui ne peuvent pas être envoyés sont listés dans dropped et jamais facturés. Codes : no_country_code, too_short, too_long, not_a_number, wrong_length, not_covered, duplicate et suppressed (sur votre liste de non-envoi).

Obtenir ou lister les envois

GET/api/v1/sends/{id}

GET/api/v1/sends?limit=25&before={id}

Un seul envoi inclut counts par statut de message. Les listes sont les plus récentes en premier ; passez next_before comme before pour la page suivante. Les envois depuis l'espace de travail apparaissent ici aussi, avec source: "web".

Messages

GET/api/v1/sends/{id}/messages?status=failed&limit=100

GET/api/v1/messages/{id}

{ "message": { "id": "MS3D8RW0K2QJ7N", "send_id": "CA7Q29XMAB4DFK", "to": "+447700900461", "country": "GB",
  "segments": 1, "price_usd": "0.00868", "status": "delivered", "reason": null, "reason_text": null,
  "created_at": "2026-11-07T10:12:03.120Z", "sent_at": "2026-11-07T10:12:04.010Z", "done_at": "2026-11-07T10:12:09.400Z" } }

Annuler un envoi

POST/api/v1/sends/{id}/cancel

Seuls les envois programmés qui n'ont pas commencé peuvent être annulés. Le coût total retourne à votre crédit et la réponse inclut refunded_usd.

Webhooks

Définissez une URL https dans l'espace de travail (API et webhooks). Nous POSTons un événement lorsqu'un message est livré ou échoue, pour les envois depuis l'API et l'espace de travail. Répondez avec un statut 2xx dans les 10 secondes. Les livraisons échouées sont réessayées jusqu'à 7 fois au total : immédiatement, puis après 1 minute, 5 minutes, 30 minutes, 1 heure, 2 heures et 3 heures. L'espace de travail montre chaque tentative avec son statut et sa réponse.

POST https://your-server.example/sms-events
X-SSO-Event: message.delivered
X-SSO-Delivery: evt_5a0c9e31d2b7f4a68c
X-SSO-Timestamp: 1794132729
X-SSO-Signature: sha256=8f1c...e42a

{ "id": "evt_5a0c9e31d2b7f4a68c", "event": "message.delivered", "created": "2026-11-07T10:12:09.000Z",
  "data": { "message": { "id": "MS3D8RW0K2QJ7N", "send_id": "CA7Q29XMAB4DFK", "to": "+447700900461", "country": "GB",
  "segments": 1, "price_usd": "0.00868", "status": "delivered", "reason": null, "reason_text": null,
  "client_ref": "bookings-1107", "created_at": "...", "sent_at": "...", "done_at": "..." } } }

La signature est HMAC-SHA256 de timestamp + "." + raw body avec votre secret de signature (il commence par sso_whk_). Vérifiez-la sur le corps brut avant l'analyse, rejetez les timestamps de plus de 5 minutes, et utilisez l'id de livraison pour ignorer les répétitions.

import crypto from "node:crypto";

// rawBody: the request body exactly as received (a string), before JSON.parse
export function verify(rawBody, headers, secret) {
  const ts = headers["x-sso-timestamp"];
  const sig = headers["x-sso-signature"] || "";
  const want = "sha256=" + crypto.createHmac("sha256", secret).update(`${ts}.${rawBody}`).digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300;
  return fresh && sig.length === want.length && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(want));
}

Événements : message.delivered, message.failed, et test depuis le bouton Envoyer un événement test.

Statuts et raisons

ChampSignification
queuedEn attente de quitter notre file.
sentTransmis au réseau, en attente du rapport de livraison. La plupart des rapports arrivent en quelques minutes ; certains réseaux peuvent prendre jusqu'à 24 heures.
deliveredLe réseau a confirmé que le téléphone l'a reçu.
failedNon livré. Le prix a été remboursé sur votre crédit.
canceledNon envoyé car vous avez annulé. Remboursé.

Raisons d'échec (le champ reason) :

ChampSignification
network_errorErreur réseau
absentTéléphone éteint ou hors couverture
not_in_serviceNuméro hors service
blockedBloqué par le réseau ou le destinataire
landlineLigne fixe ou numéro ne pouvant pas recevoir de SMS
content_filterRejeté par le filtre de contenu du réseau
expiredExpiré : aucun rapport avant la limite de temps du réseau
canceledAnnulé avant l'envoi
unknownRéponse réseau inconnue

Les vérifications de service sont sur la page d'état et les changements d'API dans le changelog.

Obtenez votre clé API

Créez un compte, ajoutez des fonds une fois et ouvrez l'API et les webhooks dans l'espace de travail. La même clé fonctionne pour tous les pays.

Créer un compte gratuit
Documentation de l'API SMS : Envoyer des SMS en masse depuis votre code | SendSMSOnline