Recevoir un webhook
S'abonner aux changements de statut des parcours, vérifier chaque appel, et répondre sans le perdre.
Exemple : votre CRM veut savoir quand un parcours se termine. Les règles détaillées sont dans Webhooks.
1S'abonner
POST https://staging-api.docage.com/Webhooks
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…
Content-Type: application/json
{
"Name": "Parcours terminés vers le CRM",
"Url": "https://crm.example.com/hooks/parcours",
"RecordTypeName": "Transaction",
"Events": [20],
"Scope": 0,
"Condition": "{\"OperatorType\":\"And\",\"Operands\":[{\"PropertyName\":\"GlobalStatus\",\"Comparison\":\"=\",\"Value\":\"3\"}]}"
}curl -X POST "https://staging-api.docage.com/Webhooks" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "Content-Type: application/json" \
-d '{
"Name": "Parcours terminés vers le CRM",
"Url": "https://crm.example.com/hooks/parcours",
"RecordTypeName": "Transaction",
"Events": [20],
"Scope": 0,
"Condition": "{\"OperatorType\":\"And\",\"Operands\":[{\"PropertyName\":\"GlobalStatus\",\"Comparison\":\"=\",\"Value\":\"3\"}]}"
}'using HttpClient client = new();
using HttpRequestMessage request = new(HttpMethod.Post, "https://staging-api.docage.com/Webhooks");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
request.Content = new StringContent("""
{
"Name": "Parcours terminés vers le CRM",
"Url": "https://crm.example.com/hooks/parcours",
"RecordTypeName": "Transaction",
"Events": [
20
],
"Scope": 0,
"Condition": "{\"OperatorType\":\"And\",\"Operands\":[{\"PropertyName\":\"GlobalStatus\",\"Comparison\":\"=\",\"Value\":\"3\"}]}"
}
""", Encoding.UTF8, "application/json");
using HttpResponseMessage response = await client.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException(result);const response = await fetch("https://staging-api.docage.com/Webhooks", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"Name": "Parcours terminés vers le CRM",
"Url": "https://crm.example.com/hooks/parcours",
"RecordTypeName": "Transaction",
"Events": [20],
"Scope": 0,
"Condition": "{\"OperatorType\":\"And\",\"Operands\":[{\"PropertyName\":\"GlobalStatus\",\"Comparison\":\"=\",\"Value\":\"3\"}]}"
})
});
const text = await response.text();
if (!response.ok) throw new Error(text);
const result = text ? JSON.parse(text) : null;La réponse est l'identifiant du webhook. L'événement 20 est le changement de statut d'un parcours : son envoi, sa fin, son
refus… ; Condition, un filtre écrit en texte JSON, ne retient que la fin (GlobalStatus à 3). GET https://staging-api.docage.com/Webhooks/Catalog liste les
types de fiches et leurs événements. Pour un parcours signé, ou ceux d'un seul modèle, voir
Être prévenu quand un parcours est signé.
2Lire le secret de signature
POST https://staging-api.docage.com/Webhooks/6a1b2c3d-0000-4000-8000-000000000090/Secret/Reveal
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…curl -X POST "https://staging-api.docage.com/Webhooks/6a1b2c3d-0000-4000-8000-000000000090/Secret/Reveal" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…"using HttpClient client = new();
using HttpRequestMessage request = new(HttpMethod.Post, "https://staging-api.docage.com/Webhooks/6a1b2c3d-0000-4000-8000-000000000090/Secret/Reveal");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
using HttpResponseMessage response = await client.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException(result);const response = await fetch("https://staging-api.docage.com/Webhooks/6a1b2c3d-0000-4000-8000-000000000090/Secret/Reveal", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`
}
});
const text = await response.text();
if (!response.ok) throw new Error(text);
const result = text ? JSON.parse(text) : null;La réponse porte le secret dans Value (whsec_…) : gardez-le dans la configuration de votre récepteur, comme un mot de passe.
3Vérifier chaque appel
Votre adresse reçoit un POST JSON, avec les en-têtes webhook-id, webhook-timestamp et webhook-signature. En Node.js :
const crypto = require('node:crypto');
function verifier(corpsBrut, entetes, secret) {
const id = entetes['webhook-id'];
const horodatage = entetes['webhook-timestamp'];
if (Math.abs(Date.now() / 1000 - Number(horodatage)) > 300) return false;
const cle = Buffer.from(secret.replace(/^whsec_/, ''), 'base64');
const attendue = crypto.createHmac('sha256', cle).update(`${id}.${horodatage}.${corpsBrut}`).digest('base64');
return entetes['webhook-signature'].split(' ').some(s => {
const recue = s.replace(/^v1,/, '');
return recue.length === attendue.length && crypto.timingSafeEqual(Buffer.from(recue), Buffer.from(attendue));
});
}Le corps se vérifie tel que reçu, avant toute lecture du JSON : un corps relu puis réécrit ne donne plus la même signature.
4Répondre, puis traiter
Répondez 200 dès la signature vérifiée, et traitez ensuite. Dans le corps, type vaut Transaction.TransactionStatusChange,
entity.id désigne le parcours, et changes.GlobalStatus donne l'ancien statut et le nouveau, par leur nom : Done est « Terminé »
(data.GlobalStatus le donne en nombre, 3). Retenez le webhook-id : un envoi qui vous parvient deux fois se reconnaît à lui.
5Essayer, et suivre les envois
POST https://staging-api.docage.com/Webhooks/{id}/Testenvoie un appel d'essai tout de suite, et rend son résultat.GET https://staging-api.docage.com/Webhooks/{id}/Deliveriesliste les envois du webhook : leur statut, leurs tentatives, le code que votre adresse a rendu.- Un webhook dont l'adresse échoue trop longtemps est mis en panne : ses envois sont retenus et son auteur est prévenu.
POST https://staging-api.docage.com/Webhooks/{id}/Reactivatele remet en service ; avec{ "ReplayHeld": true }, les envois retenus repartent — sans lui, ils sont annulés.