Aller au contenu

Référence API

Messages

Lisez les messages individuels, et rattrapez tout ce qui a changé pendant que vous n’écoutiez pas.

C’est votre voie de récupération pour les webhooks

Lister les messages

GET/public/v1/messages

Renvoie les messages dont le statut a changé à partir d’un moment donné, ordonnés de sorte qu’un curseur puisse parcourir l’ensemble sans trou.

Portée requise MESSAGES_READ

Paramètres de requête

status_changed_sincestringObligatoire

Où reprendre. Les valeurs de plus de sept jours sont silencieusement ramenées à sept jours plutôt que refusées.

ISO 8601 — clamped to the last 7 days

limitintegerOptionnel

Combien de messages renvoyer par page.

default 100, maximum 500

batch_idstringOptionnel

Restreint les résultats à un seul lot.

26-character ULID

Requête

curl -G https://api.smsend.net/public/v1/messages \
  -H "Authorization: Bearer $SMSEND_API_KEY" \
  --data-urlencode "status_changed_since=2026-08-23T00:00:00Z" \
  --data-urlencode "limit=100"
<?php

$cursor = '2026-08-23T00:00:00Z';

do {
    $page = Http::withToken(getenv('SMSEND_API_KEY'))
        ->get('https://api.smsend.net/public/v1/messages', [
            'status_changed_since' => $cursor,
            'limit' => 500,
        ])
        ->json('data');

    foreach ($page['messages'] as $message) {
        // Deduplicate on message_id + status: the boundary row repeats.
        handle($message);
    }

    $cursor = $page['next_status_changed_since'] ?? $cursor;
} while ($page['has_more']);
let cursor = '2026-08-23T00:00:00Z'
let hasMore = true

while (hasMore) {
  const url = new URL('https://api.smsend.net/public/v1/messages')
  url.searchParams.set('status_changed_since', cursor)
  url.searchParams.set('limit', '500')

  const response = await fetch(url, {
    headers: { Authorization: `Bearer ${process.env.SMSEND_API_KEY}` },
  })

  const { data } = await response.json()

  for (const message of data.messages) {
    // Deduplicate on message_id + status: the boundary row repeats.
    handle(message)
  }

  cursor = data.next_status_changed_since ?? cursor
  hasMore = data.has_more
}
import os
import requests

cursor = "2026-08-23T00:00:00Z"
has_more = True

while has_more:
    data = requests.get(
        "https://api.smsend.net/public/v1/messages",
        headers={"Authorization": f"Bearer {os.environ['SMSEND_API_KEY']}"},
        params={"status_changed_since": cursor, "limit": 500},
    ).json()["data"]

    for message in data["messages"]:
        # Deduplicate on message_id + status: the boundary row repeats.
        handle(message)

    cursor = data["next_status_changed_since"] or cursor
    has_more = data["has_more"]

Réponses

200Une page de messages, plus le curseur d’où reprendre.
{
  "data": {
    "messages": [
      {
        "message_id": "01K3F7Y1A4B8C2D6E0F4G8H2JK",
        "batch_id": "01K3F7XQZ8V2N4M6P8R0T5CJWE",
        "msisdn": "+22670000001",
        "status": "DELIVERED",
        "channel": "SMS",
        "message_class": "TRANSACTIONAL",
        "segments": 1,
        "encoding": "GSM7",
        "submitted_at": "2026-08-23T14:05:13+00:00",
        "delivered_at": "2026-08-23T14:05:19+00:00",
        "failed_reason": null,
        "status_changed_at": "2026-08-23T14:05:19+00:00"
      },
      {
        "message_id": "01K3F7Y1A4B8C2D6E0F4G8H2JM",
        "batch_id": "01K3F7XQZ8V2N4M6P8R0T5CJWE",
        "msisdn": "+22670000002",
        "status": "FAILED",
        "channel": "SMS",
        "message_class": "TRANSACTIONAL",
        "segments": 2,
        "encoding": "UCS2",
        "submitted_at": "2026-08-23T14:05:13+00:00",
        "delivered_at": null,
        "failed_reason": "ABSENT_SUBSCRIBER",
        "status_changed_at": "2026-08-23T14:05:22+00:00"
      }
    ],
    "has_more": true,
    "next_status_changed_since": "2026-08-23T14:05:22+00:00"
  }
}

Erreurs possibles

