Suivre l'avancement du parapheur

Afficher en Markdown

Suivi & webhooks

Deux façons de suivre un parapheur

  • Interrogation (polling) — relire le parapheur (GET /api/workflows/{id}) et lire son avancement : progress, steps[].isStarted / isFinished, currentRecipientEmails / currentRecipientUsers (voir la fondation Parapheurs).
  • Webhooks — la plateforme pousse les événements vers un point de terminaison (endpoint) de votre application dès qu’ils surviennent. C’est le mécanisme recommandé pour une intégration réactive.

(À ne pas confondre avec les notifiedEvents du parapheur, qui déclenchent les notifications par e-mail aux personnes — voir la fondation Parapheurs. Les webhooks, eux, notifient votre application.)

Créer un webhook

Deux portées :

  • Webhook utilisateur — événements des parapheurs d’un utilisateur :
POST /api/users/{userId}/webhooks
{ "endpointUrl": "https://votre-app/hook/…", "notifiedEvents": [ "workflowFinished", "recipientFinished" ] }
  • Webhook global — événements de tous les parapheurs du tenant :
POST /api/tenants/{tenantId}/webhooks
{ "endpointUrl": "https://votre-app/hook/…", "notifiedEvents": [] }
ChampRôle
endpointUrlURL de votre point de terminaison (requis).
notifiedEventsLes événements auxquels s’abonner (requis).

La réponse renvoie le webhook créé (identifiant wbh_).

Les en-têtes personnalisés (customHeaders) se posent à la mise à jour, pas à la création. La création (POST) n’accepte que endpointUrl et notifiedEvents. Les customHeaders (en-têtes HTTP ajoutés à chaque appel, notamment le secret d’authentification — section Sécuriser et fiabiliser la réception) sont un champ de la mise à jour (PATCH, section Cycle de vie d’un webhook). On crée donc le webhook, puis on pose ses en-têtes :

PATCH /api/webhooks/{id}
If-Match: "<etag>"
{ "customHeaders": { "X-Webhook-Secret": "<secret>" } }

L’en-tête est ensuite livré tel quel à chaque appel.

Les événements (eventType / notifiedEvents)

Les types d’événements présents dans la référence :

DomaineeventType
ParapheurworkflowStarted, workflowStopped, workflowFinished, workflowFinishedDownloadLink, workflowFinishedDownloadLinkNoAttachment
DestinatairerecipientInvite, recipientFinished, recipientRefused
CommentairecommentCreated
CogestioncoManagerAdded, coManagerRemoved

Les variantes workflowFinishedDownloadLink… correspondent à la fin de parapheur assortie d’un lien de téléchargement des documents signés (option sendDownloadLink de l’étape, fondation Parapheurs) ; le suffixe …NoAttachment en désigne la variante sans pièces jointes. À la création d’un webhook (section Créer un webhook), on s’abonne au sous‑ensemble voulu via notifiedEvents.

Cette liste est validée par l’API : un notifiedEvents inconnu est rejeté. Il n’existe pas encore d’événement de clôture/archivage (workflowArchived) mais il est prévu dans la roadmap. Pour détecter la fin d’un parapheur, appuyez‑vous sur workflowFinished.

Le message reçu par votre endpoint

Un événement de webhook (identifiant wbe_) est léger : il indique ce qui s’est passé et sur quel parapheur, pas l’état complet. Champs :

ChampRôle
id (wbe_)Identifiant de l’événement (clé d’idempotence, section Sécuriser et fiabiliser la réception).
eventTypeType d’événement (section Les événements (eventType / notifiedEvents)).
workflowIdParapheur concerné (le cas échéant).
stepIdÉtape concernée (le cas échéant).
webhookId (wbh_)Webhook à l’origine de l’appel.
tenantId / userIdTenant ; userId renseigné pour un webhook utilisateur.
jobOperation(le cas échéant) Opération/tâche en cours à l’origine de l’événement.
created / updatedHorodatages de l’événement.

Règle d’or : relisez toujours l’état via l’API. À la réception d’un événement, faites un GET /api/workflows/{workflowId} pour obtenir l’état à jour (avancement, destinataires courants, documents…). Ne vous fiez pas au seul contenu de l’événement.

Les événements sont aussi consultables a posteriori : GET /api/webhookEvents/{id} et GET /api/webhookEvents (recherche).

