QMailing
ProduitTarifsAPI et MCP
Se connecter

Documentation

API publique et serveur MCP

Construisez des agents, des CLI et des intégrations back-office sur qmailing. PLUS et au-dessus débloquent l'authentification par token Bearer sur chaque endpoint public ainsi qu'un serveur MCP prêt à brancher dans Claude Desktop, Cursor ou tout autre client compatible.

Commencer Gérer les tokens

Sur cette page

  • 1. Démarrer
  • 2. Authentification
  • 3. Scopes
  • 4. Boîtes mail
  • 5. Domaines & DNS
  • 6. E-mail
  • 7. Webhooks
  • 8. Erreurs
  • 9. Limites de débit
  • 10. Serveur MCP

Démarrer

  1. 1. Passez à PLUS — l'API publique est disponible sur PLUS, PRO, PRO_PLUS ou PRO_MAX. Les appelants en FREE reçoivent 402 Payment Required quel que soit le token.
  2. 2. Émettez un token — rendez-vous dans Paramètres → Développeurs, cliquez sur Nouveau token, choisissez les scopes nécessaires et copiez la valeur qm_live_… dès qu'elle s'affiche. Les tokens n'apparaissent qu'une seule fois.
  3. 3. Effectuez votre premier appel :
    curl https://qmailing.com/api/v1/pub/mailboxes \
      -H "Authorization: Bearer qm_live_your_token_here"

Authentification

Chaque appel à l'API publique embarque un en-tête Authorization: Bearer qm_live_<…>. Le format du token utilise un préfixe (qm_live_ suivi de 32 caractères aléatoires) afin que les fuites dans les logs et les dépôts soient évidentes.

Les tokens sont stockés sous forme de hash (SHA-256). Le texte en clair est affiché une seule fois lors de l'émission et plus jamais — si vous le perdez, créez-en un nouveau. Chaque requête déclenche une comparaison de hash en temps constant ; last_used_at est mis à jour en cas de succès afin que l'UI développeurs puisse mettre en évidence les identifiants obsolètes.

Les rétrogradations de plan désactivent immédiatement les tokens existants. L'API revérifie apiAccess à chaque requête signée — pas besoin de révoquer les tokens un par un lors d'un changement de plan.

Scopes

Chaque token porte une ou plusieurs chaînes de scope. Les endpoints déclarent les scopes dont ils ont besoin ; un scope manquant renvoie 403 InsufficientScope. Le joker (*) accorde tout — ne l'activez que si c'est vraiment nécessaire.

ScopePermet
mailboxes:readLister et inspecter les boîtes mail
mailboxes:writeCréer, modifier, supprimer des boîtes mail
domains:readLister les domaines et les enregistrements DNS
domains:writeAjouter, vérifier, supprimer des domaines personnalisés (FE uniquement en v1)
email:readLister et inspecter les messages des boîtes
email:sendEnvoyer du courrier sortant via l'API
webhooks:readLister les endpoints de webhook
webhooks:writeEnregistrer, lister et révoquer les endpoints de webhook
*Joker — accorde tout. À utiliser avec parcimonie.

Boîtes mail

Lister

GET /api/v1/pub/mailboxes
# scope: mailboxes:read

Récupérer une

GET /api/v1/pub/mailboxes/{id}
# scope: mailboxes:read

Créer

POST /api/v1/pub/mailboxes
Content-Type: application/json
# scope: mailboxes:write

{
  "localPart": "support",
  "domain": "yourbrand.com",
  "displayName": "Support team",
  "forwardTo": "ops@yourbrand.com"
}

Le champ domain est optionnel — omettez-le et la boîte est créée sur qmailing.com. Sur un domaine personnalisé, celui-ci doit être à la fois claimed ET fullyVerified ; un domaine pas encore prêt renvoie 400 au lieu de créer silencieusement une boîte non routable.

La suppression d'une boîte mail n'est volontairement pas exposée en v1. Elle enregistre un consentement aux conditions d'utilisation et consomme un emplacement du quota à vie du compte — ces deux actions reviennent à une personne qui appuie sur un bouton après avoir lu la modale, pas à un agent sans surveillance. Utilisez le front-end.

Domaines & DNS

Lister les domaines personnalisés

GET /api/v1/pub/domains
# scope: domains:read

Liste des enregistrements DNS

GET /api/v1/pub/domains/{id}/dns-records
# scope: domains:read

Renvoie la liste complète des 8 enregistrements (TXT de challenge, MX, SPF, trois CNAME DKIM, DMARC, optionnellement _amazonses) avec un status par ligne parmi PENDING / FOUND / MISMATCH / NOT_REQUIRED. L'agent peut s'en servir pour indiquer à l'utilisateur exactement ce qu'il doit publier chez son fournisseur DNS.

E-mail

