Webhooks : un avis signé quand un document est créé ou partagé
Menu du compte → Webhooks enregistre une URL qui reçoit un POST signé pour deux événements, document.created et document.shared. La gestion se fait uniquement depuis une session, et c’est voulu : cela reste en dehors de l’API scriptable, pour qu’une clé d’API fuitée ne se transforme pas en flux permanent de tous les documents qui suivront.
Deux choses se produisent dans un compte dont un autre programme peut vouloir être averti sur-le-champ : un document a été créé, et un document a été partagé. Dans Menu du compte → Webhooks, vous indiquez une URL en https:// qui doit en entendre parler et lisez le secret dont ses livraisons sont signées.
Ce qui arrive
Un POST dont le corps JSON a trois clés — l’événement, l’heure et les données :
{
"event": "document.created",
"created_at": "2026-09-11T09:12:44.000Z",
"data": { "id": "…", "name": "release-notes.md", "kind": "markdown-to-html", "size": 4193 }
}
document.shared porte l’id et le name, le mode dans lequel le document se trouve désormais, l’url de partage, et notified, les adresses auxquelles un avis est réellement parti. Aucun des deux événements ne porte le texte du document : un destinataire qui en a besoin a l’identifiant et une clé d’API.
Vérifier la signature
Chaque livraison porte un en-tête x-transformpipe-signature de la forme t=<secondes unix>,v1=<hex>, où v1 est un HMAC-SHA256 sur la chaîne <t>.<corps>, avec le secret de ce webhook pour clé — la forme qu’utilisent Stripe et GitHub, si bien qu’un code de vérification existant ne demande en général qu’un secret différent.
import { createHmac, timingSafeEqual } from 'node:crypto';
// `raw` est le corps tel qu’il est arrivé. Analysé puis ré-encodé, il ne donne pas le même condensé.
export function verify(raw, header, secret) {
const [t, v1] = header.split(',').map((part) => part.split('=')[1]);
const want = createHmac('sha256', secret).update(`${t}.${raw}`).digest('hex');
return (
v1.length === want.length &&
timingSafeEqual(Buffer.from(v1, 'hex'), Buffer.from(want, 'hex'))
);
}
Refusez un t vieux de plus de quelques minutes et une livraison interceptée ne pourra pas être rejouée plus tard. Le secret commence par whsec_ et se relit dans la boîte de dialogue à volonté : contrairement à une clé d’API, c’est nous qui vous le présentons, le revoir en installant un destinataire est donc légitime plutôt qu’une fuite.
Une tentative, pas de file d’attente
Une livraison est une requête unique avec un délai de cinq secondes. Il n’y a ni nouvelle tentative ni file d’attente : un destinataire à l’arrêt manque cet événement, et le suivant réessaie de lui-même. La boîte de dialogue montre le dernier statut par URL. Les redirections ne sont pas suivies, et l’adresse est revérifiée comme publique au moment de l’envoi.
Pourquoi une clé d’API ne peut pas en enregistrer un
Les webhooks se gèrent uniquement depuis une session connectée. Une clé capable d’en enregistrer un transformerait une fuite ponctuelle en flux permanent de tous les documents suivants, ce qui se perd bien plus mal qu’une clé.
Autres lectures : convertir des documents avec une API et publier depuis GitHub Actions.