Webhooks
Recevoir sur votre serveur les feedbacks créés, les changements de statut et les livraisons, signés par Galet.
Où : Paramètres → Webhooks. Réservé aux Admins.
Galet envoie un POST JSON à l'adresse de votre choix à chaque événement. Une organisation peut avoir 5 webhooks, chacun abonné aux événements qu'il veut. L'adresse doit être en https et publique.
Les événements
| Événement | Quand |
|---|---|
feedback.created | Un nouveau feedback est créé, quel que soit le canal |
feedback.status_changed | Le statut d'un feedback change, depuis l'inbox, l'API, le MCP ou le ticket Jira ou Linear lié |
feedback.shipped | Un feedback passe à delivered : le correctif est livré (envoyé en plus de feedback.status_changed) |
Le corps
{
"id": "evt_5c1f…",
"type": "feedback.status_changed",
"createdAt": "2026-10-13T21:40:12.000Z",
"data": {
"feedback": {
"key": "FIF-12",
"id": "tkt_…",
"title": "Export CSV vide",
"kind": "problem",
"severity": "P1",
"status": "fixed",
"app": { "id": "app_…", "name": "Acme" },
"reports": 3,
"createdAt": "2026-10-10T08:02:11.000Z",
"lastActivityAt": "2026-10-12T16:45:03.000Z",
"url": "https://app.galet.dev/feedbacks/FIF-12"
},
"previousStatus": "in_development"
}
}data.feedback a la forme de GET /v1/feedbacks (voir la référence de l'API), lu au moment de l'envoi. previousStatus n'existe que pour les changements de statut. id identifie l'événement : il est le même pour chaque nouvelle tentative, servez-vous-en pour ignorer un doublon.
Vérifier la signature
Le secret (whsec_…) est affiché une seule fois, à la création du webhook. Chaque envoi porte l'en-tête :
Galet-Signature: t=1791934812,v1=5257a869e7ec…v1 est le HMAC-SHA256, en hexadécimal, de <t>.<corps brut> avec le secret. Calculez-le sur le corps reçu tel quel, avant tout parsing, et refusez un t trop ancien (5 minutes, par exemple) pour bloquer le rejeu.
import { createHmac, timingSafeEqual } from "node:crypto"
function verify(rawBody, header, secret) {
const { t, v1 } = Object.fromEntries(header.split(",").map((part) => part.split("=")))
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false
const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex")
return v1.length === expected.length && timingSafeEqual(Buffer.from(v1), Buffer.from(expected))
}Les en-têtes Galet-Event (le type) et Galet-Delivery (l'identifiant de l'envoi) accompagnent aussi chaque requête.
Réponse et nouvelles tentatives
Répondez 2xx en moins de 10 secondes ; faites le travail long après avoir répondu. Galet ne suit pas les redirections. Sans 2xx, Galet réessaie après 1 min, 5 min, 30 min, 2 h, 6 h, 12 h et 24 h, puis abandonne. Paramètres → Webhooks montre le résultat du dernier envoi de chaque webhook.
L'ordre d'arrivée n'est pas garanti : comparez createdAt si l'ordre compte.