Aller au contenu
MMSDuck

API v1

Documentation

Tout ce qu'il faut pour envoyer votre premier SMS depuis votre code : une clé, un appel, un webhook.

Démarrer

L'API MMSDuck est une API REST. Toutes les requêtes se font en HTTPS, les corps de requête et de réponse sont en JSON encodé en UTF-8.

URL de base
https://api.mmsduck.com

Avant votre premier envoi, il vous faut trois choses :

  1. un compte marchand ;
  2. une clé API active ;
  3. un solde de crédits : un SMS d'un segment coûte 50 Ar.

Authentification

Chaque requête s'authentifie avec une clé API transmise dans l'en-tête Authorization, selon le schéma Bearer.

Authorization: Bearer mmsduck_live_…
Votre clé est un secret : elle donne le droit de dépenser vos crédits. Gardez-la côté serveur, jamais dans une application mobile ni dans du JavaScript livré au navigateur. La clé complète n'est affichée qu'au moment de sa création. Ensuite, seule sa révocation est possible.

Une clé révoquée est refusée immédiatement, avec une réponse 401. Vous pouvez faire coexister plusieurs clés, par exemple une par application, pour en révoquer une sans interrompre les autres.

Bac à sable et production

Deux environnements, deux préfixes de clé : mmsduck_test_… pour le bac à sable, mmsduck_live_… pour la production. L'environnement vient toujours de la clé présentée, jamais d'un paramètre de la requête.

Le bac à sable ne demande aucune vérification d'identité, mais ne peut écrire qu'aux numéros que vous avez vous-même déclarés et validés (menu « Numéros de test » du tableau de bord, trois numéros au maximum). La production exige un dossier d'identité approuvé. Dans les deux cas, l'envoi débite réellement vos crédits : le bac à sable n'est pas gratuit.

Envoyer un SMS

POST/api/v1/sms/send

Crée un message et le place en file d'envoi. La réponse est immédiate : elle confirme la prise en charge, pas encore la livraison.

ParamètreTypeDescription
tostringNuméro du destinataire. Les formats locaux sont acceptés puis normalisés en +261XXXXXXXXX. Requis.
messagestringContenu du SMS, sans le nom de votre entreprise : il est ajouté automatiquement en première ligne (voir plus bas). Au-delà d'un segment, en-tête comprise, le message est découpé et chaque segment consomme un crédit. Requis.
Requête
curl -X POST https://api.mmsduck.com/api/v1/sms/send \
  -H "Authorization: Bearer mmsduck_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+261341234567",
    "message": "Votre code est 123456"
  }'
Réponse 200 OK
{
  "success": true,
  "message_id": "sms_01J8ABC123",
  "status": "queued"
}

Conservez le message_id : c'est lui qui vous permet de retrouver le message et de recouper les webhooks reçus.

Messages transactionnels uniquement. Cette API achemine les codes de vérification, les confirmations de commande ou de paiement, les notifications de livraison et les alertes de compte. La prospection, la promotion commerciale et les messages politiques ou de propagande y sont interdits et entraînent la suspension immédiate du compte. Voir les conditions générales.

Nom de l'expéditeur

Le nom de votre entreprise est ajouté automatiquement en première ligne de chaque message, suivi d'un retour à la ligne. Vous n'avez rien à faire : envoyez seulement votre texte.

Ce que reçoit le destinataire
BOUTIQUE VANILLE
Votre code est 123456
Cette ligne fait partie du message et compte donc dans le calcul des segments. Un texte qui tenait tout juste en un segment peut en occuper deux une fois l'en-tête ajoutée — et coûter deux crédits. Prévoyez la longueur de votre raison sociale, plus un caractère, dans votre budget de 160 caractères.

Suivre un message

GET/api/v1/sms/{message_id}

Renvoie l'état courant d'un message. En pratique, les webhooks vous préviennent sans que vous ayez à interroger cette route : réservez-la aux vérifications ponctuelles et aux réconciliations.

StatutSignification
queuedLe message est accepté et attend son tour.
processingLe message est en cours d'envoi.
sentLe message est parti vers le réseau mobile.
failedL'envoi a échoué définitivement, après épuisement des tentatives.
cancelledLe message a été annulé avant son envoi.
Réponse 200 OK
{
  "message_id": "sms_01J8ABC123",
  "to": "+261341234567",
  "message": "Votre code est 123456",
  "status": "sent",
  "segments": 1,
  "attempts": 1,
  "error": null,
  "created_at": "2026-08-10T09:41:00Z",
  "processed_at": "2026-08-10T09:41:02Z",
  "sent_at": "2026-08-10T09:41:03Z"
}