Sécuriser et fiabiliser la réception

  • Rendre l’endpoint joignable — la plateforme doit pouvoir atteindre votre URL depuis l’extérieur. Ne placez pas de Basic Auth, d’allowlist IP ni d’autre protection d’infrastructure bloquante en amont : elle renverrait un 401/403 à la plateforme, qui considérerait la livraison comme un échec et relancerait en vain (voir plus bas). La vérification d’authenticité doit se faire au niveau applicatif, via le secret d’en-tête ci-dessous — et non par une couche d’infrastructure.
  • Authentifier l’appel — les appels de webhook ne sont pas signés (pas de HMAC, aucun en‑tête de signature ni de condensat). Authentifiez‑les via un secret partagé placé dans customHeaders (par ex. X-Webhook-Secret: …), renvoyé tel quel à chaque appel et vérifié par votre endpoint (comparaison à temps constant). Rappel : customHeaders se pose à la mise à jour du webhook (PATCH, section Créer un webhook), pas à la création. Servez votre endpoint en HTTPS avec une URL difficile à deviner.
  • Idempotence & relances — la livraison est au moins une fois : si votre endpoint répond autre chose qu’un 2xx (ou trop lentement), la plateforme relance le même événement, à intervalle croissant — de quelques minutes au début, s’espaçant ensuite (de l’ordre du quart d’heure) — et ce pendant une longue période (plus d’une heure et de nombreuses tentatives observées). Une brève indisponibilité de votre endpoint est ainsi rattrapée automatiquement ; en contrepartie, un même événement peut vous parvenir plusieurs fois : traitez‑le une seule fois en vous appuyant sur son identifiant wbe_ (section Le message reçu par votre endpoint).
  • Répondre vite — renvoyez un 2xx rapidement, puis traitez de façon asynchrone ; une réponse lente ou en erreur déclenche une relance.

Cycle de vie d’un webhook

Les journaux : une aide au diagnostic, pas un mécanisme d’intégration

Règle, avant tout le reste. Les journaux du Workflow Manager reflètent son exécution interne. Ils ne sont pas destinés à être analysés de façon programmatique, et rien de ce qu’ils contiennent ne constitue un engagement d’interface : le vocabulaire des opérations peut changer sans préavis. Pour suivre un parapheur, servez-vous de l’interrogation et des webhooks (section Deux façons de suivre un parapheur) ; pour réagir à une erreur, branchez votre logique sur le champ code (fondation Conventions transverses > Réponses d’erreur). Cette section décrit les journaux pour les deux seuls usages où ils ont leur place : comprendre ce qui s’est passé pendant le développement, et fournir un élément au support de Goodflag.

Cela dit, à ce titre, ils rendent un service qu’aucune autre source ne rend : ils disent ce que la plateforme a fait, horodaté à l’opération, là où l’interrogation ne dit que l’état courant. C’est décisif face à une question comme tel message a-t-il été émis ?, à laquelle une boîte de réception ne répond pas de façon fiable — un message jamais envoyé et un message mis en quarantaine par un filtrage entrant y produisent le même constat.

Le journal de l’étape. La relecture d’un parapheur (GET /api/workflows/{id}) expose un tableau logs sur le parapheur et sur chacune de ses étapes — c’est ce dernier qui porte l’essentiel. Chaque entrée comporte un horodatage created, une operation, et selon les cas recipientEmail, recipientUserId ou evidenceId. Vocabulaire observé — non documenté par la référence, donc susceptible d’évoluer — sur un parcours de signature mené à son terme :

operationSignification
startL’étape a démarré.
notifyWorkflowStartedL’événement « parapheur démarré » a été levé.
signUn destinataire a signé ; evidenceId référence la preuve produite.
notifyRecipientFinishedL’événement « ce destinataire a terminé » a été levé.
notifyWorkflowFinishedL’événement « parapheur terminé » a été levé.

Une entrée notify… atteste un événement levé, pas un e-mail expédié. notifyWorkflowStarted figure au journal même lorsque notifiedEvents est vide et qu’aucun message ne part. Ces entrées se lisent donc comme la trace du déclencheur, l’envoi effectif dépendant ensuite de la configuration des notifications (fondation Messages & notifications). En sens inverse, l’absence de toute entrée relative à un événement signifie que la plateforme ne l’a pas levé — donc qu’aucun message n’a pu en découler.

Le journal d’audit du tenant. GET /api/logs — « Search in all the logs » — restitue les opérations d’API enregistrées, filtrables notamment par items.entityId (l’identifiant d’un parapheur convient), items.userId, items.operation, items.errorCode et items.created. Chaque entrée porte operation, created, duration, errorCode / errorDetail, appVersion et un bloc variables contenant la requête HTTP reçue. Sur un parapheur créé puis lancé par l’API, on lit par exemple createWorkflow, createWorkflowParts, updateWorkflow, refreshWorkflowViewAuthorizedUsers, processWorkflow, createWorkflowInvite, puis cleanWorkflowData. Pagination habituelle, 50 entrées par page au maximum ; POST /api/logs/exports en produit un export.

Ce sont les entrées log_… que désigne le champ logId des réponses d’erreur (fondation Conventions transverses > Réponses d’erreur) : le même journal, consulté par la recherche plutôt que par une référence isolée. La recommandation y est la même — transmettre la référence au support plutôt que de chercher à interpréter le journal soi-même, car certains incidents ne s’expliquent que depuis l’intérieur de la plateforme.

Portée du jeton. L’accès à /api/logs peut être refusé à un jeton applicatif ordinaire (UserNotAllowed) : ce journal relève de l’administration du tenant. C’est cohérent avec son statut — un outil d’exploitation, pas une dépendance d’intégration. Ne bâtissez pas de supervision qui suppose cet accès.