Webhooks
Enregistrez un point de terminaison public, vérifiez les charges utiles brutes signées, dédupliquez les événements et gérez les nouvelles tentatives en toute sécurité.
Enregistrez un point de terminaison public, vérifiez les charges utiles brutes signées, dédupliquez les événements et gérez les nouvelles tentatives en toute sécurité.
Les webhooks développeur envoient les événements de votre compte à votre serveur, ce qui vous évite d'effectuer des interrogations régulières (polling). La livraison s'effectue au moins une fois : les événements peuvent être dupliqués, retardés ou arriver dans le désordre.
Enregistrer un point de terminaison#
Créez une clé disposant de la permission webhooks:write, puis enregistrez un récepteur HTTPS public ainsi que le plus petit ensemble d'événements dont vous avez besoin.
curl --fail-with-body https://makeagent.fast/api/v1/webhook-endpoints \
-X POST \
-H "Authorization: Bearer $MAF_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: webhook-production-v1" \
-d '{
"url": "https://api.example.com/webhooks/make-agent-fast",
"events": ["lead.created", "conversation.started"]
}'L'URL doit utiliser HTTPS, ne peut contenir ni nom d'utilisateur, ni mot de passe, ni fragment, ni port personnalisé, et ne doit se résoudre que vers des adresses IP publiques. Les redirections ne sont pas suivies lors de la livraison.
La réponse 201 révèle une seule fois un secret de signature whsec_.... Enregistrez-le dans le gestionnaire de secrets du récepteur avant de fermer la réponse. La liste des points de terminaison renvoie ensuite l'URL, les événements, le statut, la dernière livraison et la dernière erreur, mais pas le secret.
Types d'événements#
| Événement | Émis lorsque |
|---|---|
site.published | Un site est publié |
site.unpublished | Un site repasse à l'état de brouillon |
lead.created | L'agent enregistre un nouveau lead |
lead.qualified | L'agent qualifie un lead, ou un lead est accepté dans le pipeline commercial |
booking.requested | Un visiteur demande ou réserve un créneau |
handoff.requested | L'agent transmet une conversation à une personne |
opportunity.created | Un lead devient une opportunité du pipeline |
opportunity.updated | L'attribution, la valeur, la prochaine action ou d'autres champs d'une opportunité changent |
opportunity.stage_changed | Une opportunité passe à une autre étape |
opportunity.won | Une opportunité est clôturée comme gagnée |
opportunity.lost | Une opportunité est clôturée comme perdue |
conversation.started | Un nouveau fil de conversation démarre |
conversation.message.created | Un message est ajouté à une conversation |
knowledge.ready | Une source de connaissances termine son traitement |
broadcast.sent | Une diffusion termine son flux d'envoi |
domain.activated | Obsolète : les domaines personnalisés ne sont pas proposés ; cet événement n'est jamais émis |
Format de la requête#
Make Agent Fast envoie une requête POST avec Content-Type: application/json, User-Agent: MakeAgentFast-Webhooks/1.0 et les en-têtes suivants :
maf-event-id: evt_...
maf-event-type: lead.created
maf-signature: t=1784210566,v1=HEX_HMAC_SHA256Le corps JSON repose sur une enveloppe stable. Les champs contenus dans data dépendent de l'événement et peuvent recevoir des champs supplémentaires.
{
"id": "evt_2c34...",
"type": "lead.created",
"created_at": "2026-07-16T10:02:46.000Z",
"data": {
"site_id": "SITE_ID",
"lead_id": "LEAD_ID"
}
}Utilisez l'id du corps ou maf-event-id comme clé de déduplication. N'utilisez ni l'heure de livraison ni un identifiant de base de données généré.
Vérifier la signature en Node.js#
Lisez les octets bruts exacts avant l'analyse du JSON. La valeur signée est TIMESTAMP + "." + RAW_BODY.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyWebhook(rawBody: Buffer, header: string, secret: string) {
const values = Object.fromEntries(header.split(",").map((part) => part.split("=", 2)));
const timestamp = values.t;
const supplied = values.v1;
if (!timestamp || !supplied || !/^[a-f0-9]{64}$/.test(supplied)) return false;
const age = Math.abs(Date.now() / 1_000 - Number(timestamp));
if (!Number.isFinite(age) || age > 300) return false;
const expected = createHmac("sha256", secret)
.update(`${timestamp}.`)
.update(rawBody)
.digest("hex");
return timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(supplied, "hex"));
}Rejetez les signatures malformées et les horodatages plus anciens que la fenêtre de rejeu que vous acceptez ; cinq minutes constituent une valeur par défaut raisonnable. Comparez des octets de longueur fixe en temps constant.
Vérifier la signature en Python#
import hashlib
import hmac
import time
def verify_webhook(raw_body: bytes, header: str, secret: str) -> bool:
values = dict(part.split("=", 1) for part in header.split(",") if "=" in part)
timestamp = values.get("t", "")
supplied = values.get("v1", "")
try:
if abs(time.time() - int(timestamp)) > 300:
return False
except ValueError:
return False
signed = timestamp.encode() + b"." + raw_body
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, supplied)N'analysez et ne traitez le JSON qu'une fois la vérification de la signature réussie.
Accuser réception et traiter#
- Vérifiez la signature et l'horodatage.
- Insérez l'identifiant de l'événement dans une table dotée d'une contrainte d'unicité.
- S'il existe déjà, renvoyez
204sans répéter l'effet de bord. - Validez l'événement (commit) ou mettez votre tâche interne en file d'attente.
- Renvoyez rapidement une réponse
2xx.
L'expéditeur de Make Agent Fast expire après 10 secondes. Il ne considère comme réussies que les réponses 2xx et ne suit pas les redirections.
Comportement des nouvelles tentatives#
Les livraisons échouées sont mises en file d'attente pour huit tentatives au maximum. Le délai d'attente commence autour de deux secondes, double avec une variation aléatoire (jitter) et est plafonné à une heure ; l'heure réelle de livraison peut être plus tardive lorsque les workers sont occupés. Comme les nouvelles tentatives peuvent survivre à la requête d'origine et arriver dans le désordre, ne partez jamais du principe qu'un événement arrive immédiatement avant un autre.
Ne renvoyez une réponse non 2xx que si vous souhaitez que Make Agent Fast réessaie. Pour une version d'événement définitivement non prise en charge ou une destination supprimée, acceptez et consignez l'événement, ou supprimez le point de terminaison, plutôt que de produire des échecs transitoires sans fin.
Renouveler un secret de signature#
Les secrets de signature ne peuvent être ni révélés ni modifiés. Pour les renouveler en toute sécurité :
- Créez un second point de terminaison pointant vers un chemin de récepteur temporaire ou versionné.
- Enregistrez le secret nouvellement révélé.
- Acceptez et dédupliquez les événements provenant des deux points de terminaison.
- Vérifiez que le nouveau point de terminaison reçoit des livraisons valides.
- Supprimez l'ancien point de terminaison avec
DELETE /webhook-endpoints/{endpoint_id}.
Si l'URL du récepteur reste identique, faites en sorte que l'application réceptrice accepte les deux secrets pendant la période de chevauchement et utilisez les identifiants d'événement pour éviter les effets de bord en double.
Dépannage#
| Symptôme | À vérifier |
|---|---|
| Aucune livraison | Statut du point de terminaison, événements sélectionnés, DNS public, certificat HTTPS, et si l'événement s'est réellement produit |
| Signature non concordante | Accès au corps brut, concaténation exacte timestamp.body, secret correct du point de terminaison, et transformations du corps par un proxy |
| Livraisons répétées | Code de retour, délai d'expiration de 10 secondes, exceptions internes et unicité des identifiants d'événement |
| Erreur d'adresse privée | Les enregistrements DNS ne doivent se résoudre que vers des adresses publiques ; localhost et les plages internes sont rejetés |
| Échec de redirection | Enregistrez directement l'URL HTTPS finale ; les redirections ne sont pas suivies |
N'incluez jamais le secret de signature, la clé bearer ni la charge utile complète d'un visiteur dans un message adressé au support. Indiquez l'identifiant du point de terminaison, l'identifiant de l'événement, l'heure de l'échec et des journaux du récepteur nettoyés de toute donnée sensible.