Webhooks

Déclarez une URL de rappel dans votre tableau de bord. Quand un message atteint un état terminal, envoyé ou définitivement échoué, MMSDuck y envoie une requête POST décrivant le message concerné. Les états intermédiaires (en attente, en cours) ne déclenchent pas de webhook : suivez-les en direct depuis votre tableau de bord si vous en avez besoin.

POST https://votre-site.mg/webhooks/sms
X-MMSDuck-Signature: sha256=5f2b9c1e8a3d…

{
  "message_id": "sms_01J8ABC123",
  "status": "sent",
  "to": "+261341234567"
}

Vérifier la signature

Chaque appel porte un en-tête X-MMSDuck-Signature, au format sha256=<hex> : un HMAC-SHA256 du corps brut de la requête, calculé avec le secret de votre webhook (whsec_…, visible dans Webhooks sur votre tableau de bord). Recalculez-le de votre côté et comparez : si les valeurs diffèrent, ignorez la notification, elle ne vient pas de nous.

Bac à sable et production ont chacun leur propre URL et leur propre secret : un SMS envoyé avec une clé mmsduck_test_… ne livre jamais sur votre webhook de production, et inversement. Configurez les deux séparément dans Webhooks, en sélectionnant l'espace concerné.
Vérifier la signature (Node.js)
import crypto from "node:crypto";

// rawBody = corps BRUT de la requête (pas l'objet JSON déjà parsé)
const header = req.headers["x-mmsduck-signature"]; // "sha256=<hex>"
const expected = "sha256=" + crypto
  .createHmac("sha256", process.env.MMSDUCK_WEBHOOK_SECRET) // whsec_…
  .update(rawBody)
  .digest("hex");

const valid =
  header.length === expected.length &&
  crypto.timingSafeEqual(Buffer.from(header), Buffer.from(expected));

if (!valid) {
  return res.status(400).send("Signature invalide");
}
Comparez les signatures avec une fonction à temps constant, pas avec une égalité de chaînes classique. Et traitez vos webhooks de façon idempotente : un même message peut vous être notifié plusieurs fois si votre serveur a mis du temps à répondre.

Réessais

Répondez 2xx dès réception, avant même votre traitement métier. Toute autre réponse, ou une absence de réponse, est considérée comme un échec, et la notification est représentée plus tard.

Erreurs

Les erreurs utilisent les codes HTTP usuels et renvoient toujours un corps JSON de même forme.

{
  "success": false,
  "error": {
    "code": "insufficient_credits",
    "message": "Solde insuffisant pour envoyer ce message."
  }
}
HTTPCodeCause
400invalid_requestParamètre manquant ou corps de requête illisible.
400invalid_recipientLe numéro du destinataire n'est pas un numéro mobile malgache valide.
401invalid_api_keyClé absente, inconnue ou révoquée.
402insufficient_creditsLe solde ne couvre pas le nombre de segments du message.
403kyc_not_approvedClé de production, mais votre dossier d'identité n'est pas encore approuvé. Utilisez une clé de bac à sable en attendant.
403recipient_not_verifiedClé de bac à sable envoyée vers un numéro que vous n'avez pas déclaré et validé.
403bulk_content_refusedCe message, identique, a déjà été envoyé à trop de destinataires distincts en peu de temps : signature d'un envoi en masse, interdit par les conditions générales.
404sms_not_foundAucun message avec cet identifiant sur votre compte.
429rate_limitedLimite d'envoi atteinte. Réessayez plus tard.
429daily_limit_reachedPlafond de SMS sur 24 h atteint. Contactez-nous si votre volume légitime le dépasse.
503insufficient_platform_capacityLa plateforme n'a temporairement plus de capacité d'envoi. Réessayez plus tard, votre solde n'a pas été débité.
500internal_errorIncident de notre côté. Le message n'a pas été débité.

Limites d'envoi

Deux compteurs indépendants s'appliquent à chaque envoi, par clé API et par compte marchand : par défaut 2 requêtes par seconde et 60 par minute. Un pic ponctuel sur l'un des deux suffit à déclencher la limite, même si l'autre est encore large.

Au-delà, l'API répond 429 (rate_limited) sans débiter de crédit. Espacez alors vos appels, en allongeant le délai à chaque nouvel échec plutôt qu'en réessayant immédiatement. Un volume plus élevé que ces limites par défaut ? Contactez-nous, elles sont ajustables par compte.

Prêt à envoyer ?

Créez votre compte, générez une clé et faites partir un message de test.

Créer un compte