CodeStatutSignification
VALIDATION_FAILED422La requête était malformée : un paramètre manquant ou invalide, une longueur de clé d’idempotence incorrecte, une URL de webhook rejetée, ou une méthode ou un type de contenu non pris en charge.
UNAUTHORIZED401Aucune clé, une clé malformée, un mauvais secret, une clé révoquée ou expirée, ou un compte suspendu. Tous indiscernables par construction.
INSUFFICIENT_SCOPE403La clé est valide mais ne porte pas la portée exigée par ce point de terminaison. La portée est nommée dans le detail.
RATE_LIMITED429La limite de requêtes de la clé a été dépassée. Attendez l’intervalle indiqué dans Retry-After.
INTERNAL_ERROR500Une défaillance inattendue du côté de SMSend. Citez l’identifiant de requête à l’assistance.

Récupérer un message

GET/public/v1/messages/{message_id}

Lit un message unique à partir de son identifiant.

Portée requise MESSAGES_READ

Paramètres de chemin

message_idstringObligatoire

L’identifiant du message, tel que renvoyé par le point de terminaison de liste ou par un webhook.

26-character ULID

Requête

curl https://api.smsend.net/public/v1/messages/01K3F7Y1A4B8C2D6E0F4G8H2JK \
  -H "Authorization: Bearer $SMSEND_API_KEY"
<?php

$message = Http::withToken(getenv('SMSEND_API_KEY'))
    ->get('https://api.smsend.net/public/v1/messages/01K3F7Y1A4B8C2D6E0F4G8H2JK')
    ->json('data');
const response = await fetch(
  'https://api.smsend.net/public/v1/messages/01K3F7Y1A4B8C2D6E0F4G8H2JK',
  { headers: { Authorization: `Bearer ${process.env.SMSEND_API_KEY}` } },
)

const { data } = await response.json()
import os
import requests

message = requests.get(
    "https://api.smsend.net/public/v1/messages/01K3F7Y1A4B8C2D6E0F4G8H2JK",
    headers={"Authorization": f"Bearer {os.environ['SMSEND_API_KEY']}"},
).json()["data"]

Réponses

200Le message.
{
  "data": {
    "message_id": "01K3F7Y1A4B8C2D6E0F4G8H2JK",
    "batch_id": "01K3F7XQZ8V2N4M6P8R0T5CJWE",
    "msisdn": "+22670000001",
    "status": "DELIVERED",
    "channel": "SMS",
    "message_class": "TRANSACTIONAL",
    "segments": 1,
    "encoding": "GSM7",
    "submitted_at": "2026-08-23T14:05:13+00:00",
    "delivered_at": "2026-08-23T14:05:19+00:00",
    "failed_reason": null,
    "status_changed_at": "2026-08-23T14:05:19+00:00"
  }
}

Erreurs possibles

CodeStatutSignification
NOT_FOUND404Ressource inexistante — y compris une ressource appartenant à un autre compte, délibérément indiscernable d’une ressource qui n’a jamais existé.
UNAUTHORIZED401Aucune clé, une clé malformée, un mauvais secret, une clé révoquée ou expirée, ou un compte suspendu. Tous indiscernables par construction.
INSUFFICIENT_SCOPE403La clé est valide mais ne porte pas la portée exigée par ce point de terminaison. La portée est nommée dans le detail.
RATE_LIMITED429La limite de requêtes de la clé a été dépassée. Attendez l’intervalle indiqué dans Retry-After.
INTERNAL_ERROR500Une défaillance inattendue du côté de SMSend. Citez l’identifiant de requête à l’assistance.

Statuts de message

Un message finit dans exactement un état terminal et ne change plus jamais d’état une fois qu’il y est.

ValeurSignification
SCHEDULED
En attente de son heure planifiée.
QUEUED
En attente du routeur.
RETRY_SCHEDULED
Une tentative de livraison a échoué et une autre est planifiée.
SUBMITTED
Remis à un fournisseur qui n’a pas encore renvoyé de référence. Un message bloqué ici est le problème de SMSend, pas le vôtre.
SENT
Le fournisseur l’a accepté et a renvoyé une référence.
DELIVEREDTerminal
Le téléphone a confirmé la réception.
FAILEDTerminal
La livraison a échoué. Le motif est dans failed_reason.
EXPIREDTerminal
Aucun accusé de réception n’est arrivé dans les 48 heures.
REJECTEDTerminal
Le fournisseur ou l’opérateur l’a refusé.
CANCELLEDTerminal
Écarté au moment de l’envoi, ou annulé avec son lot.

Comment fonctionne le curseur

Les pages sont ordonnées par le moment du changement de statut, avec l’identifiant du message comme départage. L’ordre est ainsi total, si bien que deux messages ayant changé dans la même milliseconde ne peuvent pas se retrouver à cheval sur une frontière de page et voir l’un des deux oublié.

Quand une page revient vide, le curseur est nul. Gardez votre propre position plutôt que d’avancer — avancer au-delà d’une période calme sauterait des événements qui n’avaient pas encore été écrits.