Lisez le contenu des boîtes mail et envoyez du courrier sortant. La pagination par dossier correspond à l'API interne du front — un agent qui passe de l'auth cookie-JWT à Bearer ne voit aucun changement de forme.

Lister par dossier

GET /api/v1/pub/email?folder=INBOX&offset=0&limit=25
# scope: email:read

# Optional query params:
#   mailboxId   filter to one mailbox (omit for all-mailboxes view)
#   folder      INBOX | SENT | DRAFTS | TRASH | STARRED | SPAM
#   offset      0-based index, default 0
#   limit       1..100, default 25

folder vaut par défaut INBOX ; valeurs autorisées : INBOX, SENT, DRAFTS, TRASH, STARRED, SPAM. mailboxId est optionnel — l'omettre liste toutes les boîtes de l'appelant. Pagination via offset + limit ; limit est borné à la plage 1–100 (par défaut 25).

Récupérer un

GET /api/v1/pub/email/{id}
# scope: email:read

Renvoie le EmailDetailDto complet : corps HTML / texte, liste de labels, métadonnées des pièces jointes. Les pièces jointes ne sont pas inline — récupérez chacune via GET /{emailId}/attachments/{index} ci-dessous, qui diffuse les octets bruts avec le nom de fichier d'origine et le content-type.

Télécharger une pièce jointe

GET /api/v1/pub/email/{emailId}/attachments/{index}
# scope: email:read
# response: streamed bytes, Content-Type from the attachment

