Cette page t'a-t-elle aidé ?
Cette page t'a-t-elle aidé ?
Référence
Gilbert dispatche des events HMAC-signés vers vos endpoints quand un dossier franchit une étape côté votre métier (créé, mis à jour, qualifié, réclamé, devis prêt). Vous vérifiez la signature, vous agissez.
Dernière mise à jour : 10 juin 2026
| Event | Émis quand |
|---|---|
| dossier.created | Nouveau dossier prospect, généralement après le premier message d'un visiteur. |
| dossier.updated | Le dossier a été enrichi (nouvelles informations captées par Gilbert). |
| dossier.qualified | Une proposition (devis, RDV, qualification) a été enregistrée par Gilbert. |
| dossier.claimed | Un humain a pris la main sur le dossier côté admin. |
| devis.ready | Un devis est prêt à être présenté au prospect. |
Le body est un JSON plat. Deux headers accompagnent chaque livraison : X-Gilbert-Signature (HMAC, voir ci-dessous) et X-Gilbert-Event(le nom de l'event, pour router sans parser le body).
{
"event": "dossier.qualified",
"product_context": "solve-learn",
"dossier_id": "uuid",
"summary": "RDV démo le 12 mai à 14h",
"admin_url": "https://gilbert.solveholding.com/admin/dossiers/<uuid>",
"created_at": "2026-05-07T14:32:18.000Z"
}Le header X-Gilbert-Signature contient un hexadécimal SHA-256 calculé sur le body brut avec le secret de votre endpoint (colonne secret de votre webhook enregistré, ou GILBERT_WEBHOOK_SECRET partagé en mode fallback : voir Enregistrer un endpoint).
import crypto from 'node:crypto'
import express from 'express'
const app = express()
// IMPORTANT : on lit le body brut, pas le JSON parsé
// (la signature est calculée sur les octets exacts).
app.post(
'/webhooks/gilbert',
express.raw({ type: 'application/json' }),
(req, res) => {
const signature = req.header('X-Gilbert-Signature') ?? ''
if (!verifySignature(req.body, signature)) {
return res.status(401).send('invalid signature')
}
const event = JSON.parse(req.body.toString('utf8'))
handleEvent(event) // votre logique métier
res.status(200).send('ok')
},
)
function verifySignature(rawBody, signature) {
const secret = process.env.GILBERT_WEBHOOK_SECRET
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex')
const a = Buffer.from(expected)
const b = Buffer.from(signature)
if (a.length !== b.length) return false
return crypto.timingSafeEqual(a, b)
}failed dans le journal côté Gilbert (webhook_deliveries), sans nouvelle tentative. Le journal n'existe que pour les endpoints enregistrés (rows product_webhooks) ; les livraisons via le fallback env ne sont pas journalisées.Il n'y a pas encore d'UI consolepour enregistrer un endpoint webhook (elle est à venir). Le provisioning se fait avec l'équipe Solve, par l'un des deux mécanismes que le dispatcher résout dans cet ordre :
gilbert.product_webhooks : url, secret (propre à l'endpoint), events[] (filtre optionnel : vide = tous), active, product_context. C'est le mode multi-endpoints recommandé.PRODUCT_WEBHOOK_URL_<PRODUCT_CONTEXT> + secret partagé GILBERT_WEBHOOK_SECRET, utilisé seulement si aucune row active n'existe pour votre product_context.Transmettez l'URL publique de votre endpoint à votre interlocuteur Solve ; le secret vous est remis au provisioning.
Gilbert ne peut pas appeler localhost. Utilisez un tunnel public type ngrok ou cloudflared et faites enregistrer l'URL publique comme endpoint (voir Enregistrer un endpoint).
# Exemple ngrok
ngrok http 3000
# → URL publique : https://abcd.ngrok-free.app
# Faites pointer votre webhook (row product_webhooks ou env)
# vers https://abcd.ngrok-free.app/webhooks/gilbert