La documentation de l’API REST Sendimo v1.
Envoyez des SMS vers les numéros tunisiens avec un seul appel HTTP, suivez leur livraison et recevez les statuts par webhook. API REST en HTTPS, JSON en entrée et en sortie.
L’essentiel
| Élément | Valeur |
|---|---|
URL de base | https://api.sendimo.tn/v1 |
Protocole | HTTPS uniquement (TLS 1.2+) |
Format | JSON, UTF-8, Content-Type: application/json |
Authentification | Authorization: Bearer sk_live_… (ou sk_test_…) |
Versions | Version majeure dans le chemin (/v1). Les ajouts non cassants sont publiés sans changer de version. |
Dates | ISO 8601, UTC (2026-10-30T09:15:00Z) |
Identifiants | Chaînes préfixées : msg_…, bat_…, snd_… |
Montants | Chaîne à 3 décimales, en TND ("0.140") |
Numéros | E.164 (+21620123456). Les numéros locaux à 8 chiffres sont acceptés et convertis en +216. |
Endpoints v1
| Méthode | Chemin | Rôle |
|---|---|---|
POST | /messages | Envoyer un SMS |
POST | /messages/bulk | Envoyer le même SMS, ou des SMS personnalisés, à plusieurs numéros |
GET | /messages/{id} | Obtenir un message et son statut |
GET | /messages | Lister les messages (filtres et pagination) |
DELETE | /messages/{id} | Annuler un message programmé |
GET | /batches/{id} | Suivre la progression d’un envoi en masse |
GET | /sender-ids | Lister vos noms d’expéditeur approuvés |
POST | /sender-ids | Demander un nouveau nom d’expéditeur |
GET | /account/balance | Consulter le solde restant |
Authentification
Chaque requête porte une clé API secrète en jeton Bearer. Les clés sont créées et révoquées dans votre espace ; chaque clé n’est affichée qu’une fois, à sa création.
- Les clés s’utilisent uniquement côté serveur. Ne les placez jamais dans une application mobile ou du code navigateur.
- Une clé absente ou invalide renvoie 401 unauthorized. Une clé révoquée renvoie 401 key_revoked.
- Numéros de test : en mode test, +21600000001 finit toujours en delivered et +21600000002 toujours en failed.
| Préfixe | Mode | Comportement |
|---|---|---|
sk_live_ | Production | Envoie de vrais SMS et débite le solde |
sk_test_ | Test | Valide la requête et renvoie des réponses réalistes ; rien n’est envoyé ni facturé. Les webhooks sont envoyés avec des statuts simulés. |
curl https://api.sendimo.tn/v1/account/balance \
-H "Authorization: Bearer sk_live_9f2c..."Envoyer un SMS — POST /messages
Envoie un SMS. L’appel répond dès que le message est accepté (202) ; le statut final arrive ensuite par webhook ou via GET /messages/{id}.
| Champ | Type | Requis | Description |
|---|---|---|---|
to | string | Oui | Destinataire, en E.164 ou numéro local à 8 chiffres |
from | string | Oui | Un Sender ID approuvé (par exemple SENDIMO) |
text | string | Oui | Contenu, 1 530 caractères GSM-7 ou 670 caractères Unicode maximum (10 segments) |
scheduled_at | string | Non | Date ISO 8601 pour un envoi différé, jusqu’à 30 jours à l’avance |
reference | string | Non | Votre propre identifiant (64 caractères max), renvoyé dans les réponses et les webhooks |
webhook_url | string | Non | Remplace le webhook du compte pour ce message |
curl -X POST https://api.sendimo.tn/v1/messages \
-H "Authorization: Bearer $SENDIMO_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-4821-otp" \
-d '{
"to": "+21620123456",
"from": "SENDIMO",
"text": "Votre code est 482913. Il expire dans 5 minutes.",
"reference": "order-4821"
}'{
"id": "msg_01HF8Z3K9QX2",
"status": "queued",
"to": "+21620123456",
"from": "SENDIMO",
"text": "Votre code est 482913. Il expire dans 5 minutes.",
"encoding": "GSM-7",
"segments": 1,
"network": "ooredoo",
"cost": { "amount": "0.140", "currency": "TND" },
"reference": "order-4821",
"scheduled_at": null,
"created_at": "2026-10-30T09:15:00Z"
}Envoi en masse — POST /messages/bulk
Envoie jusqu’à 10 000 messages en un seul appel, avec un texte commun ou un texte par destinataire pour personnaliser. Renvoie un lot (batch) que vous pouvez suivre.
Les numéros invalides ne font pas échouer tout le lot : ils sont listés dans rejected avec { "index", "to", "error" } et ne sont pas facturés. GET /batches/{id} renvoie le même objet avec le nombre de messages par statut (queued, sent, delivered, failed).
| Champ | Type | Requis | Description |
|---|---|---|---|
from | string | Oui | Sender ID approuvé, utilisé pour tous les messages |
text | string | Non* | Texte commun à tous les destinataires. *Requis sauf si chaque élément a son propre text |
messages | array | Oui | Liste de { "to", "text"?, "reference"? } ; un text par élément remplace le texte commun |
scheduled_at | string | Non | Envoyer tout le lot plus tard |
reference | string | Non | Votre identifiant pour le lot |
{
"from": "SENDIMO",
"text": "Promo -20% ce week-end sur tout le magasin.",
"messages": [
{ "to": "+21620123456" },
{ "to": "+21698765432", "text": "Salut Amine, -20% pour toi ce week-end." }
]
}{
"id": "bat_01HF90AB7C",
"status": "processing",
"total": 2,
"accepted": 2,
"rejected": [],
"estimated_cost": { "amount": "0.280", "currency": "TND" },
"created_at": "2026-10-30T09:20:00Z"
}Consulter et lister les messages
GET /messages/{id} renvoie le message avec son statut actuel, ainsi que sent_at, delivered_at et error (null sauf en cas d’échec).
GET /messages liste les messages, du plus récent au plus ancien, avec une pagination par curseur.
| Paramètre | Exemple | Description |
|---|---|---|
status | failed | Filtrer par statut |
to | +21620123456 | Filtrer par destinataire |
batch_id | bat_01HF90AB7C | Messages d’un envoi en masse |
reference | order-4821 | Votre identifiant |
created_after / created_before | 2026-10-01T00:00:00Z | Intervalle de dates |
limit | 50 | 1 à 100, 20 par défaut |
cursor | eyJpZCI6... | Valeur de next_cursor de la page précédente |
{
"data": [ { "id": "msg_01HF8Z3K9QX2", "status": "delivered", "...": "..." } ],
"has_more": true,
"next_cursor": "eyJpZCI6Im1zZ18wMUhGOFoz..."
}Statuts des messages
Un message passe de queued à sent puis delivered, ou se termine en failed, undelivered ou canceled. Les statuts finaux ne changent plus. Pour annuler un message programmé : DELETE /messages/{id} (uniquement tant qu’il est scheduled).
| Statut | Final | Signification | Facturé |
|---|---|---|---|
scheduled | Non | En attente de scheduled_at ; peut être annulé | Non |
queued | Non | Accepté, en attente d’envoi à l’opérateur | Réservé |
sent | Non | Transmis à l’opérateur (Ooredoo, Orange, Tunisie Telecom) | Oui |
delivered | Oui | L’opérateur a confirmé la remise sur le téléphone | Oui |
undelivered | Oui | L’opérateur n’a pas pu livrer (téléphone éteint trop longtemps, abonné invalide) | Oui |
failed | Oui | Rejeté avant d’atteindre l’opérateur ; voir error.code | Non, remboursé |
canceled | Oui | Message programmé annulé par vous | Non |
Sender IDs
Un Sender ID est le nom que voient les destinataires à la place d’un numéro (11 lettres ou chiffres max, par exemple SENDIMO). Les opérateurs tunisiens exigent que chaque nom soit approuvé avant utilisation : les nouveaux noms passent donc en revue.
Statuts : pending, approved, rejected (avec un motif). Envoyer avec un nom non approuvé renvoie 403 sender_not_approved.
{
"data": [
{ "id": "snd_01HE7A", "name": "SENDIMO", "status": "approved", "created_at": "2026-10-01T10:00:00Z" },
{ "id": "snd_01HF2C", "name": "MYSHOP", "status": "pending", "created_at": "2026-10-29T14:30:00Z" }
]
}{ "name": "MYSHOP", "use_case": "Order confirmations and delivery updates for myshop.tn" }Solde — GET /account/balance
reserved est bloqué pour les messages en file d’attente, puis libéré ou débité quand ils atteignent un statut. Un envoi qui dépasse le solde disponible renvoie 402 insufficient_balance.
{
"balance": { "amount": "152.340", "currency": "TND" },
"reserved": { "amount": "0.900", "currency": "TND" },
"low_balance_threshold": { "amount": "20.000", "currency": "TND" }
}Erreurs
Toutes les erreurs ont le même format, avec un code stable lisible par machine et un request_id pour le support.
| HTTP | Code | Cause |
|---|---|---|
400 | invalid_request | JSON mal formé ou champ manquant |
400 | invalid_phone_number | to n’est pas un numéro valide |
400 | text_too_long | Plus de 10 segments |
401 | unauthorized / key_revoked | Clé API invalide, absente ou révoquée |
402 | insufficient_balance | Solde insuffisant pour l’envoi |
403 | sender_not_approved | from n’est pas un Sender ID approuvé |
404 | not_found | Message, lot ou Sender ID inconnu |
409 | idempotency_conflict | Même Idempotency-Key réutilisée avec un corps différent |
422 | destination_not_supported | Numéro hors des pays pris en charge (v1 : Tunisie uniquement) |
429 | rate_limited | Trop de requêtes ; voir Retry-After |
500 / 503 | internal_error / unavailable | Erreur de notre côté ; vous pouvez réessayer avec la même clé d’idempotence |
{
"error": {
"code": "invalid_phone_number",
"message": "'to' must be a valid Tunisian number in E.164 format.",
"field": "to",
"request_id": "req_01HF9C4D"
}
}Limites de débit
Chaque réponse contient X-RateLimit-Limit, X-RateLimit-Remaining et X-RateLimit-Reset. Une réponse 429 inclut Retry-After, en secondes.
| Portée | Limite |
|---|---|
Tous les endpoints, par clé API | 100 requêtes / seconde |
POST /messages/bulk | 10 requêtes / minute |
Débit d’envoi | 50 SMS / seconde par compte (plus sur demande) |
Idempotence
Envoyez un en-tête Idempotency-Key (n’importe quelle chaîne unique, 64 caractères max) sur chaque POST. Si une erreur réseau vous laisse dans le doute, réessayez avec la même clé : Sendimo renvoie la réponse d’origine au lieu d’envoyer deux fois. Les clés sont conservées 24 heures.
Encodage et segments
Sendimo choisit l’encodage automatiquement et facture par segment. Le texte arabe, les emojis et certains caractères accentués imposent l’Unicode, ce qui divise environ par deux le nombre de caractères par SMS.
La réponse indique toujours encoding, segments et cost : vous pouvez afficher le prix avant un envoi en masse.
| Encodage | 1 segment | Par segment si découpé | Contenu typique |
|---|---|---|---|
GSM-7 | 160 caractères | 153 caractères | Texte latin, français sans la plupart des accents |
Unicode (UCS-2) | 70 caractères | 67 caractères | Arabe, emojis, caractères hors GSM-7 |