Diffuse les octets bruts de la pièce jointe (index zéro-basé dans la liste des pièces jointes du message). Même forme que l'endpoint basé session : Content-Type en pass-through, Content-Disposition selon RFC 6266 avec le nom d'origine (UTF-8 percent-encoded pour l'Unicode). Les agents MCP appelant via @qmailing/mcp-server utilisent l'outil qmailing_get_attachment, qui renvoie les octets encodés en base64 inline (limite 5 Mio).

Envoyer

Endpoint d'envoi. Corps multipart/form-data avec une partie JSON command (destinataires / sujet / corps) et zéro ou plusieurs parties-fichiers attachments :

POST /api/v1/pub/email/send
Content-Type: multipart/form-data
# scope: email:send

--boundary
Content-Disposition: form-data; name="command"
Content-Type: application/json

{
  "mailboxId": "11111111-2222-3333-4444-555555555555",
  "to":      ["alice@example.com"],
  "cc":      [],
  "bcc":     [],
  "subject": "Order #1428 confirmed",
  "bodyText": "Plain-text body",
  "bodyHtml": "<p>Rich body</p>",
  "replyToId": null
}
--boundary
Content-Disposition: form-data; name="attachments"; filename="receipt.pdf"
Content-Type: application/pdf

<binary bytes>
--boundary--

Forme du JSON command :

{
  "mailboxId":   "uuid (required) — must be SES-verified",
  "to":          "string[] (required, max 50)",
  "cc":          "string[] (optional, max 50)",
  "bcc":         "string[] (optional, max 50)",
  "subject":     "string (max 998 chars)",
  "bodyText":    "string — plain-text body",
  "bodyHtml":    "string — HTML body",
  "replyToId":   "uuid | null — set for in-thread replies"
}

Chaque appel décompte de la limite quotidienne d'envoi du plan, du quota d'envoi SES de la boîte et du quota de stockage de l'utilisateur pour la copie résultante dans SENT. Les destinataires sont validés comme adresses RFC 5322 ; ceux qui échouent renvoient 400 avec l'adresse fautive. La boîte référencée par mailboxId doit être vérifiée par SES — sinon, 409 MailboxNotVerified avec un indice pointant vers la page domaines.

Les agents qui préfèrent le JSON au multipart peuvent utiliser l'outil MCP fourni — il encode les pièces jointes en base64 inline puis les ré-empaquette en multipart/form-data à la sortie, le format sur le fil reste donc identique.
Le filename sur la partie command est facultatif — Node fetch tamponne automatiquement filename="blob" sur chaque Blob, les clients navigateur et Node peuvent transmettre command.json, et les clients curl --form conformes peuvent l'omettre. Les trois formes sont parsées de la même manière ; seul Content-Type: application/json sur les octets de la partie compte.

Scopes : email:read pour lister + récupérer un, email:send pour l'envoi.

Webhooks

Enregistrez un endpoint HTTPS et QMailing POSTe un envelope JSON signé dès qu'un des événements souscrits se déclenche. La livraison est en service — le worker interroge une file par événement toutes les 10s, signe chaque requête en HMAC-SHA256 et réessaie les échecs en backoff exponentiel (1m / 5m / 15m / 1h / 6h) avant de parquer la ligne en DLQ pour revue manuelle.

Enregistrer un endpoint

POST /api/v1/pub/webhooks
Content-Type: application/json
# scope: webhooks:write

{
  "url":    "https://your-server.example.com/qmailing-events",
  "label":  "Production listener",
  "events": ["email.received", "email.bounced", "domain.verified"]
}

# Response (201):
{
  "endpoint": {
    "id":         "uuid",
    "url":        "https://your-server.example.com/qmailing-events",
    "label":      "Production listener",
    "events":     ["email.received", "email.bounced", "domain.verified"],
    "secretPrefix": "whk_AbCd",
    "createdAt":  "2026-05-04T20:30:00Z",
    "active":     true
  },
  "plaintext":  "whk_AbCd…<32 random chars>"   // shown ONCE
}

url doit être un endpoint HTTPS (HTTP n'est autorisé que pour localhost en dev). L'hôte est vérifié contre une allow-list anti-SSRF — les adresses privées / loopback / cloud-metadata sont rejetées dès l'enregistrement. La réponse contient plaintext exactement une fois ; conservez-le côté serveur et utilisez-le pour vérifier les signatures HMAC des livraisons entrantes.

Catalogue d'événements

ÉvénementSe déclenche quand
email.receivedUn courrier entrant arrive dans l'une de vos boîtes.
email.sentUn courrier sortant a été accepté par SES.
email.bouncedUn courrier sortant a rebondi (hard ou soft).
domain.verifiedUn domaine personnalisé a terminé la vérification DNS de bout en bout.

Lister les endpoints

GET /api/v1/pub/webhooks
# scopes: webhooks:read OR webhooks:write

Renvoie les endpoints actifs et révoqués, les plus récents d'abord. L'accès en lecture fonctionne avec webhooks:read ou webhooks:write — un token inspecteur évite ainsi le scope d'écriture plus large.

Forme de la livraison

Chaque livraison est un unique POST vers votre endpoint avec Content-Type: application/json. Le corps est l envelope d événement avec les champs event (nom de l événement), occurred_at (timestamp ISO) et un bloc data dont la forme dépend de l événement (voir la table ci-dessus). Trois en-têtes portent les métadonnées de routage + vérification :

POST https://your-server.example.com/qmailing-events
Content-Type: application/json
User-Agent: qmailing-webhook/1 (+https://qmailing.com)
X-Qmailing-Event: email.received
X-Qmailing-Delivery: 5f3b2a91-7c4d-4d52-9c3e-aa1bcd8a4f12
X-Qmailing-Signature: t=1685120800,v1=abc123def…

{
  "event": "email.received",
  "occurred_at": "2026-05-23T19:46:40Z",
  "data": {
    "email_id":   "uuid",
    "mailbox_id": "uuid",
    "from":       "alice@example.com",
    "subject":    "Hello",
    "preview_text": "Just checking in…"
  }
}

Schéma de signature

# Receiver side (Node example)
const crypto = require("node:crypto");

function verify(headers, body, secret) {
  const sig = headers["x-qmailing-signature"];           // "t=1685120800,v1=abc123…"
  const m = /t=(\d+),v1=([0-9a-f]+)/.exec(sig);
  if (!m) return false;
  const ts = Number(m[1]);
  if (Math.abs(Date.now() / 1000 - ts) > 300) return false;  // 5-min replay window
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${ts}.${body}`)
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(m[2]));
}

Rejetez la requête si le timestamp a plus de 5 minutes (fenêtre anti-replay) ou si le HMAC recalculé ne correspond pas au v1 hex — notre signataire BE imite exactement les conventions Stripe / GitHub, donc n'importe quelle bibliothèque de vérification standard fonctionne telle quelle.

Politique de retry

Les réponses non-2xx (ou erreurs de transport — DNS, TLS, connexion refusée, timeout de 10s) incrémentent attempt_count et poussent next_attempt_at au prochain créneau dans 1m / 5m / 15m / 1h / 6h. Après le cinquième échec, la livraison passe en FAILED (DLQ) — visible sur /settings/developers, où le développeur peut la rejouer manuellement. Idempotence : chaque retry porte le même UUID dans X-Qmailing-Delivery — les receveurs dédupliquent sur cet en-tête.

Lister les livraisons récentes

GET /api/v1/pub/webhooks/{endpointId}/deliveries?limit=100
# scopes: webhooks:read OR webhooks:write

Renvoie les limit livraisons les plus récentes d'un endpoint (par défaut 100, max 100), les plus récentes d'abord. Chaque ligne porte statut, compteur de tentatives, dernier code HTTP, dernière erreur et le JSON original — alimente la vue d'historique sur le dashboard développeur.

Envoyer une livraison de test

POST /api/v1/pub/webhooks/{endpointId}/test
# scope: webhooks:write
# Response 202: WebhookDeliveryDto (enqueued, picked up next worker tick)

Déclenche un événement synthétique webhook.test sur l'endpoint — même chemin de dispatch que les vrais événements, utile pour vérifier la signature + TLS avant de souscrire au trafic en direct.

Réessayer une livraison

POST /api/v1/pub/webhooks/deliveries/{deliveryId}/retry
# scope: webhooks:write
# Response 200: WebhookDeliveryDto (status=PENDING, attempt=MAX-1)

Remet manuellement en file une ligne de livraison. Impossible de réessayer une livraison déjà DELIVERED (409). Le retry donne exactement une tentative supplémentaire (compteur à MAX-1), donc un endpoint cassé en permanence ne peut être bouclé indéfiniment par des clics répétés.

Supprimer une livraison de l'historique

DELETE /api/v1/pub/webhooks/deliveries/{deliveryId}
# scope: webhooks:write
# Response: 204 No Content

Hard-delete d'une ligne de livraison de la table d'historique. Le log d'audit de conformité capture le fait de la suppression, donc la traçabilité n'est pas perdue lorsque la ligne disparaît.

Révoquer un endpoint

DELETE /api/v1/pub/webhooks/{id}
# scope: webhooks:write
# Response: 204 No Content

Idempotent — révoquer un endpoint déjà révoqué réussit silencieusement. La ligne est conservée (timestamp revoked-at posé) pour que les logs d'audit gardent leurs références.

Limite : 10 endpoints actifs par compte, 32 événements par endpoint. Les deux sont vérifiés à l'enregistrement — 409 WebhookEndpointLimitExceeded / 400 InvalidWebhookEvent.

Erreurs

Chaque réponse non-2xx est un ProblemDetail RFC-7807 avec un champ code stable sur lequel votre agent peut s'aiguiller :

HTTPcodeQuand
400InvalidApiTokenScopeChaîne de scope inconnue à la création.
400InvalidEmailAddressUn destinataire n'a pas passé la validation de syntaxe RFC 5322.
400InvalidWebhookEventLa liste d'événements enregistrée contient un nom inconnu.
401—En-tête Bearer manquant, mal formé, ou token révoqué / expiré.
402PlanFeatureRequiredL'appelant est sur un plan sans accès API — passez à la version supérieure.
403InsufficientScopeLe token ne porte aucun des scopes requis.
404ApiTokenNotFoundLa cible de révocation n'existe pas ou appartient à un autre compte.
409MailboxNotVerifiedLe domaine de la boîte d'envoi n'est pas encore entièrement vérifié — terminez le DNS dans /settings/domains.
409ApiTokenLimitExceededLe compte a atteint le maximum (20) de tokens actifs ; révoquez-en un d'abord.
409WebhookEndpointLimitExceededLe compte détient déjà 10 endpoints de webhook actifs ; révoquez-en un d'abord.
429RateLimitExceededLe token a dépassé 300 requêtes par minute. Retry-After en secondes.

Limites de débit

Chaque token dispose de son propre seau de 300 requêtes par minute. Les configurations multi-agents (CLI + cron + plugin IDE) utilisant des tokens différents pour le même compte fonctionnent indépendamment — elles ne se disputent jamais un seau commun. 429 RateLimitExceeded inclut un en-tête Retry-After en secondes.

Serveur MCP

Le paquet @qmailing/mcp-server s'exécute localement et expose l'API publique sous forme d'outils Model Context Protocol. Branchez-le dans Claude Desktop, Cursor, Continue ou tout client compatible MCP.

Le format du fil est stable — mais de nouveaux outils peuvent arriver dans n'importe quelle version 0.x. Épinglez une version précise (@qmailing/mcp-server@0.2.0) si vous ne voulez pas de mises à jour automatiques.
{
  "mcpServers": {
    "qmailing": {
      "command": "npx",
      "args": ["-y", "@qmailing/mcp-server"],
      "env": {
        "QMAILING_API_TOKEN": "qm_live_your_token_here"
      }
    }
  }
}

Outils fournis : qmailing_list_mailboxes, qmailing_get_mailbox, qmailing_create_mailbox, qmailing_list_domains, qmailing_get_dns_records, qmailing_list_emails, qmailing_get_email, qmailing_get_attachment, qmailing_send_email, qmailing_register_webhook, qmailing_list_webhooks, qmailing_delete_webhook. Le catalogue s'enrichit à mesure que de nouveaux endpoints arrivent.

Vous avez trouvé un bug ou vous avez une question ? Tokens, scopes, limites de plan et compteurs de rate-limit sont tous visibles depuis votre page développeurs.

QMailing

Créez des boîtes mail, pas des comptes.

Produit

  • Tout ce dont vous avez besoin
  • Tarifs
  • Comparatif
  • Cas d'usage
  • API et MCP

Mentions légales

  • Politique de confidentialité
  • Conditions d'utilisation
  • Remboursements
  • Suppression des données

Support

  • Contacter le support
© 2026 QMailing. Tous droits réservés.