La référence développeur
Documentation API
Créez des messages depuis votre application et recevez leurs changements d’état. Retrouvez ici les formats, les limites et les précautions utiles.
Démarrer avec une clé live
Créez une clé dans API et webhooks. L’origine API est https://smsmarocpro.com. Définissez SMSMAROCPRO_API_ORIGIN=https://smsmarocpro.com, sans barre finale, dans votre configuration serveur.
Un forfait actif avec du crédit disponible permet les envois via Web et API, dans les limites du compte. Aucune approbation par campagne n’est nécessaire. Une réponse 202 confirme l’enregistrement, pas la livraison.
Premier message
POST /v1/messages
Authorization: Bearer <clé live>
Content-Type: application/json
Idempotency-Key: premier_message_2026_001
{"to":"+2126XXXXXXXX","trafficClass":"transactional","text":"Bonjour depuis votre application."}Création : HTTP 202 avec un identifiant msg_…. Remplacez le numéro masqué par un destinataire autorisé. Les exemples cURL, Node.js, Python et PHP sont disponibles dans votre espace.
Clés et autorisations
Envoyez Authorization: Bearer VOTRE_CLÉ à chaque appel, depuis votre serveur. Jamais de clé dans une URL, du JavaScript public ou un dépôt Git. Le nom d’une clé est un simple libellé, pas un niveau de permission.
Le propriétaire du compte crée et révoque les clés. Cinq clés actives au maximum sont affichées ; les anciennes clés révoquées sont conservées pour l’audit, pas dans la liste courante. Choisissez 30, 90, 180 ou 365 jours, ou aucune expiration. Le secret n’est affiché qu’une fois dans une fenêtre à copier avant de la fermer.
| Droit live | Usage |
|---|---|
messages:write / messages:read | Créer / consulter un message. |
campaigns:write / campaigns:read | Créer, suspendre, reprendre, annuler / consulter une campagne. |
contacts:write / contacts:read | Importer et consulter des listes de destinataires chiffrées. |
suppressions:write / suppressions:read | Bloquer / consulter les destinataires exclus. |
usage:read | Consulter le volume disponible et les limites. |
Choisissez uniquement les droits nécessaires. Pour renouveler une clé : créez la suivante, déployez-la dans votre application, vérifiez un appel puis révoquez l’ancienne. Une clé perdue ne peut pas être récupérée.
Créer et consulter un message réel
curl --request POST "$SMSMAROCPRO_API_ORIGIN/v1/messages" \
--header "Authorization: Bearer $SMSMAROCPRO_API_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: message_commande_2026_001" \
--data '{"to":"+2126XXXXXXXX","trafficClass":"transactional","text":"Votre commande est prête."}'Remplacez le numéro masqué par un destinataire marocain autorisé. Ce parcours accepte le trafic transactional. to, trafficClass et text sont obligatoires.
Options : sendAt pour une date future ISO 8601 avec fuseau, jusqu’à 30 jours ; sendBy pour la dernière date d’envoi acceptable. Par défaut, le message est disponible immédiatement et expire après une heure. sendBy doit suivre sendAt de sept jours au maximum.
HTTP 202 retourne un identifiant msg_…, un état et quotaUnits. Consultez ensuite GET /v1/messages/{id}. La lecture retourne l’état et l’analyse disponible, sans numéro ni contenu du SMS.
Arrêter un envoi accepté
Arrêter votre application ne retire pas les SMS déjà acceptés. Utilisez POST /v1/messages/{id}/cancel avec messages:write, une nouvelle clé d’idempotence persistée et le corps {}. HTTP 200 et l’état cancelled confirment l’annulation d’un message individuel API sans aucune tentative.
HTTP 409 MESSAGE_NOT_CANCELLABLE signifie qu’il ne peut pas être rappelé par cette action. Consultez son identifiant original et conservez les résultats incertains. En cas de réponse perdue, relisez ce même SMS ou rejouez la même demande d’annulation ; ne créez pas un nouvel envoi. L’annulation ne consomme pas de quota et ne libère pas les réservations d’autres SMS. Les campagnes utilisent leur propre action d’annulation.
La notification existante message.failed signale aussi une annulation avant tentative. Lisez toujours la ressource pour distinguer cancelled, expired et failed ; ce signal ne prouve ni échec opérateur ni consommation.
Éviter les doublons
Chaque création et contrôle de campagne exige un Idempotency-Key : 16 à 100 lettres, chiffres, tirets ou caractères de soulignement. Réutilisez la même valeur et exactement le même contenu lors d’un nouvel essai après une coupure réseau. Un rejeu exact retourne HTTP 200 avec le même identifiant, même après la date limite d’envoi ; il ne relance aucun SMS. Un contenu différent avec la même clé retourne 409. Utilisez une nouvelle valeur pour une nouvelle opération.
Préparer et piloter une campagne
POST /v1/campaigns
Authorization: Bearer <clé live>
Content-Type: application/json
Idempotency-Key: campagne_rappel_2026_001
{"name":"Rappel de rendez-vous","text":"Votre rendez-vous approche.","to":["+2126XXXXXXXX","+2127XXXXXXXX"]}Une campagne créée directement avec to est limitée à 20 destinataires et ses messages entrent dans la file dès la création, ou à la date sendAt choisie. Pour une liste plus grande, utilisez le parcours d’import et de préparation ci-dessous, dans les limites de votre forfait. Les destinataires et le message sont figés à la création : modifier une liste de contacts ne modifie pas les messages déjà préparés. Les champs de programmation sendAt et sendBy suivent les mêmes règles que pour un message.
Vous choisissez le nom, le texte, les destinataires et la programmation dans le schéma documenté. Les mêmes règles de consentement, exclusion et quota s’appliquent que dans le tableau de bord. Une réponse 202 confirme la mise en file, pas la livraison. Les destinataires sont traités progressivement, dans l’ordre de la campagne ; plusieurs appels API ne permettent pas de contourner les limites du compte.
Depuis une liste enregistrée
Commencez avec POST /v1/contact-imports, ajoutez les lignes avec POST /v1/contact-imports/{id}/batches, puis validez avec POST /v1/contact-imports/{id}/commit. La lecture GET /v1/contact-imports/{id} permet de reprendre au dernier offset confirmé. Les formats de colonnes et de lignes sont décrits dans le contrat OpenAPI.
La validation retourne contactListId et contactListVersion. Envoyez ensuite POST /v1/campaigns avec name, text, contactListId et expectedListVersion. Cette préparation ne transmet aucun SMS. Attendez l’état prepared, puis appelez POST /v1/campaigns/{id}/launch avec la révision courante. Une liste modifiée entre-temps doit être préparée à nouveau.
GET /v1/campaigns?limit=50 liste les campagnes, de la plus récente à la plus ancienne, sans les campagnes supprimées. La limite va de 1 à 100, avec 50 par défaut. Pour continuer, passez le nextCursor retourné dans cursor jusqu’à obtenir null. GET /v1/campaigns/{id} retourne l’état, la révision et les totaux.
| Opération | Effet |
|---|---|
POST /v1/campaigns/{id}/pause | Suspend les messages dont la transmission n’a pas commencé. |
POST /v1/campaigns/{id}/resume | Reprend la même campagne aux destinataires encore en attente. Les messages déjà soumis, envoyés ou reçus ne sont pas renvoyés. |
POST /v1/campaigns/{id}/cancel | Annule les messages en attente ; un SMS déjà transmis ne peut pas être rappelé. |
Envoyez {"expectedRevision":1} et une nouvelle clé d’idempotence pour chaque opération. Reprenez la révision de la dernière lecture. Une révision ancienne retourne 409 : relisez l’état avant de décider de réessayer.
Les opérations sur une grande campagne sont progressives. Si operationPending vaut true, lisez la campagne par son identifiant jusqu’à false avant l’opération suivante. Une opération simultanée retourne 409 CAMPAIGN_TRANSITION_PENDING ; elle ne crée aucun nouvel envoi.
Lorsque le maximum quotidien est atteint, la campagne reste en attente et reprend automatiquement après le renouvellement de cette limite, si son délai sendBy et son forfait restent valides. Pour une campagne mise en pause, utilisez resume sur son identifiant. Créer une nouvelle campagne avec la même liste prépare un nouvel envoi à toute la liste. Les messages expirés, annulés ou au résultat incertain ne sont pas automatiquement renvoyés.
Suivre le volume et respecter les exclusions
GET /v1/usage expose les compteurs mensuels et l’objet daily : fenêtre de 24 heures propre au compte, limite, consommation, réservations et solde restant. Elle commence à la première réservation d’envoi, puis se renouvelle toutes les 24 heures ; un renouvellement de forfait ne la réinitialise pas. Avant le premier envoi, windowStarted vaut false et windowStart/windowEnd sont nuls. Les horaires sont affichés à l’heure du Maroc. Un objet quotidien nul indique une information indisponible. Deux segments consomment deux unités. Ce relevé ne réserve pas votre prochain envoi et une allocation mensuelle ne supprime pas le maximum quotidien.
POST /v1/suppressions avec une clé d’idempotence et {"to":"+2126XXXXXXXX","reason":"opt_out"} exclut un destinataire. Motifs acceptés : opt_out, complaint, customer_request. GET /v1/suppressions permet de consulter les exclusions sans exposer les numéros complets.
L’exclusion est vérifiée à la préparation et de nouveau juste avant l’envoi. Ne réessayez pas automatiquement un destinataire exclu. La réception automatique des réponses STOP n’est pas proposée : transmettez les demandes reçues à la liste d’exclusion.
Comprendre une erreur et réessayer correctement
Les erreurs utilisent application/problem+json, avec code, status et un identifiant de requête. Conservez cet identifiant pour le support, jamais le secret ni le corps d’un SMS.
| HTTP | Action |
|---|---|
| 400 / 413 / 415 / 422 | Corriger les paramètres, la taille ou le format JSON. Ne pas réessayer à l’identique. |
| 401 | Vérifier la clé, son expiration, sa révocation et ses droits. Une autorisation manquante peut aussi retourner 401. |
| 403 | Vérifier le compte, l’abonnement, les autorisations et les éventuels avertissements de contenu. Une nouvelle clé ne contourne pas ces conditions. |
| 404 | Vérifier l’identifiant et le compte propriétaire. |
| 409 | Lire le code : conflit d’idempotence, révision obsolète, exclusion ou capacité de stockage atteinte. |
| 429 | Attendre le nombre de secondes de Retry-After. |
| 503 | Réessayer progressivement avec la même clé d’idempotence, sans recréer une opération. |
Les codes MESSAGE_CONTENT_BLOCKED et LIVE_CAMPAIGN_CONTENT_BLOCKED indiquent un contenu bloqué après examen. Ne réessayez pas ce texte automatiquement : consultez l’avertissement dans votre compte, corrigez le contenu ou contactez le support depuis la plateforme.
Débit actuel : 120 appels HTTP par minute par clé live. Ce plafond limite les requêtes, pas le débit d’envoi des SMS. Les requêtes JSON sont limitées à 64 Kio.
Les demandes acceptées sont mises en attente puis traitées progressivement. Une forte activité peut allonger l’attente et faire expirer sendBy. Choisissez ce délai selon votre volume et votre besoin, puis suivez les états par identifiant ou webhook. Une réponse HTTP 202 ne garantit ni délai ni livraison ; un résultat incertain doit être vérifié sans recréer le message.
SMS standard : 160 unités conseillées, 306 maximum. Unicode : 70 conseillées, 132 maximum. Certains caractères GSM occupent deux unités. Utilisez le testeur d’analyse avant l’envoi ; les caractères non pris en charge sont refusés.
Vérifier le texte final et la disponibilité
La politique ma_sms_content_v1 refuse les schémas http:// et https:// dans le texte du SMS, ainsi que les suites maximales de exactement cinq ou six chiffres décimaux Unicode. Les appels API et les webhooks restent en HTTPS. Un lien comme exemple.ma/page ou une référence volontairement groupée comme 123-456 respecte cette compatibilité, sans garantir la réception.
HTTP 422 MESSAGE_URL_SCHEME_NOT_ALLOWED a priorité sur MESSAGE_DIGIT_RUN_NOT_ALLOWED si les deux règles échouent. Corrigez le texte explicitement : aucun contenu n’est réécrit. Les variables et suffixes sont vérifiés sur chaque message final d’une nouvelle préparation. Les opérations déjà acceptées gardent leur texte et leur rejeu exact.
Pour un OTP, votre application conserve la génération, l’expiration et la vérification du code. Adaptez sa saisie au groupement choisi ; testez l’autoremplissage et recalculez les segments après ajout de tirets.
GET /v1/availability?trafficClass=transactional&requiredUnits=1, avec usage:read, retourne un état indicatif ready / unavailable, une raison et operatorActionRequired. Ce n’est pas une réservation. Pour un message immédiat, requireReady: true permet de refuser avant acceptation avec HTTP 503 DISPATCH_UNAVAILABLE. Sans cette option, la mise en file asynchrone reste inchangée.
Respectez Retry-After lorsqu’il est fourni. Suspendez les nouveaux envois lors d’une intervention requise, conservez les identifiants acceptés et limitez les opérations en vol. Plusieurs réponses Pending ne sont pas des échecs terminaux. Le Guide LLM décrit le contrat complet et le suivi des résultats dans l’ordre de votre activité.
Les lectures d’un message distinguent dispatch.submissionAttempts, dispatch.confirmedTransmissions et dispatch.outcome. Une expiration sans soumission n’est pas une tentative physique. Un état failed ne prouve pas un débit ou une absence de transmission : consultez les consommations et réservations de GET /v1/usage.
Un message accepté n’a pas encore de réception confirmée
| État | Signification |
|---|---|
ready / scheduled / accepted | En attente, programmé ou retenu en préparation. |
dispatching / submitted | Transmission en cours ou soumise au service d’envoi ; la livraison n’est pas encore confirmée. |
sent | Envoyé : l’envoi est confirmé ; la réception ne l’est pas encore. |
delivered | Réception confirmée : un accusé confirme la réception, sans prouver la lecture du message. |
reconciling / manual_review | Résultat incertain en cours de vérification ; ne pas renvoyer automatiquement. |
failed / expired / cancelled | Échec, délai dépassé ou annulation. |
Les SMS partent de numéros de téléphone variables, pas d’un Sender ID de marque fixe. Les accusés de livraison dépendent du réseau et peuvent arriver plus tard.
Recevoir les nouvelles sans interroger l’API
L’API est une demande de votre application à SMSmarocPro. Un webhook est le trajet inverse : SMSmarocPro appelle votre application lorsqu’un message change d’état.
- Préparez une URL HTTPS publique acceptant POST, sans redirection, identifiants dans l’URL ni port personnalisé.
- Dans votre espace développeur, ajoutez cette destination et choisissez un à cinq événements.
- Copiez le secret affiché une seule fois dans la configuration serveur du récepteur.
- Validez la signature de
endpoint.testet répondez 2xx. La destination devient active. Si le secret n’était pas encore configuré au premier essai, la notification est réessayée. - Traitez chaque événement une seule fois, puis consultez le message par son identifiant si nécessaire.
Après avoir enregistré le secret, déployez ou redémarrez votre récepteur pour charger sa configuration. Le test et ses nouvelles tentatives sont automatiques ; le statut se met à jour dans votre espace après la réponse 2xx. « Actualiser les accès » relit seulement les informations affichées : ce bouton ne relance pas la vérification.
Événements : message.submitted, message.sent, message.delivered, message.failed, message.reconciling. Les notifications contiennent un identifiant de ressource, pas le numéro ni le texte. Exemple de corps :
{"id":"evt_…","type":"message.delivered","occurredAt":"2026-09-05T10:00:00.000Z","data":{"id":"msg_…"}}Valider la signature en Node.js
Le HMAC SHA-256 porte sur les octets bruts du corps, suivis du timestamp, sans séparateur. Le secret est utilisé tel qu’affiché, sans décodage Base64.
import { createHmac, timingSafeEqual } from "node:crypto";
// Conserver les octets reçus AVANT tout parseur JSON.
export function verifyWebhook(rawBody, headers, secret) {
const timestamp = headers.get("x-smsmarocpro-timestamp") ?? "";
const signature = headers.get("x-smsmarocpro-signature") ?? "";
if (!/^\d{10,11}$/.test(timestamp) || !/^v1=[a-f0-9]{64}$/.test(signature)) return false;
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
const expected = createHmac("sha256", secret)
.update(rawBody).update(timestamp).digest();
return timingSafeEqual(expected, Buffer.from(signature.slice(3), "hex"));
}
// Après validation : parser le JSON, vérifier que body.id correspond à
// x-smsmarocpro-event-id et dédupliquer durablement cet identifiant.
// Accepter dans une file durable puis répondre 2xx rapidement.
// Répondre 2xx également aux doublons déjà acceptés.Les trois en-têtes sont X-SMSMarocPro-Event-Id, X-SMSMarocPro-Timestamp et X-SMSMarocPro-Signature. Tolérance de l’exemple : cinq minutes. Synchronisez l’horloge de votre serveur.
La livraison attend au maximum dix secondes. En cas d’échec, les essais sont espacés progressivement, à partir de 30 secondes ; huit tentatives maximum, puis la destination est désactivée. Les événements peuvent être dupliqués ou arriver dans un ordre différent. Un même identifiant conserve le même événement, mais chaque tentative a un timestamp et une signature actualisés.
La configuration exige le propriétaire connecté, pas une nouvelle configuration MFA. Le secret de signature prouve l’origine des notifications ; la clé API autorise vos demandes. Pour remplacer un secret perdu, ajoutez une nouvelle destination, validez-la puis désactivez l’ancienne. Un appel déjà en cours peut encore arriver après désactivation.
Pour une rotation, acceptez temporairement les deux secrets et dédupliquez les événements pendant le chevauchement. Après une panne prolongée, corrigez le récepteur puis vérifiez une destination de remplacement. Les notifications épuisées ou manquées pendant une désactivation ne sont pas rejouées : consultez les messages par leurs identifiants, sans les renvoyer.
Avant d’intégrer dans votre produit
Votre application génère, expire et vérifie ses propres codes et applique ses règles métier. SMSMarocPro assure leur transport. Les messages individuels bénéficient d’une répartition favorable avec les campagnes ; cela ne garantit pas un délai et n’interrompt pas un envoi commencé.
- Commencez avec une clé live aux permissions limitées et un destinataire autorisé.
- Testez les réponses 401, 409 et 429, les coupures réseau et les notifications dupliquées.
- Ne confondez pas confirmation d’enregistrement, envoi et livraison.
- Gardez les clés et les secrets de signature côté serveur ; masquez-les dans vos journaux.
- Vérifiez abonnement, quota, exclusions et date de validité avant chaque campagne.
Le Contrat API est la source unique des schémas machine. Il est accessible ici et dans l’application : ce ne sont pas deux copies distinctes.