Être prévenu quand un parcours est signé
Créer un webhook qui appelle votre adresse quand un parcours se termine — pour tous vos parcours, ou ceux d'un modèle —, puis télécharger ses documents signés.
Plutôt que de relire vos parcours, abonnez votre application à leur fin : un webhook appelle votre adresse dès qu'un parcours est terminé. Les règles générales — signature des appels, réponses, relances — sont dans Webhooks.
1Créer le webhook
POST https://staging-api.docage.com/Webhooks
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…
Content-Type: application/json
{
"Name": "Parcours signés vers le CRM",
"Url": "https://crm.example.com/hooks/parcours-signes",
"RecordTypeName": "Transaction",
"Events": [20],
"Scope": 0,
"Condition": "{\"OperatorType\":\"And\",\"Operands\":[{\"PropertyName\":\"GlobalStatus\",\"Comparison\":\"=\",\"Value\":\"3\"},{\"PropertyName\":\"SignatureStatus\",\"Comparison\":\"=\",\"Value\":\"5\"}]}"
}curl -X POST "https://staging-api.docage.com/Webhooks" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "Content-Type: application/json" \
-d '{
"Name": "Parcours signés vers le CRM",
"Url": "https://crm.example.com/hooks/parcours-signes",
"RecordTypeName": "Transaction",
"Events": [20],
"Scope": 0,
"Condition": "{\"OperatorType\":\"And\",\"Operands\":[{\"PropertyName\":\"GlobalStatus\",\"Comparison\":\"=\",\"Value\":\"3\"},{\"PropertyName\":\"SignatureStatus\",\"Comparison\":\"=\",\"Value\":\"5\"}]}"
}'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 signés vers le CRM",
"Url": "https://crm.example.com/hooks/parcours-signes",
"RecordTypeName": "Transaction",
"Events": [
20
],
"Scope": 0,
"Condition": "{\"OperatorType\":\"And\",\"Operands\":[{\"PropertyName\":\"GlobalStatus\",\"Comparison\":\"=\",\"Value\":\"3\"},{\"PropertyName\":\"SignatureStatus\",\"Comparison\":\"=\",\"Value\":\"5\"}]}"
}
""", 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 signés vers le CRM",
"Url": "https://crm.example.com/hooks/parcours-signes",
"RecordTypeName": "Transaction",
"Events": [20],
"Scope": 0,
"Condition": "{\"OperatorType\":\"And\",\"Operands\":[{\"PropertyName\":\"GlobalStatus\",\"Comparison\":\"=\",\"Value\":\"3\"},{\"PropertyName\":\"SignatureStatus\",\"Comparison\":\"=\",\"Value\":\"5\"}]}"
})
});
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, en chaîne JSON.
RecordTypeName:Transaction, le parcours — écrit ainsi, majuscule comprise.Events:20, le changement de statut d'un parcours.Scope:0, tous les parcours que vous voyez.Condition: le filtre que le parcours doit remplir, ici terminé (GlobalStatusà3) et signé (SignatureStatusà5). C'est un filtre de liste — voir Lire des listes —, écrit en texte JSON : la valeur deConditionest une chaîne, pas un objet.
2Ce qui déclenche l'appel
Le changement de statut part à chaque changement : l'envoi du parcours, sa fin, son refus, son annulation, son expiration. Sans
Condition, votre adresse serait appelée pour tous. Avec celle-ci, seule la fin d'un parcours signé l'appelle.
Chaque ligne ci-dessous est un critère à placer dans Operands, le tout écrit en texte comme plus haut :
| Vous voulez être prévenu… | Le critère |
|---|---|
| quand un parcours se termine, signé | GlobalStatus à 3 et SignatureStatus à 5 (ci-dessus) |
| quand un parcours se termine, quel qu'il soit | GlobalStatus à 3 |
| quand un parcours s'arrête, terminé ou non | {"PropertyName":"GlobalStatus","Comparison":"isin","Value":[3,4,5,6]} |
| quand un parcours est refusé | GlobalStatus à 4 |
« Terminé » veut dire que chaque participant a fait toutes ses actions : un paiement qui suit la signature passe avant la fin. Pour être prévenu dès la dernière signature, même si un paiement suit, surveillez la signature plutôt que le statut :
{
"Name": "Signatures faites",
"Url": "https://crm.example.com/hooks/signatures",
"RecordTypeName": "Transaction",
"Events": [2, 20],
"Scope": 0,
"WatchedFields": [ { "FieldName": "SignatureStatus", "ToValue": "Done" } ]
}3Les parcours d'un seul modèle
{
"Name": "Contrats de prestation signés",
"Url": "https://crm.example.com/hooks/contrats",
"RecordTypeName": "Transaction",
"Events": [20],
"Scope": 1,
"ScopeRecordId": "aaaaaaaa-0000-4000-8000-000000000001",
"Condition": "{\"OperatorType\":\"And\",\"Operands\":[{\"PropertyName\":\"GlobalStatus\",\"Comparison\":\"=\",\"Value\":\"3\"}]}"
}Scope 1 et le modèle dans ScopeRecordId : seuls les parcours tirés de ce modèle appellent votre adresse. Scope 2, avec
l'identifiant d'un parcours, n'écoute que lui.
4Ce que vous recevez
Un POST signé à votre adresse :
{
"id": "0f7c2a1e-0000-4000-8000-000000000091",
"type": "Transaction.TransactionStatusChange",
"timestamp": "2026-11-09T10:15:42.123Z",
"webhook": { "id": "6a1b2c3d-0000-4000-8000-000000000090", "name": "Parcours signés vers le CRM" },
"organizationId": "5c6d7e8f-0000-4000-8000-000000000042",
"entity": { "type": "Transaction", "id": "cccccccc-0000-4000-8000-000000000003", "name": "Contrat de prestation" },
"changes": {
"GlobalStatus": { "from": "Active", "to": "Done" },
"SignatureStatus": { "from": "Started", "to": "Done" }
},
"data": { "Id": "cccccccc-0000-4000-8000-000000000003", "GlobalStatus": 3, "SignatureStatus": 5, "TransactionMembers": [ … ], "TransactionFiles": [ … ] }
}changesdonne, pour chaque propriété changée, l'ancienne valeur et la nouvelle, par leur nom (Active,Done).dataest le parcours, tel que l'API le rend — ses statuts en nombres —, vu avec les droits de l'auteur du webhook : ses participants (avec leurSignDate) et ses documents, sans leur contenu.
Vérifiez la signature de chaque appel, répondez 200, puis traitez : voir Recevoir un webhook.
5Télécharger les documents signés
Avec l'identifiant du parcours (entity.id) :
GET https://staging-api.docage.com/Transactions/DownloadDocumentsAndProofFile/cccccccc-0000-4000-8000-000000000003
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…curl "https://staging-api.docage.com/Transactions/DownloadDocumentsAndProofFile/cccccccc-0000-4000-8000-000000000003" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…"using HttpClient client = new();
using HttpRequestMessage request = new(HttpMethod.Get, "https://staging-api.docage.com/Transactions/DownloadDocumentsAndProofFile/cccccccc-0000-4000-8000-000000000003");
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/Transactions/DownloadDocumentsAndProofFile/cccccccc-0000-4000-8000-000000000003", {
headers: {
Authorization: `Bearer ${token}`
}
});
const text = await response.text();
if (!response.ok) throw new Error(text);
const result = text ? JSON.parse(text) : null;L'appel part au changement de statut, juste avant que le parcours ne se clôture : son cachet, son horodatage et son fichier de
preuve se posent dans les secondes qui suivent. Dans data, les documents ne sont donc pas encore dans leur version finale. Téléchargez
après réception, et si la preuve n'est pas prête (400, « pas encore prêt »), réessayez un peu plus tard : voir
Télécharger le fichier de preuve.
Les pièges
Conditionest un texte. Un objet JSON à sa place fait refuser la création :400, « Le corps ne se lit pas comme un webhook. » Un filtre illisible pour le serveur — une propriété inconnue, une valeur qui ne se convertit pas — répond422.- La numérotation des statuts a changé : « terminé » vaut
3dansGlobalStatus. La forme de la version précédente (EntityTypeName,Action,TransactionStatusTarget, où « signé » valait5) ne sert plus qu'aux abonnements d'hier : envoyée telle quelle, elle est refusée (422), l'ancien nomTransactionStatusTargetn'étant plus lu. Écrivez un nouvel abonnement comme ci-dessus — voir Migration V1 > V2. - Un webhook ne reçoit que sa propre organisation, et ce que son auteur voit. Un intégrateur crée un webhook par organisation
cliente, avec
ParentOrganizationId. - L'adresse est en HTTPS, publique.
- Être prévenu à chaque signature d'un parcours à plusieurs signataires n'a pas d'événement à soi : l'abonnement à la modification
du parcours (
Events[2]) appelle à chaque geste, et bien plus souvent — comparez alors lesSignDatedes participants à ce que vous savez déjà. POST https://staging-api.docage.com/Webhooks/{id}/Test, avec{ "RecordId": "<identifiant d'un parcours>" }, envoie tout de suite un appel d'essai, sans juger la condition.