Aller au contenu
subagentdocs
Sommaire de la documentation

Documentation › Webhooks

Webhooks

Voir en .md

Le serveur signale par webhook ce qui arrive à un message ou à un fil : un départ, une remise, un rebond, une réponse, une désinscription. Chaque appel est un POST JSON signé.

État

Les webhooks existent sur le serveur, pour les intégrations machine. Aujourd’hui, l’adresse de réception se règle par un administrateur de Subagent, pour une marque entière. Aucun outil ne permet encore à un espace de déclarer la sienne : c’est [ à venir ].

D’ici là, un agent n’a pas besoin de webhook : il lit ses réponses avec list_threads (status = replied), puis get_thread. Voir Fils.

Les événements

ÉvénementQuandDonnées (data)
message.sentUn message est parti.message_id, thread_id, inbox_id, to, subject, metadata, dry_run
message.deliveredLe serveur du destinataire a confirmé la remise.message_id, thread_id, inbox_id, to, subject, metadata
message.bouncedUn message est revenu : adresse en échec.Les mêmes, et bounce : type, status, diagnostic
message.rejectedUne règle d’envoi a refusé un message au moment de partir.message_id, thread_id, inbox_id, to, code, reason
message.unsubscribedUne personne s’est désinscrite, ou a signalé un message.address, via, reason, message_id
thread.repliedLe correspondant a répondu dans un fil.thread_id, message_id, inbox_id, from, subject, preview, auto_reply
thread.receivedUn nouvel e-mail a ouvert un fil.Les mêmes que thread.replied
inbox.suspendedLe coupe-circuit a suspendu une boîte.inbox_id, address, reason

Une réponse automatique ne déclenche pas thread.replied.

La forme d’un appel

Corps de la requête
{
  "id": "whd_9b2e4c7a1d5f48e3a6c0b8d2f4e6a1c3",
  "type": "thread.replied",
  "created_at": "2026-10-09T16:12:00+02:00",
  "tenant_id": "ten_8d41c0b7a2e94f6cb3159e07d2a8c6f1",
  "data": {
    "thread_id": "thr_b81f4c6a2d9e47035c1a8e6f0b4d2c97",
    "message_id": "msg_e93b7d1a5c0f42b8d6e2a9c4f7b1d056",
    "inbox_id": "inb_3f9c2a7e5b1d4c8e9a0f6b2d7c4e1a95",
    "from": "nadia.morel@exemple.com",
    "subject": "Re: Rendez-vous de jeudi",
    "preview": "Bonjour, vendredi à la même heure me convient.",
    "auto_reply": false
  }
}
En-têteContenu
Subagent-Signaturet=<horodatage>,v1=<signature>
Subagent-EventLe type de l’événement, par exemple thread.replied.
Subagent-DeliveryL’identifiant de la remise, whd_…. Le même à chaque réessai.
Content-Typeapplication/json

Vérifier la signature

La signature v1 est le HMAC-SHA256, en hexadécimal, de la chaîne <horodatage>.<corps brut>, calculé avec le secret du webhook (whsec_…, affiché une seule fois à sa création).

  1. Lisez t et v1 dans l’en-tête Subagent-Signature.
  2. Refusez un horodatage trop ancien : cinq minutes est une tolérance courante.
  3. Calculez le HMAC-SHA256 de t, un point, puis le corps brut de la requête, avec votre secret.
  4. Comparez le résultat à v1, en temps constant.
Python
import hashlib, hmac, time

def signature_valide(secret: str, entete: str, corps: bytes, tolerance: int = 300) -> bool:
    champs = dict(p.strip().split("=", 1) for p in entete.split(","))
    horodatage = int(champs["t"])
    if abs(time.time() - horodatage) > tolerance:
        return False
    attendu = hmac.new(secret.encode(), f"{horodatage}.".encode() + corps, hashlib.sha256).hexdigest()
    return hmac.compare_digest(attendu, champs["v1"])

Réessais

  • Une remise réussit quand votre serveur répond par un code 2xx, en moins de 10 secondes.
  • Sinon elle est réessayée, avec un délai qui s’allonge : 30 secondes, 2 minutes, 8 minutes, 32 minutes, un peu plus de 2 heures, puis 6 heures.
  • Après 8 tentatives, la remise est abandonnée.
  • Un même événement peut donc arriver deux fois : reconnaissez-le à son id.
Cette page en Markdown : /docs/webhooks.md Toute la documentation pour un agent : /llms.txt Mentions légales · Confidentialité