Aller au contenu

Tape un mot pour chercher dans les pages.

↑↓ Choisir↵ OuvrirÉchap Fermer

API

Le format des messages que Mail envoie à ton application, leurs en-têtes, la signature à vérifier, les reprises et les événements.

Mail envoie ses webhooks au format Standard Webhooks, le même que Svix et Resend : une bibliothèque de ce standard, dans le langage de ton application, vérifie la signature à ta place. Pour créer une adresse, voir Recevoir les réponses d’un formulaire par webhook.

Événements

ÉvénementQuand
form.response.createdQuelqu’un envoie un formulaire.

Le message

Un POST en JSON, 256 Ko au plus.

{
"type": "form.response.created",
"timestamp": "2026-10-17T09:12:03.000Z",
"data": {
"id": "k57d…",
"form_id": "j97a…",
"form_public_id": "k7m2p9x4q8ab",
"form_name": "Contact",
"values": { "prenom": "Léa", "email": "lea@studio.ch", "message": "Un devis, s'il vous plaît." },
"email": "lea@studio.ch",
"consent": { "status": "pending", "text": "J'accepte de recevoir vos nouvelles", "at": "2026-10-17T09:12:03.000Z" },
"page": "https://monsite.ch/contact",
"created_at": "2026-10-17T09:12:03.000Z"
}
}
ChampSens
data.idIdentifiant de la réponse. Le même que dans GET /forms/:id/responses.
data.valuesUne valeur par champ, avec la variable du champ comme clé. Une case cochée vaut oui.
data.emailL’adresse du champ e-mail, ou null.
data.consentLa preuve de consentement : status vaut pending (en attente du lien de confirmation) ou confirmed. null si la case n’est pas cochée.
data.pageLa page du site d’où vient la réponse, sans paramètres.

En-têtes

En-têteContenu
webhook-idIdentifiant du message, msg_…. Identique à chaque essai et à chaque renvoi.
webhook-timestampHeure d’envoi, en secondes. Renouvelée à chaque essai.
webhook-signaturev1, suivi de la signature en base64.

Vérifier la signature

  1. Refuse le message si webhook-timestamp s’écarte de plus de 5 minutes de l’heure de ton serveur. C’est ce qui empêche de rejouer un message volé.
  2. Prends le secret whsec_…, enlève le préfixe whsec_ et décode le reste en base64 : c’est la clé.
  3. Calcule le HMAC-SHA256 de webhook-id, un point, webhook-timestamp, un point, puis le corps brut de la requête (avant toute lecture du JSON).
  4. Encode le résultat en base64 et compare-le, en temps constant, à chaque signature de webhook-signature (séparées par une espace, chacune préfixée v1,).
const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64")
const signed = `${id}.${timestamp}.${rawBody}`
const expected = crypto.createHmac("sha256", key).update(signed).digest()
const ok = signatureHeader.split(" ").some((part) => {
const [version, value] = part.split(",")
const received = Buffer.from(value ?? "", "base64")
return version === "v1" && received.length === expected.length && crypto.timingSafeEqual(received, expected)
})

Réponds 401 si la signature est fausse. Réponds 2xx quand le message est traité ou déjà vu.

Livraison

SuccèsUne réponse 2xx en 10 secondes.
RedirectionUn échec : Mail ne suit jamais une redirection. Donne l’adresse finale.
Essais8 au total : tout de suite, puis après 5 secondes, 5 minutes, 30 minutes, 2 heures, 5 heures, 10 heures, 10 heures.
RenvoiDepuis la page Webhooks, bouton Livraisons puis Renvoyer. Même webhook-id.
Journal30 jours.

Adresses acceptées

https uniquement, port 443, un nom de domaine public. Sont refusés : http, un numéro IP, localhost et les noms internes (.local, .internal…), un nom qui pointe vers une adresse privée, une adresse avec identifiant et mot de passe. Chaque espace garde 20 adresses au plus.