Start here
Démarrage rapide : API SMS Maroc & Passerelle SMS
EnvoiSMS.ma est une infrastructure de messagerie programmable conçue pour le Maroc. Nos routes locales vers IAM, Inwi et Orange, avec bascule automatique entre routes, visent une latence minimale pour vos envois SMS.
Générez votre clé API "smr_..." depuis votre Console EnvoiSMS.ma.
Utilisez l'authentification Bearer dans vos headers HTTP pour chaque requête.
Testez votre intégration via des clés Sandbox (env_test_...) sur l'URL live pour valider vos appels sans consommer de crédit réel.
Intégrez le WhatsApp Business API pour diviser vos coûts OTP par 10.
Configurez un Webhook signé pour recevoir les accusés de réception en temps réel.
Suivez votre consommation et vos factures MAD directement sur votre Dashboard.
// config/services.php
'envoisms' => [
'key' => env('ENVOISMS_API_KEY'),
],
// Usage
Http::withToken(config('services.envoisms.key'))
->post('https://api.envoisms.ma/v1/messages', [
'to' => '+212612345678',
'message' => 'Votre commande est en cours de livraison 🚚',
'from' => 'MaBoutique'
]); OpenAPI Spec Authentification
Authentification & Clés API SMS sécurisées
Chaque endpoint /v1 nécessite une clé API valide transmise dans le header Authorization sous forme de jeton Bearer. Les clés de test (sandbox) et de production peuvent être générées ou révoquées depuis votre console.
Authorization: Bearer smr_xxx
X-RateLimit-Limit et X-RateLimit-Remaining retournés à chaque appel.
Les requêtes provenant d'adresses IP non configurées retournent un statut 401.
JSON, Authorization, X-EnvoiSMS.ma-Signature, et X-EnvoiSMS.ma-Version sont supportés.
POST /v1/messages HTTP/1.1
Host: api.envoisms.ma
Authorization: Bearer smr_xxxxxxxx
Content-Type: application/json
Accept: application/jsonOpen Source
SDKs API SMS officiels (PHP, Node.js, Python, Laravel)
Incorporez l'API SMS EnvoiSMS en quelques lignes de code grâce à nos SDKs open-source officiels.
npm install envoismscomposer require envoisms/envoisms-phppip install envoismscomposer require envoisms/laravel-otpgo get github.com/n4ouri/envoisms-gonpx -y @envoisms/mcp-serverEnvoyer un message
Envoie un SMS ou un message WhatsApp Business à un destinataire unique.
| Champ | Type | Statut | Description |
|---|---|---|---|
| to | string | Requis | Numéro de téléphone au format E.164 (+212...). |
| message | string | Requis | Contenu textuel (max 1600 caractères). Également accepté sous le nom "body". |
| from | string | Optionnel | Sender ID personnalisé (ex: NOM_MARQUE). Par défaut "EnvoiSMS". |
| channel | string | Optionnel | "sms" ou "whatsapp". Par défaut "sms". "whatsapp" envoie depuis votre propre numéro WhatsApp Business connecté (sinon 403 WHATSAPP_NOT_CONNECTED). Hors modèle, un message WhatsApp n'est accepté que si le contact vous a écrit dans les dernières 24 h — sinon 400 OUT_OF_24H_WINDOW, sans aucun débit (avec cascade, WhatsApp est sauté au profit du canal suivant). |
| cascade | boolean | Optionnel | Si activé, tente WhatsApp puis bascule sur SMS : immédiatement si WhatsApp refuse ou signale l'échec du message (non inscrit sur WhatsApp, fenêtre de 24 h fermée…), ou si aucun accusé de réception n'arrive dans le délai cascade_timeout (120 s par défaut). Seul l'envoi réellement parti est facturé : un WhatsApp refusé ou en échec ne l'est pas, et vous ne payez que le SMS. Un WhatsApp resté sans accusé de réception est parti (il peut encore être livré quand le téléphone se reconnecte) : il est facturé avec son SMS de repli, et remboursé si WhatsApp signale ensuite son échec. Conseil : donnez à votre modèle WhatsApp une durée de vie (TTL) inférieure ou égale à cascade_timeout, pour qu'un téléphone qui revient en ligne ne reçoive pas les deux. |
| cascade_timeout | integer | Optionnel | Avec cascade : secondes d'attente d'un accusé de réception avant le canal suivant, de 30 à 43200 (12 h). Défaut : 120. Plus court = repli plus rapide mais plus de doubles envois facturés ; plus long = moins de doublons. |
| metadata | object | Optionnel | Clés-valeurs personnalisées stockées avec le message et transmises dans les webhooks (elles ne sont pas renvoyées par les endpoints GET /v1/messages). Une clé a un sens pour la plateforme : purpose: "otp" signale un code à usage unique que vous générez vous-même et active la relivraison automatique (voir notes). |
| buttons | array | Optionnel | Tableau d'objets boutons WhatsApp interactifs (max 3, type "copy" | "url" | "call"). |
| interactive | object | Optionnel | Objet de message interactif WhatsApp riche : menus listes déroulants ("list"), boutons de réponse rapide ("button"), formulaires natifs ("flow"), bouton lien ("cta_url" : action {"name":"cta_url","parameters":{"display_text","url"}}) ou demande de localisation ("location_request_message"). |
| template | object | Optionnel | (WhatsApp) Modèle approuvé de votre compte : {"name", "language"}, valeurs dans metadata.variables. Seul type de message autorisé hors fenêtre de 24 h ; tarifé selon la catégorie du modèle. |
| image | video | audio | document | sticker | object | Optionnel | (WhatsApp) Média par "id" (média déjà téléversé) OU par "link" (URL https), jamais les deux. "caption" pour image, vidéo et document ; "filename" pour document. |
| reaction | object | Optionnel | (WhatsApp) {"message_id": wamid, "emoji": "👍"} — réagit à un message de la conversation ; emoji vide pour retirer la réaction. Non facturé. |
| contacts | array | Optionnel | (WhatsApp) Fiches contact (max 20) : name.formatted_name obligatoire ; phones, emails, urls, addresses, org, birthday facultatifs. |
| location | object | Optionnel | (WhatsApp) Épingle de localisation : {"latitude", "longitude", "name"?, "address"?}. |
| context | object | Optionnel | (WhatsApp) {"message_id": wamid} — répond en citant un message précédent. Valable sur tous les types sauf reaction. |
| phone_number_id | string | Optionnel | (WhatsApp) Numéro connecté qui envoie ; par défaut votre numéro par défaut. |
- Vous envoyez des codes OTP ? Deux approches. /v1/verify/send gère tout le cycle (génération, livraison, validation, expiration) et reste la voie recommandée. Si vous générez vos propres codes et les envoyez ici, ajoutez metadata: {"purpose": "otp"} : en cas d'échec de livraison confirmé par le réseau sur un numéro marocain alors que le code est encore frais (moins de 10 minutes), la plateforme le renvoie automatiquement une fois par une route SMS alternative — même identifiant de message, aucun coût supplémentaire. Sans ce tag, le message est traité comme un SMS ordinaire.
- WhatsApp : hors modèle, un message (texte, média, réaction, contacts, localisation, interactif) ne part que vers un contact qui a écrit à votre numéro dans les dernières 24 h ; sinon la requête est refusée en 400 OUT_OF_24H_WINDOW avant tout débit. Avec une clé sandbox, l'envoi reste simulé et la réponse signale le refus dans "warnings". Pour afficher « en train d'écrire » pendant que vous préparez la réponse, voir POST /v1/messages/typing.
- Les sauts de ligne (\n) sont pleinement pris en charge sur tous les canaux et s'affichent correctement chez le destinataire.
- Le formatage de texte enrichi (gras, italique, etc.) n'est pas supporté par le canal SMS (texte brut uniquement).
- Le canal WhatsApp prend en charge le formatage de texte avec la syntaxe standard (*gras*, _italique_, ~barré~).
curl -X POST "https://api.envoisms.ma/v1/messages" \
-H "Authorization: Bearer smr_xxx" \
-H "Content-Type: application/json" \
-d '{
"to": "+212612345678",
"message": "Votre code de validation est 849204",
"from": "MaBoutique",
"channel": "whatsapp"
}'{
"id": "msg_8f2d...",
"to": "+212612345678",
"channel": "whatsapp",
"cascade": false,
"status": "queued",
"cost": {
"eur": 0.03,
"mad": 0.33
},
"segments": 1,
"created_at": "2026-05-15T10:30:00Z"
}Envoi groupé (Bulk)
Envoie jusqu'à 10 000 messages en un seul appel API avec des destinataires ou des contenus uniques.
| Champ | Type | Statut | Description |
|---|---|---|---|
| messages | array | Requis | Tableau d'objets contenant "to", "message" (ou "body"), et un objet facultatif "metadata". |
| from | string | Optionnel | Sender ID global pour tout le lot. |
| channel | string | Optionnel | Canal global ("sms" ou "whatsapp"). Par défaut "sms". |
- Les sauts de ligne (\n) sont pleinement pris en charge dans les corps des messages groupés.
- Le formatage de texte enrichi (gras, italique, etc.) n'est pas supporté par le canal SMS (texte brut uniquement).
- Le canal WhatsApp prend en charge le formatage de texte avec la syntaxe standard (*gras*, _italique_, ~barré~).
curl -X POST "https://api.envoisms.ma/v1/messages/bulk" \
-H "Authorization: Bearer smr_xxx" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"to": "+212611111111",
"message": "Hello Client 1"
},
{
"to": "+212622222222",
"message": "Hello Client 2"
}
],
"from": "ENVOISMS",
"channel": "sms"
}'{
"batch_id": "batch_9a3c...",
"total": 2,
"channel": "sms",
"estimated_cost": {
"eur": 0.056,
"mad": 0.62
},
"messages": [
{
"id": "msg_1a2b...",
"to": "+212611111111",
"status": "queued"
},
{
"id": "msg_3c4d...",
"to": "+212622222222",
"status": "queued"
}
]
}Indicateur de saisie WhatsApp
Marque comme lu un message WhatsApp reçu et affiche « en train d'écrire » à son auteur pendant que vous préparez la réponse (disparaît à la réponse ou après environ 25 s). Ce n'est pas un message : rien n'est stocké ni facturé.
| Champ | Type | Statut | Description |
|---|---|---|---|
| message_id | string | Requis | Identifiant WhatsApp (wamid) du message reçu auquel vous répondez. |
| typing_indicator | boolean | Optionnel | false envoie seulement l'accusé de lecture. Défaut: true. |
| phone_number_id | string | Optionnel | Numéro connecté qui a reçu le message ; par défaut votre numéro par défaut. |
curl -X POST "https://api.envoisms.ma/v1/messages/typing" \
-H "Authorization: Bearer smr_xxx" \
-H "Content-Type: application/json" \
-d '{}'{
"status": "ok",
"message_id": "wamid.HBgLMjEyNjEyMzQ1Njc4FQIAEhgg",
"typing_indicator": true
}Lister les messages
Récupère une liste paginée de tous les messages envoyés depuis le compte.
| Paramètre | Type | Statut | Description |
|---|---|---|---|
| limit | integer | Optionnel | Nombre de résultats à retourner (1-200, défaut: 50). |
| offset | integer | Optionnel | Nombre de résultats à ignorer pour la pagination (défaut: 0). |
| status | string | Optionnel | Filtrer par statut (queued, sent, delivered, failed, undeliverable, unconfirmed). |
| channel | string | Optionnel | Filtrer par canal (sms, whatsapp). |
| from_date | string | Optionnel | Filtrer par date de début (format ISO 8601). |
| to_date | string | Optionnel | Filtrer par date de fin (format ISO 8601). |
curl -X GET "https://api.envoisms.ma/v1/messages?limit=50&offset=0" \
-H "Authorization: Bearer smr_xxx"{
"data": [
{
"id": "msg_8f2d...",
"to": "+212612345678",
"channel": "whatsapp",
"body": "Votre code de validation est 849204",
"sender_id": "MaBanque",
"status": "delivered",
"cost_mad": 0.13,
"created_at": "2026-05-15T10:30:00Z"
}
],
"limit": 50,
"offset": 0,
"total": 1
}Statut du message
Consulte les détails et l'état de livraison en temps réel d'un message spécifique.
- Statuts possibles : queued, sent, delivered, failed, undeliverable, unconfirmed. "unconfirmed" signifie que l'opérateur n'a jamais confirmé ni infirmé la livraison — un accusé arrivant plus tard peut encore le remplacer.
- Le champ metadata fourni à l'envoi n'est pas renvoyé ici ; il est transmis dans les webhooks.
curl -X GET "https://api.envoisms.ma/v1/messages/:id" \
-H "Authorization: Bearer smr_xxx"{
"id": "msg_8f2d...",
"campaign_id": null,
"to": "+212612345678",
"channel": "sms",
"body": "Votre code de validation est 849204",
"sender_id": "MaBanque",
"unicode": 0,
"segments": 1,
"status": "delivered",
"error_code": null,
"error_message": null,
"cost_eur": 0.0436,
"cost_mad": 0.48,
"scheduled_at": null,
"sent_at": "2026-05-15T10:30:02Z",
"delivered_at": "2026-05-15T10:30:05Z",
"failed_at": null,
"operator": "Maroc Telecom",
"sandbox": 0,
"created_at": "2026-05-15T10:30:00Z"
}Lister les templates
Récupère tous les templates WhatsApp et SMS approuvés de votre compte.
curl -X GET "https://api.envoisms.ma/v1/templates" \
-H "Authorization: Bearer smr_xxx"{
"data": [
{
"id": "tpl_1234...",
"name": "otp_verification",
"channel": "whatsapp",
"body": "Votre code de validation est {{1}}",
"status": "approved",
"category": "otp",
"created_at": "2026-05-15T10:30:00Z"
}
],
"total": 1
}Créer un template
Soumet un nouveau template pour approbation par les opérateurs ou WhatsApp.
| Champ | Type | Statut | Description |
|---|---|---|---|
| name | string | Requis | Nom interne du template. |
| channel | string | Requis | "sms" ou "whatsapp". |
| body | string | Requis | Contenu du message avec variables (ex: {{1}}). |
| category | string | Optionnel | Catégorie du template (ex: "otp", "marketing"). |
| language | string | Optionnel | WhatsApp : code langue Meta (ex: "fr", "ar", "en_US"). Par défaut "fr". |
| sample_values | object | string[] | Optionnel | WhatsApp : une valeur d'exemple par variable, obligatoire pour la revue Meta (ex: {"1": "Amine"} ou ["Amine"]). |
| header_example_url | string | Optionnel | WhatsApp, en-tête image/vidéo/document : lien https public vers un fichier d'exemple, transmis à Meta pour la revue. |
| add_security_recommendation | boolean | Optionnel | WhatsApp, catégorie "authentication" : ajoute la mention de sécurité de Meta. Le texte du corps est fixé par Meta ; code_expiration_minutes (1-90) est aussi accepté. |
- Un template WhatsApp passe d'abord la revue EnvoiSMS (pending_admin), puis est soumis à Meta sur votre propre compte WhatsApp Business (pending_meta). Seule Meta l'approuve (approved) ; sa catégorie finale est celle que Meta attribue.
curl -X POST "https://api.envoisms.ma/v1/templates" \
-H "Authorization: Bearer smr_xxx" \
-H "Content-Type: application/json" \
-d '{}'{
"id": "tpl_1234...",
"name": "order_confirmation",
"channel": "whatsapp",
"language": "fr",
"status": "pending_admin"
}Synchroniser les templates depuis Meta
Importe tous les templates de votre compte WhatsApp Business (statut, catégorie, qualité). Un template absent chez Meta passe en deleted_on_meta.
curl -X POST "https://api.envoisms.ma/v1/templates/sync" \
-H "Authorization: Bearer smr_xxx" \
-H "Content-Type: application/json" \
-d '{}'{
"ok": true,
"wabas": ["1234567890"],
"fetched": 12,
"updated": 9,
"inserted": 3,
"marked_deleted": 1,
"complete": true,
"errors": []
}Lister les Sender IDs
Récupère la liste de vos Sender IDs avec leur statut d'approbation.
curl -X GET "https://api.envoisms.ma/v1/sender-ids" \
-H "Authorization: Bearer smr_xxx"{
"data": [
{
"id": "sid_9a8b...",
"sender_id": "MABANQUE",
"status": "approved",
"requested_at": "2026-05-10T09:00:00Z"
}
]
}Demander un Sender ID
Soumet un nouveau Sender ID pour approbation (requis pour le Maroc).
| Champ | Type | Statut | Description |
|---|---|---|---|
| sender_id | string | Requis | Le nom d'expéditeur souhaité (max 11 caractères). |
| rc_url | string | Optionnel | Lien vers le Registre de Commerce pour vérification. |
curl -X POST "https://api.envoisms.ma/v1/sender-ids" \
-H "Authorization: Bearer smr_xxx" \
-H "Content-Type: application/json" \
-d '{}'{
"id": "sid_9a8b...",
"sender_id": "MABANQUE",
"status": "pending"
}Générer un OTP
Envoie un code de vérification à usage unique. Deux modes au choix : "whatsapp" — vérification gérée, le plus simple (EnvoiSMS génère le code et le livre sur WhatsApp depuis un expéditeur vérifié ; si Meta refuse l'envoi WhatsApp, le code part par SMS ; avec une application Verify configurée en auto_cascade, le SMS part aussi à l'expiration du délai du canal, 30 s par défaut ; aucun code à stocker de votre côté) — ou "sms" — vous gardez le contrôle total du code, du modèle et de l'expéditeur. Dans les deux cas, la validation se fait avec le même appel /v1/verify/check. Vous souhaitez votre propre marque sur le repli SMS de la vérification gérée ? Disponible sur demande : [email protected].
| Champ | Type | Statut | Description |
|---|---|---|---|
| to | string | Requis | Destinataire au format E.164. |
| channel | string | Optionnel | "whatsapp" (vérification gérée : WhatsApp, repli SMS si Meta refuse l'envoi ou, avec auto_cascade, après le délai du canal ; code valide 10 min, 3 essais, facturée par vérification au tarif WhatsApp OTP) ou "sms" (code généré pour vous, envoyé avec votre marque). Défaut: "sms". |
| app_id | string | Optionnel | ID de l'application Verify configurée sur le tableau de bord (ex: vra_...). Applique automatiquement les paramètres de code, de délais et la cascade de canaux. |
| brand | string | Optionnel | (Canal sms) Nom de la marque affiché (ex: MonApp, max 32 car.). Par défaut "EnvoiSMS". |
| code_length | integer | Optionnel | (Canal sms) Longueur du code généré (de 4 à 8 chiffres, défaut: 6). |
| expiry | integer | Optionnel | Durée de validité du code en secondes (de 60 à 1800, défaut: 600). Sur le canal whatsapp, plafonnée à 600 (limite du modèle d'authentification). |
| cascade | array | Optionnel | (Canal sms) Liste ordonnée de canaux pour le basculement automatique en cascade (ex: ["whatsapp", "sms"]). |
| template | string | Optionnel | (Canal sms) Texte personnalisé avec les variables {{code}} et {{brand}}. En mode whatsapp, le message localisé (fr/en/es) est géré pour vous. |
| otp_button_text | string | Optionnel | (Canal whatsapp) Libellé personnalisé pour le bouton de copie automatique WhatsApp (max 25 car.). |
| web_otp_domain | string | Optionnel | (Optionnel, canal sms) Domaine web pour le remplissage automatique W3C WebOTP (ex: "https://monsite.ma"). Ajoute le tag @domaine #code à la fin du SMS. |
| app_hash | string | Optionnel | (Optionnel, canal sms) Hash de signature d'application Android de 11 caractères (SMS Retriever API) pour détection automatique du code sur Android. |
- Autofill OTP (iOS & Android) : Pour permettre à vos utilisateurs de remplir automatiquement le code reçu en 1 clic au-dessus de leur clavier, ajoutez simplement autocomplete="one-time-code" et inputmode="numeric" sur le champ <input> de votre site.
- Temporisation anti-spam (cooldown) : Le champ "resend_after_seconds" indique la temporisation exacte avant réessai (60 s pour WhatsApp conformément aux normes Meta, 30 s pour SMS). Un plafond journalier de 10 vérifications par numéro s'applique.
curl -X POST "https://api.envoisms.ma/v1/verify/send" \
-H "Authorization: Bearer smr_xxx" \
-H "Content-Type: application/json" \
-d '{
"to": "+212612345678",
"brand": "MonApp",
"channel": "whatsapp",
"code_length": 6,
"expiry": 600
}'{
"session_id": "vrf_7e2a...",
"to": "+212612345678",
"channel": "whatsapp",
"resend_after_seconds": 60,
"fallback_channels": ["sms", "whatsapp"],
"expires_at": "2026-05-15T10:35:00Z",
"status": "sent",
"cost": { "eur": 0.05, "mad": 0.55 }
}Renvoyer un code OTP (SMS ou WhatsApp)
Renvoie le même code OTP actif via SMS ou WhatsApp une fois le délai de temporisation écoulé (30 s pour SMS, 60 s pour WhatsApp). Aucun nouveau code n'est généré, évitant les erreurs de saisie.
| Champ | Type | Statut | Description |
|---|---|---|---|
| session_id | string | Requis | Identifiant de session reçu lors de l'appel initial à /v1/verify/send. |
| channel | string | Optionnel | Canal de renvoi cible ("sms" | "whatsapp"). Par défaut : "sms". |
- Disponible sur toutes les offres, Starter comprise : /v1/verify/resend exige la même permission "verify" que /v1/verify/send, rien de plus.
- D'où part le décompte : la temporisation court depuis le dernier envoi effectif, pas depuis votre appel — 60 s après une remise WhatsApp, 30 s après un SMS. GET /v1/verify/{session_id} renvoie le décompte en cours ("resend_available_in_seconds") et l'en-tête Retry-After du 429 porte le nombre exact de secondes restantes. La fenêtre est propre à votre compte et au numéro : le trafic d'un autre client vers le même numéro ne vous bloque jamais.
- Clés sandbox (env_test_) : rien n'est délivré et rien n'est facturé. La réponse porte alors "sandbox": true et "sandbox_code", pour qu'un renvoi simulé ne soit jamais confondu avec un renvoi réellement délivré. Une clé sandbox ne peut renvoyer que les sessions qu'elle a créées, et une clé live que des sessions live.
curl -X POST "https://api.envoisms.ma/v1/verify/resend" \
-H "Authorization: Bearer smr_xxx" \
-H "Content-Type: application/json" \
-d '{}'{
"session_id": "vrf_7e2a...",
"message_id": "wamid.HBgMMjEy...",
"to": "+212612345678",
"channel": "whatsapp",
"status": "sent",
"resend_after_seconds": 60,
"expires_at": "2026-05-15T10:35:00Z"
}Vérifier un OTP
Valide le code fourni par l'utilisateur pour une session de vérification donnée.
| Champ | Type | Statut | Description |
|---|---|---|---|
| session_id | string | Requis | ID de session reçu lors de l'appel à /v1/verify/send. |
| code | string | Requis | Le code reçu et saisi par l'utilisateur. |
curl -X POST "https://api.envoisms.ma/v1/verify/check" \
-H "Authorization: Bearer smr_xxx" \
-H "Content-Type: application/json" \
-d '{
"session_id": "vrf_7e2a...",
"code": "849204"
}'{
"session_id": "vrf_7e2a...",
"verified": true,
"verified_at": "2026-05-15T10:35:12Z"
}Statut de session OTP
Consulte l'état (validé ou expiré) d'une session de vérification spécifique.
curl -X GET "https://api.envoisms.ma/v1/verify/:id" \
-H "Authorization: Bearer smr_xxx"{
"id": "vrf_7e2a...",
"to": "+212612345678",
"channel": "whatsapp",
"expires_at": "2026-05-15T10:40:00Z",
"verified_at": "2026-05-15T10:35:12Z",
"created_at": "2026-05-15T10:30:00Z"
}Validation de numéro
Valide le format, l'opérateur (carrier), le type de ligne et la localisation géographique d'un numéro de téléphone.
| Champ | Type | Statut | Description |
|---|---|---|---|
| number | string | Requis | Le numéro de téléphone à valider (format local ou international). |
| country_code | string | Optionnel | Code pays ISO à 2 lettres (ex: MA, FR). Recommandé si le numéro est au format local. |
curl -X POST "https://api.envoisms.ma/v1/verify/lookup" \
-H "Authorization: Bearer smr_xxx" \
-H "Content-Type: application/json" \
-d '{
"number": "+212612345678",
"country_code": "MA"
}'{
"valid": true,
"number": "212612345678",
"local_format": "0612345678",
"international_format": "+212612345678",
"country_prefix": "212",
"country_code": "MA",
"country_name": "Morocco",
"location": "Casablanca",
"carrier": "Maroc Telecom (IAM)",
"line_type": "mobile"
}Lister les contacts
Récupère tous les contacts de votre compte, avec filtrage par mot-clé ou par liste.
| Paramètre | Type | Statut | Description |
|---|---|---|---|
| limit | integer | Optionnel | Résultats par page (1-500, défaut: 100). |
| offset | integer | Optionnel | Décalage de pagination (défaut: 0). |
| q | string | Optionnel | Recherche par nom, téléphone ou email. |
| list_id | string | Optionnel | Filtrer uniquement les membres d'une liste de contacts spécifique. |
curl -X GET "https://api.envoisms.ma/v1/contacts?limit=50&offset=0" \
-H "Authorization: Bearer smr_xxx"{
"data": [
{
"id": "ctc_a2b3...",
"phone": "+212612345678",
"name": "Karim Bennani",
"email": "[email protected]",
"custom1": "VIP",
"custom2": null,
"custom3": null,
"created_at": "2026-05-10T14:20:00Z"
}
],
"limit": 100,
"offset": 0,
"total": 1
}Créer / Modifier un contact
Ajoute un contact ou met à jour les informations d'un contact existant (détection par numéro).
| Champ | Type | Statut | Description |
|---|---|---|---|
| phone | string | Requis | Numéro de téléphone au format E.164. |
| name | string | Optionnel | Nom complet du contact. |
| string | Optionnel | Adresse email. | |
| list_id | string | Optionnel | Associer immédiatement le contact à une liste existante. |
| custom_fields | object | Optionnel | Objet contenant jusqu'à 3 champs personnalisés ("custom1", "custom2", "custom3"). |
curl -X POST "https://api.envoisms.ma/v1/contacts" \
-H "Authorization: Bearer smr_xxx" \
-H "Content-Type: application/json" \
-d '{
"phone": "+212612345678",
"name": "Karim Bennani",
"email": "[email protected]",
"list_id": "lst_f84b..."
}'{
"id": "ctc_a2b3...",
"phone": "+212612345678",
"name": "Karim Bennani",
"email": "[email protected]",
"custom1": "VIP",
"custom2": null,
"custom3": null,
"created_at": "2026-05-10T14:20:00Z"
}Importer des contacts
Importe massivement jusqu'à 5 000 contacts en un seul appel.
| Champ | Type | Statut | Description |
|---|---|---|---|
| contacts | array | Requis | Tableau d'objets contenant "phone", "name" (optionnel) et "email" (optionnel). |
| list_id | string | Optionnel | ID de la liste dans laquelle importer le groupe. |
curl -X POST "https://api.envoisms.ma/v1/contacts/import" \
-H "Authorization: Bearer smr_xxx" \
-H "Content-Type: application/json" \
-d '{
"contacts": [
{
"phone": "+212611111111",
"name": "Karim"
},
{
"phone": "+212622222222",
"name": "Youssef"
}
],
"list_id": "lst_f84b..."
}'{
"imported": 150,
"skipped": 3
}Supprimer un contact
Supprime définitivement un contact à partir de son identifiant unique.
curl -X DELETE "https://api.envoisms.ma/v1/contacts/:id" \
-H "Authorization: Bearer smr_xxx"{
"deleted": true,
"id": "ctc_a2b3..."
}Lister les listes
Récupère toutes les listes de contacts créées pour les campagnes de diffusion.
curl -X GET "https://api.envoisms.ma/v1/contacts/lists" \
-H "Authorization: Bearer smr_xxx"{
"data": [
{
"id": "lst_f84b...",
"name": "Newsletter Clients",
"description": "Clients inscrits à notre lettre d'information",
"count": 1420,
"created_at": "2026-04-15T09:00:00Z"
}
]
}Créer une liste
Crée un nouveau groupe (liste de contacts) vide destiné aux campagnes.
| Champ | Type | Statut | Description |
|---|---|---|---|
| name | string | Requis | Nom de la liste. |
| description | string | Optionnel | Description de la liste. |
curl -X POST "https://api.envoisms.ma/v1/contacts/lists" \
-H "Authorization: Bearer smr_xxx" \
-H "Content-Type: application/json" \
-d '{
"name": "Newsletter Clients",
"description": "Clients inscrits"
}'{
"id": "lst_f84b...",
"name": "Newsletter Clients",
"description": "Clients inscrits",
"count": 0
}Lister les désinscriptions
Récupère la liste des numéros qui se sont désinscrits (STOP) de vos communications.
curl -X GET "https://api.envoisms.ma/v1/optouts" \
-H "Authorization: Bearer smr_xxx"{
"data": [
{
"phone": "+212611111111",
"opted_out_at": "2026-06-01T12:00:00Z"
}
],
"total": 1
}Ajouter une désinscription
Ajoute manuellement un numéro à votre liste de désinscription (blacklist globale).
| Champ | Type | Statut | Description |
|---|---|---|---|
| phone | string | Requis | Numéro de téléphone au format E.164. |
curl -X POST "https://api.envoisms.ma/v1/optouts" \
-H "Authorization: Bearer smr_xxx" \
-H "Content-Type: application/json" \
-d '{}'{
"success": true,
"phone": "+212611111111",
"opted_out_at": "2026-06-01T12:00:00Z"
}Retirer une désinscription
Retire un numéro de la liste de désinscription.
curl -X DELETE "https://api.envoisms.ma/v1/optouts/:phone" \
-H "Authorization: Bearer smr_xxx"{
"deleted": true,
"phone": "+212611111111"
}Lister les consentements
Registre de consentements en ajout seul (loi 09-08, art. 10) : chaque accord et chaque retrait est un événement horodaté avec sa source et sa preuve. La plateforme y écrit elle-même les mots-clés STOP/START reçus sur WhatsApp et SMS et la première conversation ouverte par le client. Ajoutez format=csv pour exporter tout le jeu filtré.
| Paramètre | Type | Statut | Description |
|---|---|---|---|
| phone | string | Optionnel | Numéro E.164 à filtrer. |
| purpose | string | Optionnel | marketing, transactional, otp ou service. |
| status | string | Optionnel | granted ou withdrawn. |
| from | string | Optionnel | Événements enregistrés à partir de cette date ISO 8601. |
| format | string | Optionnel | csv pour télécharger le jeu complet en fichier. |
curl -X GET "https://api.envoisms.ma/v1/consents?limit=50&offset=0" \
-H "Authorization: Bearer smr_xxx"{
"data": [
{
"id": "cns_9f2a...",
"phone": "+212612345678",
"channel": "whatsapp",
"purpose": "marketing",
"status": "withdrawn",
"source": "whatsapp_keyword",
"evidence": { "keyword": "stop", "lang": "fr" },
"recorded_at": "2026-09-05T09:00:00.000Z",
"created_at": "2026-09-05T09:00:00.000Z"
}
],
"total": 1,
"limit": 100,
"offset": 0
}Enregistrer un consentement
Consigne qu'une personne a donné ou retiré son consentement, avec la preuve que vous détenez (texte affiché, URL, IP, référence). recorded_at peut être antidaté pour un consentement importé, jamais dans le futur. Un retrait marketing est placé immédiatement sur la liste d'exclusion.
| Champ | Type | Statut | Description |
|---|---|---|---|
| phone | string | Requis | Numéro au format E.164. |
| purpose | string | Requis | marketing, transactional, otp ou service. |
| status | string | Optionnel | granted (défaut) ou withdrawn. |
| channel | string | Optionnel | whatsapp, sms ou any (défaut). |
| source | string | Optionnel | api (défaut), form, import ou dashboard. Les sources par mot-clé sont réservées à la plateforme. |
| evidence | object | Optionnel | Objet JSON libre (< 4 Ko) : texte présenté, url, ip, référence. |
| recorded_at | string | Optionnel | Date ISO 8601 de l'acte de la personne (défaut : maintenant). |
curl -X POST "https://api.envoisms.ma/v1/consents" \
-H "Authorization: Bearer smr_xxx" \
-H "Content-Type: application/json" \
-d '{}'{
"id": "cns_9f2a...",
"phone": "+212612345678",
"channel": "any",
"purpose": "marketing",
"status": "granted",
"source": "form",
"evidence": { "text": "J'accepte de recevoir les offres par WhatsApp", "url": "https://example.ma/inscription" },
"recorded_at": "2026-09-01T10:00:00.000Z",
"created_at": "2026-09-09T12:00:00.000Z"
}État et historique d'un numéro
La réponse à « montrez-moi le consentement de cette personne » : l'état courant par finalité (dérivé de l'événement le plus récent, unknown sans historique, jamais présumé accordé), la présence sur la liste d'exclusion et l'historique complet.
curl -X GET "https://api.envoisms.ma/v1/consents/:phone" \
-H "Authorization: Bearer smr_xxx"{
"phone": "+212612345678",
"opted_out": true,
"opted_out_at": "2026-09-05T09:00:00Z",
"state": {
"marketing": { "status": "withdrawn", "channel": "whatsapp", "source": "whatsapp_keyword", "recorded_at": "2026-09-05T09:00:00.000Z", "record_id": "cns_3" },
"transactional": { "status": "unknown" },
"otp": { "status": "unknown" },
"service": { "status": "granted", "channel": "whatsapp", "source": "whatsapp_inbound", "recorded_at": "2026-07-01T09:00:00.000Z", "record_id": "cns_1" }
},
"history": []
}Lister les campagnes
Récupère toutes vos campagnes d'envoi programmé ou de diffusion en cours.
curl -X GET "https://api.envoisms.ma/v1/campaigns" \
-H "Authorization: Bearer smr_xxx"{
"data": [
{
"id": "cmp_8d2a...",
"name": "Soldes d'été 2026",
"channel": "sms",
"list_id": "lst_f84b...",
"template_id": null,
"body": "Bonjour {{name}}, profitez de -50% sur toute la collection avec le code ETE50 !",
"sender_id": "SOLDES",
"status": "draft",
"scheduled_at": null,
"created_at": "2026-06-01T12:00:00Z"
}
]
}Créer une campagne
Enregistre une nouvelle campagne en tant que brouillon ou la planifie à une date précise.
| Champ | Type | Statut | Description |
|---|---|---|---|
| name | string | Requis | Nom de la campagne. |
| body | string | Requis | Corps du message. Peut utiliser la variable {{name}}. (L'un des deux requis : body ou template_id.) |
| template_id | string | Requis | Alternativement, ID d'un template approuvé. (L'un des deux requis : body ou template_id.) |
| channel | string | Optionnel | "sms" ou "whatsapp". Par défaut "sms". |
| list_id | string | Optionnel | ID de la liste de contacts destinataire. |
| sender_id | string | Optionnel | Nom d'expéditeur. |
| scheduled_at | string | Optionnel | Date de programmation (ISO 8601). Met le statut en "scheduled". |
| buttons | array | Optionnel | Tableau de boutons WhatsApp interactifs (max 3, type "copy" | "url" | "call"). |
| metadata | object | Optionnel | Métadonnées personnalisées (ex: options de throttling / cadence d'envoi : {"throttling": "50_min"}). |
curl -X POST "https://api.envoisms.ma/v1/campaigns" \
-H "Authorization: Bearer smr_xxx" \
-H "Content-Type: application/json" \
-d '{
"name": "Soldes d'été 2026",
"body": "Bonjour {{name}}, profitez de -50% avec le code ETE50 !",
"channel": "sms",
"list_id": "lst_f84b...",
"sender_id": "SOLDES"
}'{
"id": "cmp_8d2a...",
"status": "draft"
}Détails d'une campagne
Récupère les détails, la planification et le statut d'exécution d'une campagne.
curl -X GET "https://api.envoisms.ma/v1/campaigns/:id" \
-H "Authorization: Bearer smr_xxx"{
"id": "cmp_8d2a...",
"name": "Soldes d'été 2026",
"channel": "sms",
"list_id": "lst_f84b...",
"body": "Bonjour {{name}}, profitez de -50%...",
"sender_id": "SOLDES",
"status": "running",
"total_count": 1420,
"sent_count": 840,
"started_at": "2026-06-15T10:00:00Z",
"created_at": "2026-06-01T12:00:00Z"
}Lancer une campagne
Démarre immédiatement la diffusion d'une campagne de type brouillon vers tous les contacts associés.
curl -X POST "https://api.envoisms.ma/v1/campaigns/:id/send" \
-H "Authorization: Bearer smr_xxx" \
-H "Content-Type: application/json" \
-d '{}'{
"id": "cmp_8d2a...",
"queued": 1420,
"total": 1420
}Lister les webhooks
Récupère la liste de tous vos endpoints de webhooks enregistrés.
curl -X GET "https://api.envoisms.ma/v1/webhooks" \
-H "Authorization: Bearer smr_xxx"{
"data": [
{
"id": "whk_5f2b...",
"url": "https://mon-serveur.ma/api/envoisms-receiver",
"events": [
"message.delivered",
"message.failed"
],
"active": true,
"last_triggered_at": "2026-06-15T09:30:15Z",
"last_status": 200,
"created_at": "2026-05-01T10:00:00Z"
}
]
}Créer un Webhook
Enregistre une URL HTTPS de callback pour recevoir les notifications d'événements.
| Champ | Type | Statut | Description |
|---|---|---|---|
| url | string | Requis | URL cible sécurisée commençant par "https://". |
| events | array | Optionnel | Tableau d'événements (ex: ["message.delivered", "message.failed"]). Les jokers "message.*" et "*" sont acceptés. Défaut: ["message.delivered", "message.failed"]. |
| secret | string | Optionnel | Clé de signature secrète. Si non fournie, elle sera générée automatiquement. |
curl -X POST "https://api.envoisms.ma/v1/webhooks" \
-H "Authorization: Bearer smr_xxx" \
-H "Content-Type: application/json" \
-d '{
"url": "https://mon-serveur.ma/api/envoisms-receiver",
"events": [
"message.delivered",
"message.failed"
]
}'{
"id": "whk_5f2b...",
"url": "https://mon-serveur.ma/api/envoisms-receiver",
"events": [
"message.delivered",
"message.failed"
],
"secret": "whsec_2f8a9e7d...",
"active": true
}Modifier un Webhook
Met à jour la configuration d'un webhook (URL, événements surveillés ou état actif).
| Champ | Type | Statut | Description |
|---|---|---|---|
| url | string | Optionnel | Nouvelle URL HTTPS. |
| events | array | Optionnel | Nouvelle liste d'événements abonnés. |
| active | boolean | Optionnel | Activer (true) ou désactiver (false) le webhook. |
curl -X PATCH "https://api.envoisms.ma/v1/webhooks/:id" \
-H "Authorization: Bearer smr_xxx" \
-H "Content-Type: application/json" \
-d '{
"url": "https://mon-serveur.ma/api/envoisms-receiver-updated",
"active": false
}'{
"updated": true,
"id": "whk_5f2b..."
}Supprimer un Webhook
Désactive et supprime logiquement un endpoint de webhook.
curl -X DELETE "https://api.envoisms.ma/v1/webhooks/:id" \
-H "Authorization: Bearer smr_xxx"{
"deleted": true,
"id": "whk_5f2b..."
}Tester un Webhook
Déclenche un événement de test ("message.test") vers l'URL configurée du webhook.
curl -X POST "https://api.envoisms.ma/v1/webhooks/:id/test" \
-H "Authorization: Bearer smr_xxx" \
-H "Content-Type: application/json" \
-d '{}'{
"queued": true,
"id": "whk_5f2b..."
}Lister les Inbound Webhooks
Récupère tous les endpoints de capture de leads publicitaires configurés sur votre compte.
curl -X GET "https://api.envoisms.ma/v1/inbound-webhooks" \
-H "Authorization: Bearer smr_xxx"{
"webhooks": [
{
"id": "inw_9a8b...",
"name": "Campagne TikTok Ads Casablanca",
"source": "tiktok_ads",
"catch_url": "https://api.envoisms.ma/v1/inbound-webhooks/catch/inw_9a8b...",
"auto_start_funnel": true,
"total_received": 142,
"created_at": "2026-08-20T14:00:00Z"
}
]
}Créer un Inbound Webhook
Génère une URL de capture pour recevoir instantanément les prospects depuis TikTok Lead Ads, Meta Lead Ads, Zapier ou Make.
| Champ | Type | Statut | Description |
|---|---|---|---|
| name | string | Requis | Nom descriptif de la source (ex: "Facebook Lead Gen Promotion Été"). |
| source | string | Optionnel | Identifiant de source ("tiktok_ads", "meta_leads", "google_forms", "custom"). Défaut: "custom". |
| auto_start_funnel | boolean | Optionnel | Si true, déclenche immédiatement le funnel de qualification WhatsApp AtlasAI™ dès réception du lead. |
curl -X POST "https://api.envoisms.ma/v1/inbound-webhooks" \
-H "Authorization: Bearer smr_xxx" \
-H "Content-Type: application/json" \
-d '{}'{
"id": "inw_9a8b...",
"name": "Campagne TikTok Ads Casablanca",
"catch_url": "https://api.envoisms.ma/v1/inbound-webhooks/catch/inw_9a8b...",
"auto_start_funnel": true
}Lister les clés API
Récupère la liste de toutes vos clés d'API actives ou révoquées.
curl -X GET "https://api.envoisms.ma/v1/api-keys" \
-H "Authorization: Bearer smr_xxx"{
"data": [
{
"id": "key_e84c...",
"name": "Production Server",
"key_prefix": "smr_a8f7d6c5",
"sandbox": false,
"ip_whitelist": [
"196.200.1.4"
],
"rate_limit": 100,
"permissions": [
"send",
"verify",
"status"
],
"active": true,
"last_used_at": "2026-06-15T10:30:00Z",
"created_at": "2026-05-01T08:00:00Z"
}
]
}Créer une clé API
Génère un nouveau jeton d'API sécurisé avec des permissions et restrictions spécifiques.
| Champ | Type | Statut | Description |
|---|---|---|---|
| name | string | Optionnel | Libellé pour identifier la clé (défaut: "API key"). |
| permissions | array | Optionnel | Droits accordés (send, verify, status, balance, contacts, campaigns, webhooks, billing, analytics, api_keys, optouts). |
| ip_whitelist | array | Optionnel | Liste d'adresses IP autorisées à exécuter des requêtes avec cette clé. |
| rate_limit | integer | Optionnel | Limite maximale de requêtes/min (de 10 à 2000). Défaut selon le palier : Starter 60, Business 180, Pro 500, Enterprise 1 200. |
| sandbox | boolean | Optionnel | true génère une clé de test préfixée env_test_. Les requêtes sont validées et enregistrées, mais aucun message n'est réellement envoyé et rien n'est facturé. Le mode d'une clé est définitif : pour changer, créez une nouvelle clé. |
curl -X POST "https://api.envoisms.ma/v1/api-keys" \
-H "Authorization: Bearer smr_xxx" \
-H "Content-Type: application/json" \
-d '{
"name": "Production Server",
"permissions": [
"send",
"verify",
"status"
],
"ip_whitelist": [
"196.200.1.4"
],
"rate_limit": 100
}'{
"id": "key_e84c...",
"name": "Production Server",
"key_prefix": "smr_a8f7d6c5",
"api_key": "smr_a8f7d6c5b4a3...",
"sandbox": false,
"permissions": [
"send",
"verify",
"status"
],
"rate_limit": 100,
"warning": "The full API key is shown once. Store it securely."
}Modifier une clé API
Met à jour les permissions, restrictions IP ou l'état d'activation d'une clé API.
| Champ | Type | Statut | Description |
|---|---|---|---|
| name | string | Optionnel | Nouveau nom. |
| permissions | array | Optionnel | Nouvelle liste de permissions. |
| ip_whitelist | array | Optionnel | Nouvelle liste d'adresses IP autorisées. |
| rate_limit | integer | Optionnel | Nouvelle limite de débit par minute. |
| active | boolean | Optionnel | Activer ou suspendre la clé. |
curl -X PATCH "https://api.envoisms.ma/v1/api-keys/:id" \
-H "Authorization: Bearer smr_xxx" \
-H "Content-Type: application/json" \
-d '{
"name": "Backup Server",
"active": true
}'{
"updated": true,
"id": "key_e84c..."
}Révoquer une clé API
Révoque définitivement une clé API pour l'empêcher d'authentifier les requêtes.
curl -X DELETE "https://api.envoisms.ma/v1/api-keys/:id" \
-H "Authorization: Bearer smr_xxx"{
"revoked": true,
"id": "key_e84c..."
}Profil WhatsApp Business
Consulte les informations publiques de votre profil WhatsApp Business vérifié (nom, photo, description, adresse, site web).
curl -X GET "https://api.envoisms.ma/v1/whatsapp/profile" \
-H "Authorization: Bearer smr_xxx"{
"about": "Service client officiel EnvoiSMS.ma",
"address": "Casablanca, Maroc",
"description": "Infrastructure de messagerie programmable pour les entreprises au Maroc.",
"email": "[email protected]",
"websites": [
"https://votremarque.ma"
],
"profile_picture_url": "https://pps.whatsapp.net/v/..."
}Mettre à jour le profil WhatsApp
Met à jour les informations visibles par vos clients sur votre profil WhatsApp Business officiel.
| Champ | Type | Statut | Description |
|---|---|---|---|
| about | string | Optionnel | Statut textuel court (max 139 caractères). |
| description | string | Optionnel | Description détaillée de votre entreprise (max 512 caractères). |
| address | string | Optionnel | Adresse physique de votre siège ou boutique. |
| string | Optionnel | Adresse email de contact client. | |
| websites | array | Optionnel | Tableau contenant jusqu'à 2 URLs de votre site web. |
curl -X POST "https://api.envoisms.ma/v1/whatsapp/profile" \
-H "Authorization: Bearer smr_xxx" \
-H "Content-Type: application/json" \
-d '{}'{
"success": true,
"updated_at": "2026-08-30T10:00:00Z"
}Lister les conversations
Récupère la liste des discussions WhatsApp avec suivi en temps réel de la fenêtre de service 24h et statut du bot.
| Paramètre | Type | Statut | Description |
|---|---|---|---|
| limit | integer | Optionnel | Nombre maximum de conversations à retourner (défaut 30, max 100). |
| bot_status | string | Optionnel | Filtrer par statut bot ("active" ou "muted"). |
curl -X GET "https://api.envoisms.ma/v1/conversations?limit=50&offset=0" \
-H "Authorization: Bearer smr_xxx"{
"conversations": [
{
"phone": "+212612345678",
"contact_name": "Yassine Alami",
"last_message": "Bonjour, je souhaite visiter l'appartement témoin",
"last_message_at": "2026-08-30T11:42:00Z",
"unread_count": 1,
"bot_muted": true,
"can_reply_free": true,
"window_expires_at": "2026-08-31T11:42:00Z"
}
],
"total": 1
}Historique d'une conversation
Récupère le fil complet des messages échangés avec un contact WhatsApp.
curl -X GET "https://api.envoisms.ma/v1/conversations/:phone" \
-H "Authorization: Bearer smr_xxx"{
"phone": "+212612345678",
"contact_name": "Yassine Alami",
"bot_muted": true,
"messages": [
{
"id": "msg_01J...",
"direction": "inbound",
"body": "Je souhaite des informations sur le projet Casablanca Marina",
"type": "text",
"timestamp": "2026-08-30T11:40:00Z"
},
{
"id": "msg_02J...",
"direction": "outbound",
"body": "Bonjour Yassine ! Quel type de bien recherchez-vous ?",
"type": "interactive",
"timestamp": "2026-08-30T11:40:05Z"
}
]
}Répondre en direct (Live Chat)
Envoie une réponse d'agent et met le bot en pause sur cette conversation (réactivation via POST /v1/conversations/:phone/toggle-bot). Un message WhatsApp libre exige que le contact ait écrit dans les dernières 24 h (sinon 400 OUT_OF_24H_WINDOW) ; un modèle approuvé peut être envoyé à tout moment.
| Champ | Type | Statut | Description |
|---|---|---|---|
| message | string | Optionnel | Texte de la réponse (ou légende du média joint). Obligatoire sans modèle ni média. |
| channel | string | Optionnel | "whatsapp" (défaut) ou "sms". |
| template_name | string | Optionnel | Modèle WhatsApp approuvé à envoyer à la place d'un texte libre (avec template_language et variables). |
| media_url | string | Optionnel | URL https (ou data URI) d'une image ou d'un PDF à joindre ; media_type "image" ou "document". |
| phone_number_id | string | Optionnel | Numéro WhatsApp connecté qui répond. |
curl -X POST "https://api.envoisms.ma/v1/conversations/:phone/messages" \
-H "Authorization: Bearer smr_xxx" \
-H "Content-Type: application/json" \
-d '{}'{
"ok": true,
"message": {
"id": "msg_03J...",
"phone": "212612345678",
"role": "agent",
"message": "Bonjour Yassine, votre visite est confirmée.",
"status": "sent",
"channel": "whatsapp",
"created_at": "2026-08-30T11:45:00Z"
}
}Lister les leads qualifiés
Récupère les prospects qualifiés automatiquement par AtlasAI™ avec leur score BANT et leur statut CRM.
| Paramètre | Type | Statut | Description |
|---|---|---|---|
| stage | string | Optionnel | Filtrer par étape ("new", "contacted", "meeting_scheduled", "closed_won", "closed_lost"). |
| min_score | integer | Optionnel | Score BANT minimum (0 à 100). |
- Les leads sont qualifiés par AtlasAI™ Conversational Engine, AtlasAI™ Voice et AtlasAI™ Vision directement via les échanges WhatsApp entrants. Ces moteurs IA sont intégrés nativement à WABA et ne sont pas proposés comme API autonomes externes.
curl -X GET "https://api.envoisms.ma/v1/qualified-leads?limit=50&offset=0" \
-H "Authorization: Bearer smr_xxx"{
"leads": [
{
"id": "lead_01J...",
"phone": "+212612345678",
"name": "Dr. Benjelloun",
"preset": "medical_equipment",
"bant_score": 85,
"is_hot": true,
"breakdown": {
"budget": 25,
"authority": 25,
"need": 20,
"timeline": 15
},
"intent": "Échographe Doppler pour nouveau cabinet",
"stage": "meeting_scheduled",
"created_at": "2026-08-30T09:15:00Z"
}
],
"total": 1
}Mettre à jour le statut du lead
Met à jour le statut dans le pipeline commercial d'un prospect qualifié.
| Champ | Type | Statut | Description |
|---|---|---|---|
| status | string | Requis | "new" | "contacted" | "meeting_scheduled" | "closed_won" | "closed_lost" |
| notes | string | Optionnel | Notes internes de suivi commercial. |
curl -X POST "https://api.envoisms.ma/v1/qualified-leads/:id/status" \
-H "Authorization: Bearer smr_xxx" \
-H "Content-Type: application/json" \
-d '{}'{
"success": true,
"lead_id": "lead_01J...",
"stage": "meeting_scheduled"
}Consulter le solde
Consulte le solde disponible en Dirhams Marocains (MAD) ainsi que la devise et le forfait actif.
curl -X GET "https://api.envoisms.ma/v1/billing/balance" \
-H "Authorization: Bearer smr_xxx"{
"balance_mad": 1492.5,
"currency": "MAD",
"plan": "croissance"
}Statistiques d'usage
Récupère des métriques clés sur vos envois (volumes totaux, taux de délivrabilité, et coûts facturés).
curl -X GET "https://api.envoisms.ma/v1/analytics" \
-H "Authorization: Bearer smr_xxx"{
"summary": {
"total": 12840,
"delivered": 12570,
"delivery_rate": 97.9,
"cost_mad": 2663.1
}
}Guide Webhooks
Webhooks DLR & Accusés de réception SMS en temps réel
EnvoiSMS.ma poste des événements JSON à l'URL HTTPS de votre serveur avec un en-tête X-EnvoiSMS.ma-Signature pour authentifier chaque rapport de livraison SMS.
message.sentLe message a été accepté par le réseau de l'opérateur.
message.deliveredLe message a été remis avec succès au destinataire (SMS ou WhatsApp).
message.readLe message WhatsApp a été ouvert et lu par le destinataire (deux coches bleues).
message.failedÉchec de livraison côté route ou soumission (rejet, erreur d'acheminement).
message.undeliverableLe réseau a confirmé que le message ne peut pas être remis (numéro inexistant, expiré en file opérateur).
message.fallbackCascade (cascade: true) : le message passe au canal suivant (ex. WhatsApp → SMS), parce que le premier a refusé ou signalé l'échec du message, ou n'a pas remis d'accusé de réception dans le délai cascade_timeout. channel donne le nouveau canal, previous_channel l'ancien, error_code et error_message la raison.
message.inboundUn destinataire a répondu à l'un de vos messages SMS ou WhatsApp (inbound).
message.flow_responseUn utilisateur a validé et soumis un formulaire natif WhatsApp Flow.
message.locationUn utilisateur a partagé sa position géographique GPS sur WhatsApp.
lead.qualifiedAtlasAI™ a terminé la qualification BANT d'un prospect sur WhatsApp.
contact.optoutUn destinataire s'est désabonné (mot-clé STOP ou assimilé).
import crypto from 'node:crypto';
// La signature arrive dans le header X-EnvoiSMS-Signature
// (format "sha256=<hex>"), l'événement dans X-EnvoiSMS-Event.
// Corps livré : { "event": "...", "data": { ... }, "timestamp": "..." }
export function verifySignature(body: string, sig: string, secret: string) {
const hmac = crypto.createHmac('sha256', secret)
.update(body)
.digest('hex');
return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from('sha256=' + hmac));
} OpenAPI Spec Erreurs
Codes d'erreur et statuts de livraison de l'API SMS
Toutes les erreurs de l'API retournent un code HTTP approprié (4xx ou 5xx) ainsi qu'une enveloppe JSON prévisible contenant le code de l'erreur et une description claire.
{
"error": {
"code": "INVALID_PHONE",
"message": "to must be E.164 format, for example +212612345678",
"docs": "https://envoisms.ma/docs#errors"
}
}UNAUTHORIZEDClé API manquante ou invalide.
INSUFFICIENT_BALANCESolde insuffisant pour effectuer l'envoi.
INVALID_PHONELe numéro de téléphone n'est pas au format E.164.
WHATSAPP_NOT_CONNECTEDAucun numéro WhatsApp Business connecté : connectez le vôtre dans le tableau de bord pour envoyer sur WhatsApp.
OUT_OF_24H_WINDOWMessage WhatsApp libre vers un contact qui ne vous a pas écrit depuis plus de 24 h. Envoyez un modèle approuvé. Rien n'a été débité.
WHATSAPP_ONLY_FIELDUn champ réservé à WhatsApp (reaction, contacts, location, sticker, context) a été envoyé sur un autre canal.
CONFLICTING_MESSAGE_TYPESUn message porte un seul type de contenu : reaction, contacts, location ou sticker ne se combinent avec rien d'autre.
CASCADE_NOT_SUPPORTEDcascade est impossible avec une réaction, des contacts, une localisation ou un sticker : pas d'équivalent SMS.
INVALID_REACTIONreaction doit contenir le message_id (wamid) visé et un seul emoji (ou une chaîne vide pour retirer la réaction).
INVALID_CONTEXTcontext doit contenir le message_id (wamid) du message cité, et ne se combine pas avec reaction.
INVALID_CONTACTScontacts doit être un tableau de 1 à 20 fiches, chacune avec name.formatted_name ; le message indique la fiche fautive.
INVALID_LOCATIONlocation exige latitude (-90 à 90) et longitude (-180 à 180) numériques ; name et address sont facultatifs.
INVALID_MEDIAUn média WhatsApp prend soit "id", soit "link" (URL https), jamais les deux ; pas de légende sur audio et sticker.
META_WINDOW_EXPIREDÉchec WhatsApp (131047) : la fenêtre de service de 24 h était fermée. Envoyez un modèle approuvé ou un SMS.
META_UNDELIVERABLEÉchec WhatsApp (131026) : le numéro n'est pas joignable sur WhatsApp. Pas de nouvel essai automatique.
META_MARKETING_LIMITÉchec WhatsApp (131049) : limite de messages marketing par destinataire. Ne renvoyez pas tout de suite.
META_RATE_LIMITEDÉchec WhatsApp (130429 débit du numéro, ou 131056 pour META_PAIR_RATE_LIMITED : trop de messages vers ce contact) après 3 essais automatiques.
META_POLICY_BLOCKEDÉchec WhatsApp (368 ; aussi META_ACCOUNT_LOCKED 131031, META_PAYMENT_ISSUE 131042, META_PHONE_NOT_REGISTERED 133010) : numéro ou compte émetteur restreint. Vérifiez WhatsApp Manager.
META_TEMPLATE_NOT_FOUNDÉchec WhatsApp (132001 ; aussi META_TEMPLATE_PARAM_MISMATCH 132000, META_TEMPLATE_PARAM_FORMAT 132012) : modèle introuvable dans cette langue ou variables incorrectes.
RATE_LIMITEDLimite de requêtes par minute dépassée. Respectez l'en-tête Retry-After.
INVALID_IDEMPOTENCY_KEYL'en-tête Idempotency-Key dépasse 255 caractères.
IDEMPOTENCY_IN_FLIGHTLa requête originale portant cette Idempotency-Key est encore en cours. Réessayez dans un instant.
IDEMPOTENCY_KEY_REUSEDCette Idempotency-Key a déjà été utilisée avec un corps de requête différent. Utilisez une nouvelle clé.
INVALID_CHANNELLe canal demandé n'existe pas. Canaux valides : sms, whatsapp, telegram, voice, rcs.
CHANNEL_NOT_CONFIGUREDLe canal demandé n'est pas disponible actuellement sur la plateforme.
CHANNEL_DISABLEDLe canal demandé est désactivé sur la plateforme.
FORBIDDENLa clé API n'a pas la permission requise pour cette action, ou le compte est suspendu.
SENDER_ID_TOO_LONGLe Sender ID dépasse la limite de 11 caractères.
SENDER_ID_INVALIDLe Sender ID contient des caractères non autorisés.
SENDER_ID_NOT_APPROVEDLe Sender ID n'a pas encore été approuvé par les opérateurs.
SENDER_ID_PENDINGLe Sender ID est en cours d'approbation.
SENDER_ID_REJECTEDLe Sender ID a été rejeté par les opérateurs.
MISSING_FIELDUn champ obligatoire est manquant dans la requête.
CASCADE_TIMEOUTLe premier canal a expiré, basculement vers le canal secondaire (cascade).
UPSTREAM_ERRORErreur de livraison au niveau de l'opérateur ou de la passerelle.
OPTED_OUTLe numéro a refusé vos communications (STOP). Envoi interdit.
SPAM_OR_PHISHING_DETECTEDLe message contient un lien identifié comme hameçonnage ou spam. Envoi refusé.
CONTENT_BLOCKEDLe contenu du message a été bloqué par le contrôle anti-abus et le compte est suspendu en attente de revue. Contactez le support.
INVALID_CODELe code OTP soumis est incorrect. Le message d'erreur indique le nombre de tentatives restantes.
EXPIRED_CODELe code OTP a expiré. Demandez un nouveau code via /v1/verify/send.
MAX_ATTEMPTSNombre maximal de tentatives de vérification dépassé. La session est clôturée.
STRIPE_ERRORLa page de paiement n'a pas pu être créée de notre côté. Aucun débit n'a eu lieu — réessayez dans un instant.
INTERNAL_ERRORErreur interne de notre côté. Réessayez ; contactez le support si le problème persiste.
INVALID_PURPOSEpurpose doit être marketing, transactional, otp ou service.
INVALID_STATUSstatus doit être granted ou withdrawn.
INVALID_CHANNELchannel doit être whatsapp, sms ou any.
INVALID_SOURCEsource doit être api, form, import ou dashboard.
INVALID_EVIDENCEevidence doit être un objet JSON de moins de 4 Ko.
INVALID_DATEUne date n'est pas au format ISO 8601, ou recorded_at est dans le futur.
Limites
Limites de débit et quotas d'envoi SMS en production
60 à 1 200 requêtes / minute par clé selon le palier (Starter 60, Business 180, Pro 500, Enterprise 1 200), relevable sur demande.
1600 caractères maximum par message.
Jusqu'à 10 000 messages par appel API.
90 jours pour les rapports détaillés.