Déclencheurs
Référence complète de chaque déclencheur des Workflows d'automatisation de Certivant — quand chaque événement se déclenche, son contexte de données, des exemples de charges utiles, la configuration du sujet et de la portée, et les modèles de filtre DSL.
Un Déclencheur est le nœud d'entrée unique de chaque Workflow d'automatisation. Il déclare quel événement démarre le workflow, quels objets cet événement doit concerner (le sujet et la portée), et un filtre facultatif qui restreint davantage les événements admissibles. Lorsque la plateforme émet un événement correspondant, une KycWorkflowRun est créée et le workflow s'exécute nœud par nœud.
Cette page documente chaque déclencheur que le moteur émet réellement, en se fondant sur les points d'émission de la plateforme. Chaque entrée indique quand il se déclenche, le context exact qu'il transporte, une charge utile d'exemple réelle, et la manière de le cibler et de le filtrer.
Le catalogue des déclencheurs est une énumération fixe du moteur (
WorkflowTriggerEvent). Le sélecteur de l'éditeur, cette page, le schéma de validation de l'API et le répartiteur lisent tous la même liste : chaque déclencheur que vous pouvez sélectionner est donc un déclencheur que la plateforme émet réellement — aucun n'est purement décoratif. Quelques capacités qui n'ont pas encore de point d'émission sont volontairement exclues du sélecteur et listées dans Déclencheurs à venir avec l'alternative fonctionnelle, afin que vous ne construisiez jamais un workflow qui ne s'exécute silencieusement jamais.
Fonctionnement des déclencheurs
Les déclencheurs ne sont pas interrogés périodiquement. Chacun correspond à un point réel de la plateforme où du code appelle le répartiteur :
emitter (API route / worker / cron)
└─ enqueueWorkflowDispatch({ organizationId, event, kycRequestId?, …, context })
└─ workflow.worker → dispatchWorkflowEvent(input)
├─ load active workflows whose trigger.event === input.event
├─ narrow by subject + scope (form / applicant / document / check)
├─ evaluate the optional DSL filter (against input.context)
└─ create + run a KycWorkflowRun per match
Trois propriétés comptent pour les intégrateurs :
- Envoi sans attente de réponse (fire-and-forget). L'envoi est encapsulé de sorte qu'un échec ne casse jamais l'action d'origine. Si un envoi de document déclenche un workflow et que l'envoi échoue, l'envoi du document réussit tout de même. Considérez les effets secondaires d'un workflow comme exécutés à terme, et non de façon synchrone avec l'appel API.
- Un événement, plusieurs exécutions. Un seul événement peut démarrer chaque workflow actif qui lui correspond. Utilisez le sujet, la portée et les filtres pour garder cette diffusion intentionnelle.
- Le contexte est capturé une seule fois. L'objet
contextde l'événement est placé dans la première entrée du journal de l'exécution. Les nœuds Condition, les modèles d'action et les jetons de fusion lisent cet instantané : ils n'interrogent pas à nouveau l'état en direct.
L'enveloppe d'envoi
Chaque émission produit un WorkflowEventInput. Les champs autres que organizationId et event sont facultatifs et dépendent de l'événement :
| Champ | Type | Présent lorsque |
|---|---|---|
organizationId | chaîne | toujours |
event | WorkflowTriggerEvent | toujours |
kycRequestId | chaîne | null | l'événement concerne une vérification précise (la plupart des événements) |
formId | chaîne | null | form.submitted |
formKind | "kyc" | "kyb" | null | form.submitted |
applicantId | chaîne | null | événements liés à un demandeur |
documentType | chaîne | null | document.* |
checkKind | chaîne | null | check.completed, liveness.*, sanctions.* |
context | objet | faits propres à l'événement (voir chaque déclencheur ci-dessous) |
workflowId | chaîne | uniquement lors d'un appel direct de trigger_workflow |
chainDepth | nombre | profondeur de chaînage d'un workflow à un autre (5 au maximum) |
subject, scope, filter, formIds, schedule, scoreMin et scoreMax sont des propriétés du déclencheur que vous configurez — et non de l'événement d'exécution. Le répartiteur les utilise pour décider si un événement entrant correspond à votre workflow.
La définition du déclencheur
Voici la structure enregistrée dans le trigger d'un workflow (WorkflowTrigger) :
interface WorkflowTrigger {
event: WorkflowTriggerEvent; // which event starts the workflow
subject?: "form" | "applicant" // the kind of object it binds to
| "document" | "check"; // (absent → legacy org-wide)
scope?: {
mode: "specific" | "category" | "all";
ids?: string[]; // specific-mode object ids
category?: string; // category-mode key (see below)
};
filter?: string; // optional DSL predicate over context
formIds?: string[]; // legacy form-binding (pre-subject)
schedule?: string; // schedule only — cron expression
scoreMin?: number; // score.threshold convenience bound
scoreMax?: number; // score.threshold convenience bound
}
Sujet et portée
Le sujet est le type d'objet que le déclencheur surveille ; la portée le restreint à des objets précis, à une catégorie, ou à tout. C'est ce qui empêche un déclencheur de se déclencher accidentellement pour toute l'organisation.
| Sujet | Événements valides | Modes de scope.mode | Signification de category |
|---|---|---|---|
form | form.submitted | specific (identifiants de formulaires) · category · all | "kyc" ou "kyb" |
applicant | request.*, score.threshold, edd.triggered, verification.expired | specific (identifiants de demandeurs) · all | — |
document | document.uploaded, document.failed | specific · category · all | un type de document, par exemple "passport" |
check | check.completed, liveness.*, sanctions.* | specific · category · all | un type de contrôle, par exemple "sanctions" |
| (aucun) | schedule, inbound.webhook, monitoring.alert, kyb.*, reviewer.decision, conference.completed | — | pour toute l'organisation |
Modes de portée :
specific: ne correspond qu'auxidslistés (par exemple, ne se déclencher que pour ce formulaire d'accueil). Si chaque identifiant d'une portéespecificpointe vers un objet supprimé, le déclencheur est orphelin : le répartiteur refuse de le déclencher (il reste inactif au lieu de retomber silencieusement sur « tous »), et l'éditeur affiche un badge « Nécessite une nouvelle liaison ».category: correspond à une classe d'objets (tous les formulaires KYC, tous les passeports, toutes les vérifications de sanctions).all: correspond à tout objet du type de sujet dans l'organisation.
Un déclencheur sans subject conserve le comportement historique : pour toute l'organisation, restreint éventuellement par formIds pour form.submitted. Les nouveaux workflows doivent toujours définir un sujet et une portée.
Filtre (DSL)
filter est un prédicat booléen facultatif utilisant la même grammaire d'expressions que le DSL des politiques de décision. Il s'exécute sur le context de l'événement. Un filtre vide se déclenche à chaque événement de ce type. Voir Filtrer avec le DSL.
Catégories de déclencheurs
| Catégorie | Déclencheurs |
|---|---|
| Entrée | form.submitted, request.created |
| Cycle de vie / statut | request.status_changed, request.approved, request.rejected, request.escalated, request.resubmitted, request.documents_ready |
| Documents | document.uploaded, document.failed |
| Signaux de vérification | check.completed, liveness.passed, liveness.failed, face_match.passed, face_match.failed, sanctions.hit, sanctions.cleared, pep.match, kyb.match_found, kyb.match_not_found, kyb.ubo_completed |
| Révision humaine | reviewer.decision, reviewer.assigned, reviewer.comment_added, edd.triggered |
| Risque / score | score.threshold, score.updated |
| Temps / surveillance | schedule, verification.expired, monitoring.alert |
| Externe / intégration | inbound.webhook, conference.completed |
Référence des déclencheurs
Chaque entrée ci-dessous suit la même structure : Quand il se déclenche, Contexte de données, Exemple d'événement et Exemple d'utilisation dans un workflow. Chaque charge utile correspond à la forme réelle d'envoi produite par le code émetteur.
Entrée
form.submitted
« Un formulaire est soumis » · sujet form
Quand il se déclenche. Un formulaire de vérification public et hébergé est soumis par un demandeur. Émis depuis la route des formulaires publics au moment où une soumission crée sa KycRequest. L'événement associé request.created est émis en même temps.
Contexte de données.
| Champ | Type | Remarques |
|---|---|---|
type | chaîne | Type de demandeur à la soumission, par exemple "INDIVIDUAL" |
Au premier niveau : formId, formKind ("kyc"/"kyb"), kycRequestId, applicantId.
Exemple d'événement.
{
"organizationId": "org_01H...",
"event": "form.submitted",
"kycRequestId": "req_01J...",
"formId": "form_01H...",
"formKind": "kyc",
"context": { "type": "INDIVIDUAL" }
}
Exemple d'utilisation dans un workflow. Lier un workflow d'envoi de courriel de bienvenue et de demande de documents à un seul formulaire d'accueil précis :
trigger:
event: form.submitted
subject: form
scope: { mode: specific, ids: ["form_01H..."] }
request.created
« Une vérification est créée » · sujet applicant
Quand il se déclenche. Une demande de vérification est créée — via POST /api/v1/kyc/requests (201) ou en complément d'un form.submitted hébergé. Il se déclenche avant tout envoi de document et avant l'exécution de tout contrôle : utilisez-le pour l'acheminement à l'admission, et non pour les résultats.
Contexte de données.
| Champ | Type | Remarques |
|---|---|---|
applicant.country | chaîne | null | ISO-3166 alpha-2, à partir de la nationalité du demandeur |
applicant.type | chaîne | "INDIVIDUAL" ou "BUSINESS" |
type | chaîne | Type de la demande (INDIVIDUAL / BUSINESS) |
Exemple d'événement.
{
"organizationId": "org_01H...",
"event": "request.created",
"kycRequestId": "req_01J...",
"applicantId": "app_01J...",
"context": {
"applicant": { "country": "CA", "type": "INDIVIDUAL" },
"type": "INDIVIDUAL"
}
}
Exemple d'utilisation dans un workflow. Acheminer les demandeurs entreprises vers une file d'admission KYB :
trigger:
event: request.created
subject: applicant
scope: { mode: all }
filter: 'applicant.type = "BUSINESS"'
Cycle de vie / statut
Ces événements se déclenchent une fois l'orchestration finalisée. Ils transportent tous le même contexte de décision. Les événements spécifiques (approved/rejected/escalated) se déclenchent en plus de l'événement générique request.status_changed ; écoutez donc l'un ou l'autre, mais pas les deux, afin d'éviter une double exécution.
Contexte de données commun (request.approved, request.rejected, request.escalated, request.status_changed) :
| Champ | Type | Remarques |
|---|---|---|
status | chaîne | Nouveau statut, par exemple "APPROVED", "REJECTED", "ESCALATED", "PENDING_SECOND_REVIEW" |
decision | chaîne | null | Verdict du moteur de score : "approve" / "reject" / "escalate" |
overallScore | nombre | null | Score de confiance de 0 à 100 (plus il est élevé, plus le risque est faible) |
applicant.country | chaîne | null | ISO-3166 alpha-2 |
applicant.type | chaîne | INDIVIDUAL / BUSINESS |
request.approved
« Une vérification est approuvée » · sujet applicant
Quand il se déclenche. L'orchestration finalise la demande à l'état APPROVED.
{
"organizationId": "org_01H...",
"event": "request.approved",
"kycRequestId": "req_01J...",
"applicantId": "app_01J...",
"context": {
"status": "APPROVED",
"decision": "approve",
"overallScore": 88,
"applicant": { "country": "US", "type": "INDIVIDUAL" }
}
}
Exemple d'utilisation dans un workflow. Générer et envoyer par courriel un rapport de vérification uniquement pour les approbations à haute confiance :
trigger:
event: request.approved
subject: applicant
scope: { mode: all }
filter: 'overallScore >= 80'
request.rejected
« Une vérification est rejetée » · sujet applicant
Quand il se déclenche. L'orchestration finalise la demande à l'état REJECTED. Contexte identique à request.approved, avec status: "REJECTED" et decision: "reject".
Exemple d'utilisation dans un workflow. Prévenir le gestionnaire assigné et étiqueter le demandeur en cas de rejet :
trigger:
event: request.rejected
subject: applicant
scope: { mode: all }
request.escalated
« Une vérification est escaladée » · sujet applicant
Quand il se déclenche. L'orchestration (ou une action escalate d'un workflow précédent) fait passer la demande à ESCALATED / révision humaine. Contexte identique à la structure commune avec status: "ESCALATED".
Exemple d'utilisation dans un workflow. Planifier une conférence d'identification en direct chaque fois qu'une demande est escaladée :
trigger:
event: request.escalated
subject: applicant
scope: { mode: all }
request.status_changed
« Le statut d'une vérification change » · sujet applicant
Quand il se déclenche. Toujours émis lors d'un changement de statut finalisé, en même temps que tout événement de décision spécifique. Utilisez-le pour réagir à des statuts qui n'ont pas d'événement dédié (par exemple PENDING_SECOND_REVIEW, CANCELLED).
Exemple d'utilisation dans un workflow. Réagir uniquement à une transmission à deux réviseurs :
trigger:
event: request.status_changed
subject: applicant
scope: { mode: all }
filter: 'status = "PENDING_SECOND_REVIEW"'
request.resubmitted
« Une vérification est soumise à nouveau » · sujet applicant
Quand il se déclenche. Un réviseur renvoie une demande pour nouvelle soumission depuis le tableau de bord ; la demande revient à PENDING_UPLOAD.
Contexte de données.
| Champ | Type | Remarques |
|---|---|---|
status | chaîne | "PENDING_UPLOAD" |
kind | chaîne | "resubmit" |
{
"organizationId": "org_01H...",
"event": "request.resubmitted",
"kycRequestId": "req_01J...",
"context": { "status": "PENDING_UPLOAD", "kind": "resubmit" }
}
Exemple d'utilisation dans un workflow. Renvoyer au demandeur son lien d'envoi et démarrer une minuterie de rappel.
request.documents_ready
« Tous les documents requis sont envoyés » · sujet applicant
Quand il se déclenche. Le demandeur vient d'envoyer le dernier élément de sa liste de documents requis, de sorte que la demande passe à DOCUMENTS_READY. Émis depuis les deux voies d'envoi — l'API des documents du tableau de bord et la session de vérification hébergée — uniquement lors de l'envoi qui vide l'ensemble des documents en attente. C'est le signal concret « le demandeur a complété toutes les étapes requises ».
Contexte de données.
| Champ | Type | Remarques |
|---|---|---|
status | chaîne | "DOCUMENTS_READY" |
{
"organizationId": "org_01H...",
"event": "request.documents_ready",
"kycRequestId": "req_01J...",
"context": { "status": "DOCUMENTS_READY" }
}
Exemple d'utilisation dans un workflow. Prévenir le réviseur assigné dès qu'un dossier est prêt à être examiné :
trigger:
event: request.documents_ready
subject: applicant
scope: { mode: all }
Documents
document.uploaded
« Un document est envoyé » · sujet document
Quand il se déclenche. À chaque envoi d'un document — via l'API du tableau de bord ou une session de vérification hébergée. Un événement par document.
Contexte de données.
| Champ | Type | Remarques |
|---|---|---|
document.type | chaîne | par exemple "passport", "id_card", "drivers_license" |
document.side | chaîne | null | "front" / "back" (null lorsque ce n'est pas applicable) |
Au premier niveau : documentType, kycRequestId.
{
"organizationId": "org_01H...",
"event": "document.uploaded",
"kycRequestId": "req_01J...",
"documentType": "passport",
"context": { "document": { "type": "passport", "side": "front" } }
}
Exemple d'utilisation dans un workflow. Ne se déclencher que pour les envois de justificatifs de domicile :
trigger:
event: document.uploaded
subject: document
scope: { mode: category, category: "utility_bill" }
document.failed
« Un document échoue à la validation » · sujet document
Quand il se déclenche. L'orchestration marque un document comme failed (l'OCR n'a pas pu s'exécuter, l'authenticité l'a rejeté, etc.). Un événement par document en échec. Même structure de contexte que document.uploaded.
{
"organizationId": "org_01H...",
"event": "document.failed",
"kycRequestId": "req_01J...",
"documentType": "drivers_license",
"context": { "document": { "type": "drivers_license", "side": "back" } }
}
Exemple d'utilisation dans un workflow. Demander automatiquement une nouvelle capture de tout document en échec.
Signaux de vérification
check.completed
« Un contrôle de vérification se termine » · sujet check
Quand il se déclenche. Une fois par contrôle individuel qui se termine pendant une exécution de l'orchestration (OCR, correspondance faciale, vivacité, sanctions, authenticité du document, lecture NFC…). C'est le signal générique et à haute fréquence : pour un résultat clair et unique, préférez liveness.* / sanctions.*.
Diffusion protégée. Comme plusieurs contrôles se terminent pour chaque demande, la plateforme vérifie d'abord si l'organisation possède au moins un workflow actif à l'écoute de
check.completed. Sans écouteur, aucune tâche d'envoi n'est créée. Le repère par exécution empêche une nouvelle vérification de réémettre les contrôles d'une exécution précédente.
Contexte de données.
| Champ | Type | Remarques |
|---|---|---|
check.type | chaîne | Type de contrôle, par exemple "sanctions", "face_match", "ocr" |
check.status | chaîne | "passed" / "failed" / "error" |
check.provider | chaîne | null | Fournisseur ayant exécuté le contrôle |
check.score | nombre | null | Score du fournisseur, lorsqu'il est applicable |
Au premier niveau : checkKind, kycRequestId.
{
"organizationId": "org_01H...",
"event": "check.completed",
"kycRequestId": "req_01J...",
"checkKind": "face_match",
"context": {
"check": { "type": "face_match", "status": "passed",
"provider": "azure_face", "score": 0.93 }
}
}
Exemple d'utilisation dans un workflow. Réagir à la fin de tout contrôle de sanctions, quel que soit le résultat (pour un résultat réussite/échec clair, préférez les événements dédiés sanctions.* / face_match.* ci-dessous) :
trigger:
event: check.completed
subject: check
scope: { mode: category, category: "sanctions" }
liveness.passed / liveness.failed
« Vivacité réussie » / « Vivacité échouée » · sujet check (
liveness)
Quand il se déclenche. Après l'orchestration, lorsque le contrôle de vivacité a enregistré un booléen isLive sans ambiguïté. passed lorsque isLive vaut vrai, failed lorsqu'il vaut faux. Un contrôle de vivacité en erreur ou ambigu n'émet aucun des deux.
Contexte de données.
| Champ | Type | Remarques |
|---|---|---|
liveness.isLive | booléen | Indique si le demandeur a réussi le contrôle de vivacité |
liveness.confidence | nombre | null | Confiance de 0 à 1 |
{
"organizationId": "org_01H...",
"event": "liveness.failed",
"kycRequestId": "req_01J...",
"checkKind": "liveness",
"context": { "liveness": { "isLive": false, "confidence": 0.31 } }
}
Exemple d'utilisation dans un workflow. Escalader en cas d'échec de vivacité à faible confiance :
trigger:
event: liveness.failed
subject: check
scope: { mode: all }
filter: 'liveness.confidence < 0.5'
face_match.passed / face_match.failed
« Correspondance faciale réussie » / « Correspondance faciale échouée » · sujet check (
face_match)
Quand il se déclenche. Après l'orchestration, lorsque le contrôle de correspondance faciale (selfie ↔ portrait du document) a enregistré un booléen isMatch sans ambiguïté. passed lorsque isMatch vaut vrai, failed lorsqu'il vaut faux. Un contrôle en erreur ou ambigu n'émet aucun des deux.
Contexte de données.
| Champ | Type | Remarques |
|---|---|---|
faceMatch.isMatch | booléen | Indique si le selfie correspond au visage du document |
faceMatch.confidence | nombre | null | Confiance de 0 à 1 |
{
"organizationId": "org_01H...",
"event": "face_match.failed",
"kycRequestId": "req_01J...",
"checkKind": "face_match",
"context": { "faceMatch": { "isMatch": false, "confidence": 0.42 } }
}
Exemple d'utilisation dans un workflow. Escalader pour examen manuel tout échec de correspondance faciale :
trigger:
event: face_match.failed
subject: check
scope: { mode: all }
sanctions.hit / sanctions.cleared
« Correspondance de sanctions » / « Sanctions levées » · sujet check (
sanctions)
Quand il se déclenche. Après l'orchestration, à partir du résultat enregistré des sanctions et listes de surveillance. hit lorsqu'une correspondance atteint le seuil (ou qu'une correspondance quelconque est présente) ; cleared dans le cas contraire. Une correspondance qui concerne spécifiquement une personne politiquement exposée déclenche aussi l'événement dédié pep.match : écoutez-le si vous ne vous intéressez qu'à l'exposition PEP.
Contexte de données.
| Champ | Type | Remarques |
|---|---|---|
sanctions.isMatch | booléen | Vrai en cas de correspondance |
sanctions.matchCount | nombre | Nombre de correspondances dans les listes de surveillance |
sanctions.topScore | nombre | null | Score de la correspondance la plus forte, de 0 à 1 |
sanctions.listsChecked | chaîne[] | Listes contrôlées (OFAC, OpenSanctions, …) |
{
"organizationId": "org_01H...",
"event": "sanctions.hit",
"kycRequestId": "req_01J...",
"checkKind": "sanctions",
"context": {
"sanctions": { "isMatch": true, "matchCount": 2, "topScore": 0.91,
"listsChecked": ["OFAC", "OpenSanctions"] }
}
}
Exemple d'utilisation dans un workflow. Escalader et prévenir la conformité en cas de forte correspondance de sanctions :
trigger:
event: sanctions.hit
subject: check
scope: { mode: all }
filter: 'sanctions.topScore >= 0.85'
pep.match
« Correspondance PEP trouvée » · sujet check (
sanctions)
Quand il se déclenche. Après l'orchestration, lorsque le résultat des sanctions et listes de surveillance contient au moins une correspondance dont le type d'entité est une personne politiquement exposée. Il repose sur le même criblage que sanctions.*, mais ne se déclenche que lorsqu'une entrée PEP est présente : une simple correspondance de liste de surveillance sans entrée PEP ne le déclenche pas.
Contexte de données.
| Champ | Type | Remarques |
|---|---|---|
pep.isMatch | booléen | Toujours true (l'événement ne se déclenche que sur une correspondance PEP) |
pep.matchCount | nombre | Nombre de correspondances PEP |
{
"organizationId": "org_01H...",
"event": "pep.match",
"kycRequestId": "req_01J...",
"checkKind": "sanctions",
"context": { "pep": { "isMatch": true, "matchCount": 1 } }
}
Exemple d'utilisation dans un workflow. Déclencher automatiquement la Diligence raisonnable renforcée et acheminer vers la conformité dès qu'une PEP est détectée :
trigger:
event: pep.match
subject: check
scope: { mode: all }
kyb.match_found / kyb.match_not_found
« Entreprise trouvée au registre KYB » / « Aucune correspondance KYB » · pour toute l'organisation
Quand il se déclenche. Pour les demandes BUSINESS uniquement, après que l'orchestration a enregistré le résultat de vérification au registre des entreprises. verified → kyb.match_found ; failed → kyb.match_not_found. Tout autre statut (en attente ou absent) est ambigu et n'émet rien.
Contexte de données.
| Champ | Type | Remarques |
|---|---|---|
kyb.status | chaîne | "verified" ou "failed" |
{
"organizationId": "org_01H...",
"event": "kyb.match_not_found",
"kycRequestId": "req_01J...",
"context": { "kyb": { "status": "failed" } }
}
Exemple d'utilisation dans un workflow. Demander des documents de constitution supplémentaires lorsqu'aucune correspondance au registre n'est trouvée.
kyb.ubo_completed
« Vérification des bénéficiaires effectifs terminée » · pour toute l'organisation
Quand il se déclenche. Pour les demandes BUSINESS uniquement, après que l'orchestration a résolu chaque fiche de bénéficiaire effectif ultime de l'entreprise (aucune ne reste pending). Transporte le nombre de bénéficiaires criblés et le nombre d'échecs, afin qu'un workflow puisse se ramifier selon le risque UBO sans nouvelle interrogation. Ne se déclenche que lorsque l'entreprise possède effectivement des fiches UBO et que la ligne KYB a été modifiée pendant cette exécution.
Contexte de données.
| Champ | Type | Remarques |
|---|---|---|
kyb.uboCount | nombre | Nombre de bénéficiaires effectifs ultimes criblés |
kyb.failedUboCount | nombre | Nombre de ces bénéficiaires ayant échoué à la vérification |
{
"organizationId": "org_01H...",
"event": "kyb.ubo_completed",
"kycRequestId": "req_01J...",
"context": { "kyb": { "uboCount": 3, "failedUboCount": 1 } }
}
Exemple d'utilisation dans un workflow. Escalader lorsqu'un bénéficiaire effectif échoue au criblage :
trigger:
event: kyb.ubo_completed
filter: 'kyb.failedUboCount > 0'
Révision humaine
reviewer.decision
« Un réviseur prend une décision manuelle » · pour toute l'organisation
Quand il se déclenche. Un réviseur humain enregistre une décision manuelle dans le tableau de bord — approbation unique, l'une des deux étapes d'un processus à deux réviseurs, ou une action en lot.
Contexte de données.
| Champ | Type | Remarques |
|---|---|---|
decision | chaîne | Le choix du réviseur |
reviewerId | chaîne | Identifiant du réviseur qui décide |
stage | chaîne | undefined | "first_approval" / "second_approval" dans les processus à deux réviseurs |
via | chaîne | undefined | "bulk" lorsqu'il fait partie d'une action en lot |
{
"organizationId": "org_01H...",
"event": "reviewer.decision",
"kycRequestId": "req_01J...",
"context": { "decision": "approve", "reviewerId": "usr_01H...",
"stage": "second_approval" }
}
Exemple d'utilisation dans un workflow. Consigner dans le journal d'audit chaque décision manuelle et prévenir le demandeur après une approbation finale (la seconde).
reviewer.assigned
« Une demande est assignée à un réviseur » · pour toute l'organisation
Quand il se déclenche. Une demande est réassignée à un nouveau propriétaire ou réviseur depuis l'action de réassignation du tableau de bord. Le propriétaire initial défini à la création de la demande ne déclenche pas cet événement : seule une réassignation explicite le fait, de sorte que l'événement marque un véritable changement d'acheminement.
Contexte de données.
| Champ | Type | Remarques |
|---|---|---|
assignedUserId | chaîne | Le nouveau propriétaire ou réviseur |
previousUserId | chaîne | null | Le propriétaire précédent |
teamId | chaîne | null | L'équipe à laquelle la demande appartient désormais |
{
"organizationId": "org_01H...",
"event": "reviewer.assigned",
"kycRequestId": "req_01J...",
"context": { "assignedUserId": "usr_new...", "previousUserId": "usr_old...",
"teamId": "team_01H..." }
}
Exemple d'utilisation dans un workflow. Envoyer au réviseur nouvellement assigné une notification avec un lien direct vers le dossier.
reviewer.comment_added
« Un réviseur ajoute un commentaire » · pour toute l'organisation
Quand il se déclenche. Un réviseur publie un commentaire sur un dossier (le flux de commentaires du dossier). Le corps du commentaire n'est volontairement pas placé dans le contexte de l'événement — il peut contenir des notes sensibles à la casse — de sorte que l'événement ne transporte que des métadonnées.
Contexte de données.
| Champ | Type | Remarques |
|---|---|---|
commentId | chaîne | Identifiant du commentaire créé |
authorId | chaîne | Le réviseur auteur du commentaire |
authorName | chaîne | Nom d'affichage de l'auteur |
{
"organizationId": "org_01H...",
"event": "reviewer.comment_added",
"kycRequestId": "req_01J...",
"context": { "commentId": "cmt_01J...", "authorId": "usr_01H...",
"authorName": "Jordan Lee" }
}
Exemple d'utilisation dans un workflow. Recopier les nouveaux commentaires de dossier dans un canal Slack de l'équipe conformité.
edd.triggered
« La Diligence raisonnable renforcée est déclenchée » · sujet applicant
Quand il se déclenche. La Diligence raisonnable renforcée (EDD) est lancée pour une demande (actuellement, l'action EDD manuelle).
Contexte de données.
| Champ | Type | Remarques |
|---|---|---|
kind | chaîne | "edd" |
{
"organizationId": "org_01H...",
"event": "edd.triggered",
"kycRequestId": "req_01J...",
"context": { "kind": "edd" }
}
Exemple d'utilisation dans un workflow. Au lancement de l'EDD, demander les documents sur l'origine des fonds et assigner le dossier au service conformité.
Risque / score
Les deux événements de score se déclenchent depuis le chemin de décision une fois que le moteur de score a enregistré un score de confiance pour l'exécution (une demande finalisée sans score — par exemple annulée avant le scoring — n'émet ni l'un ni l'autre). Ils transportent le contexte de décision complet, de sorte que overallScore, decision, status et applicant.* sont tous disponibles pour les filtres.
score.updated
« Le score de confiance est mis à jour » · sujet applicant
Quand il se déclenche. À chaque finalisation (re)scorée — y compris une nouvelle analyse qui recalcule le score. Utilisez-le pour réagir au nouveau score de confiance, quel que soit le verdict.
Contexte de données. Le contexte de décision commun (overallScore, decision, status, applicant.country, applicant.type).
{
"organizationId": "org_01H...",
"event": "score.updated",
"kycRequestId": "req_01J...",
"applicantId": "app_01J...",
"context": {
"status": "APPROVED",
"decision": "approve",
"overallScore": 72,
"applicant": { "country": "CA", "type": "INDIVIDUAL" }
}
}
Exemple d'utilisation dans un workflow. Étiqueter toute demande dont le score tombe dans une plage de révision manuelle :
trigger:
event: score.updated
subject: applicant
scope: { mode: all }
filter: 'overallScore >= 50 AND overallScore < 70'
score.threshold
« Le score de confiance franchit un seuil » · sujet applicant
Quand il se déclenche. Même point d'émission que score.updated, mais le répartiteur le restreint par les bornes propres au déclencheur, scoreMin / scoreMax (il lit context.overallScore). Définissez les bornes sur le déclencheur : le workflow ne s'exécute alors que lorsque le score se situe entre elles. C'est un raccourci par rapport à l'écriture manuelle d'un filtre sur overallScore.
Contexte de données. Identique à score.updated.
{
"organizationId": "org_01H...",
"event": "score.threshold",
"kycRequestId": "req_01J...",
"applicantId": "app_01J...",
"context": {
"status": "REJECTED",
"decision": "reject",
"overallScore": 38,
"applicant": { "country": "US", "type": "INDIVIDUAL" }
}
}
Exemple d'utilisation dans un workflow. N'exécuter que pour les scores faibles en définissant une borne supérieure sur le déclencheur :
trigger:
event: score.threshold
subject: applicant
scope: { mode: all }
scoreMax: 50 # fire only when overallScore <= 50
scoreMin/scoreMaxsont inclusives. Définissez l'une ou les deux. Si aucune n'est définie,score.thresholdse comporte commescore.updated.
Temps / surveillance
schedule
« Selon un calendrier » · pour toute l'organisation, non lié à une demande
Quand il se déclenche. Selon l'expression cron propre au workflow (définie dans le champ schedule du déclencheur). L'exécution est au niveau de l'organisation — kycRequestId est null — utilisez-le donc pour les traitements par lot ou de maintenance, et non pour une logique propre à un demandeur.
Le cron est une expression standard à 5 champs (minute heure jour mois jour-de-la-semaine) évaluée en UTC, et il ne peut pas se déclencher plus souvent que toutes les 5 minutes : la publication refuse * * * * * ou */2 * * * *.
Contexte de données.
| Champ | Type | Remarques |
|---|---|---|
firedAt | chaîne | Heure de déclenchement au format ISO-8601 |
cron | chaîne | L'expression cron qui a déclenché l'exécution |
{
"organizationId": "org_01H...",
"event": "schedule",
"kycRequestId": null,
"context": { "firedAt": "2026-05-31T09:00:00.000Z", "cron": "0 9 * * 1" }
}
Exemple d'utilisation dans un workflow. Chaque lundi à 9 h, générer une synthèse hebdomadaire des vérifications et la publier dans Slack :
trigger:
event: schedule
schedule: "0 9 * * 1"
verification.expired
« Une vérification expire » · sujet applicant
Quand il se déclenche. Depuis la tâche cron d'expiration, lorsqu'une demande dépasse sa période de validité.
Contexte de données.
| Champ | Type | Remarques |
|---|---|---|
status | chaîne | "EXPIRED" |
{
"organizationId": "org_01H...",
"event": "verification.expired",
"kycRequestId": "req_01J...",
"context": { "status": "EXPIRED" }
}
Exemple d'utilisation dans un workflow. Envoyer au demandeur un nouveau lien par courriel et prévenir le propriétaire lorsqu'une vérification expire.
monitoring.alert
« Une alerte de surveillance se déclenche » · pour toute l'organisation
Quand il se déclenche. Depuis le recriblage continu des sanctions, lorsqu'un sujet précédemment libéré correspond de nouveau à une liste de surveillance (surveillance continue).
Contexte de données.
| Champ | Type | Remarques |
|---|---|---|
alertType | chaîne | Le type d'alerte de surveillance |
severity | chaîne | Gravité de l'alerte |
topScore | nombre | null | Score de la nouvelle correspondance la plus forte |
{
"organizationId": "org_01H...",
"event": "monitoring.alert",
"kycRequestId": "req_01J...",
"context": { "alertType": "sanctions_rescreen", "severity": "high",
"topScore": 0.88 }
}
Exemple d'utilisation dans un workflow. Sur une alerte de surveillance de gravité élevée, escalader la demande et ouvrir un dossier de conformité.
Externe / intégration
inbound.webhook
« Un webhook entrant arrive » · pour toute l'organisation, charge utile libre
Quand il se déclenche. Votre système envoie une requête POST vers le point de terminaison de webhook entrant du workflow. Le context est ce que vous envoyez en JSON : vous contrôlez entièrement sa forme. Non lié à une demande, sauf si votre charge utile fournit un identifiant que votre workflow utilise.
Point de terminaison. POST /api/v1/kyc/workflows/inbound, authentifié par une clé API dotée du scope workflows:trigger. Aucun scope kyc:* ne l'inclut : cochez Workflows d'automatisation → Déclencher en créant ou en modifiant la clé. Votre forfait doit inclure les workflows d'automatisation (sinon 402). Envoyez context (votre objet JSON, 64 Ko maximum) et, si vous le souhaitez, eventId (votre propre identifiant : une nouvelle tentative avec le même eventId ne lance jamais deux fois le même workflow). L'appel répond 202 et les exécutions ont lieu en arrière-plan. Une clé limitée à certains workflows ne lance que ceux-ci.
curl -X POST https://app.certivant.com/api/v1/kyc/workflows/inbound \
-H "Authorization: Bearer ck_live_..." \
-H "Content-Type: application/json" \
-d '{"eventId":"crm-9281-1","context":{"externalRef":"user-9281","tier":"vip"}}'
Contexte de données. Libre : le corps JSON envoyé, tel quel.
{
"organizationId": "org_01H...",
"event": "inbound.webhook",
"context": { "externalRef": "user-9281", "tier": "vip", "source": "crm" }
}
Exemple d'utilisation dans un workflow. Laisser votre CRM lancer un lot de nouvelle vérification pour les clients signalés ; filtrer sur un champ que vous avez envoyé :
trigger:
event: inbound.webhook
filter: 'tier = "vip"'
conference.completed
« Une conférence en direct se termine » · pour toute l'organisation
Quand il se déclenche. Une conférence de vérification d'identité en direct par web se termine et l'animateur (notaire ou réviseur) enregistre une décision. Émis une seule fois, protégé pour qu'une décision concurrente ne puisse pas le déclencher en double.
Contexte de données.
| Champ | Type | Remarques |
|---|---|---|
decision | chaîne | La décision enregistrée par l'animateur |
status | chaîne | Le statut résultant de la demande |
{
"organizationId": "org_01H...",
"event": "conference.completed",
"kycRequestId": "req_01J...",
"context": { "decision": "approve", "status": "APPROVED" }
}
Exemple d'utilisation dans un workflow. Après une conférence, générer le certificat de vérification signé et l'envoyer par courriel au demandeur.
Filtrer avec le DSL
Le filter du déclencheur (et chaque nœud Condition) utilise la grammaire d'expressions des politiques : un seul prédicat booléen, sans appel de fonction :
- Champs : le contexte d'événement aplati (les champs indiqués dans le tableau Contexte de données de chaque déclencheur).
- Opérateurs :
=!=>>=<<=,IN/NOT IN. - Combinateurs :
ANDORNOT, avec regroupement par( ). - Littéraux : nombres,
"chaînes",true/false/null, et listes[...].
L'opérateur d'égalité est un simple
=(et non==). Les champs absents sont sûrs par défaut : un champ absent s'évalue à faux au lieu de lever une erreur ; un filtre qui référencesanctions.topScoresur un événement qui n'est pas de sanctions ne correspond donc tout simplement pas.
Les champs renseignés dépendent de l'événement : un événement sanctions.* transporte sanctions.*, un événement liveness.* transporte liveness.*, et les événements de décision transportent overallScore / decision / status / applicant.*.
| Champ | Type | Disponible sur |
|---|---|---|
overallScore | nombre | événements de décision / statut |
decision | chaîne | événements de décision / statut |
status | chaîne | événements de décision / statut / cycle de vie |
applicant.country | chaîne | événements d'entrée / décision |
applicant.type | chaîne | événements d'entrée / décision |
document.type | chaîne | document.* |
document.side | chaîne | document.* |
liveness.isLive | booléen | liveness.* |
liveness.confidence | nombre | liveness.* |
faceMatch.isMatch | booléen | face_match.* |
faceMatch.confidence | nombre | face_match.* |
sanctions.isMatch | booléen | sanctions.* |
sanctions.topScore | nombre | sanctions.* |
sanctions.matchCount | nombre | sanctions.* |
pep.isMatch | booléen | pep.match |
pep.matchCount | nombre | pep.match |
kyb.status | chaîne | kyb.match_* |
kyb.uboCount | nombre | kyb.ubo_completed |
kyb.failedUboCount | nombre | kyb.ubo_completed |
reviewerId | chaîne | reviewer.decision |
Exemples de prédicats :
overallScore >= 75
decision = "reject"
applicant.country IN ["CA","US"]
document.type = "passport"
sanctions.isMatch = true AND sanctions.topScore >= 0.85
overallScore < 50 OR sanctions.isMatch = true
Dans l'éditeur, appuyez sur Ctrl+Espace dans n'importe quel champ d'expression pour obtenir une saisie semi-automatique selon le contexte et une fenêtre d'aide listant les champs, les opérateurs et des exemples prêts à l'emploi.
Tableau comparatif
| Déclencheur | Catégorie | Sujet | Lié à une demande | Fréquence | Remarques |
|---|---|---|---|---|---|
form.submitted | Entrée | form | oui | par soumission | Formulaires hébergés uniquement |
request.created | Entrée | applicant | oui | par demande | Avant l'exécution de tout contrôle |
request.status_changed | Cycle de vie | applicant | oui | par changement de statut | Générique ; complète les événements spécifiques |
request.approved | Cycle de vie | applicant | oui | par finalisation | |
request.rejected | Cycle de vie | applicant | oui | par finalisation | |
request.escalated | Cycle de vie | applicant | oui | par finalisation | |
request.resubmitted | Cycle de vie | applicant | oui | par nouvelle soumission | |
request.documents_ready | Cycle de vie | applicant | oui | par fin de liste de contrôle | Tous les documents requis envoyés |
document.uploaded | Documents | document | oui | par document | |
document.failed | Documents | document | oui | par document en échec | |
check.completed | Signaux | check | oui | par contrôle (élevée) | Protégé ; un par contrôle |
liveness.passed | Signaux | check | oui | par demande | Uniquement si sans ambiguïté |
liveness.failed | Signaux | check | oui | par demande | Uniquement si sans ambiguïté |
face_match.passed | Signaux | check | oui | par demande | Uniquement si sans ambiguïté |
face_match.failed | Signaux | check | oui | par demande | Uniquement si sans ambiguïté |
sanctions.hit | Signaux | check | oui | par demande | Correspondance dans une liste de surveillance |
sanctions.cleared | Signaux | check | oui | par demande | |
pep.match | Signaux | check | oui | par demande | Entrées PEP uniquement |
kyb.match_found | Signaux | — | oui | par demande d'entreprise | BUSINESS uniquement |
kyb.match_not_found | Signaux | — | oui | par demande d'entreprise | BUSINESS uniquement |
kyb.ubo_completed | Signaux | — | oui | par demande d'entreprise | BUSINESS uniquement ; bénéficiaires résolus |
reviewer.decision | Révision | — | oui | par décision manuelle | Étapes + lots |
reviewer.assigned | Révision | — | oui | par réassignation | Réassignation explicite uniquement |
reviewer.comment_added | Révision | — | oui | par commentaire | Corps absent du contexte |
edd.triggered | Révision | applicant | oui | par lancement de l'EDD | |
score.updated | Risque | applicant | oui | par (re)scoring | Se déclenche avec la décision |
score.threshold | Risque | applicant | oui | par (re)scoring | Restreint par scoreMin/scoreMax |
schedule | Temps | — | non | cron | Exécution au niveau de l'organisation |
verification.expired | Temps | applicant | oui | par expiration | Depuis la tâche cron d'expiration |
monitoring.alert | Surveillance | — | oui | par alerte | Criblage continu |
inbound.webhook | Externe | — | facultatif | par POST | Charge utile libre |
conference.completed | Externe | — | oui | par conférence | Sessions Live ID |
Déclencheurs à venir
Quelques déclencheurs fréquemment demandés ne figurent pas dans le sélecteur, car la plateforme n'a pas encore de signal à émettre pour eux : les proposer comme options sélectionnables créerait des déclencheurs qui ne se déclenchent jamais. Ils sont listés ici avec l'alternative fonctionnelle, afin que l'écart soit explicite. (Tout ce qui figure dans le sélecteur, en revanche, est réellement émis — y compris score.threshold, la correspondance faciale, la PEP, les bénéficiaires effectifs et les événements de révision, qui ont été câblés dans cette version.)
| Déclencheur demandé | Pourquoi il n'est pas émis | Alternative à utiliser |
|---|---|---|
| Fraude détectée par un signal | Aucun type de contrôle autonome d'analyse forensique ou d'authenticité n'existe encore (l'analyse forensique est actuellement un facteur de scoring du risque, et non un contrôle distinct qui émet un résultat). | Se ramifier sur document.failed, ou sur les événements de décision / score.threshold pour le risque qui en résulte. |
| Données du registre des entreprises mises à jour | La KYB est une vérification ponctuelle ; aucune tâche continue de nouvelle récupération ou de surveillance du registre n'existe pour déclencher un événement. | Relancer la vérification selon un schedule et se ramifier sur kyb.match_* / kyb.ubo_completed. |
| Lien de courriel ou de SMS cliqué | Aucune infrastructure de suivi de l'ouverture des courriels ou des clics sur les liens n'existe. | Utiliser form.submitted / document.uploaded comme signaux concrets que « le demandeur a agi ». |
Il s'agit de véritables éléments de la feuille de route, et non de lacunes de documentation : l'énumération des déclencheurs du moteur fait foi, et ce tableau reflète ce qu'elle ne contient pas encore.
Bonnes pratiques
- Définissez toujours un sujet et une portée. Un déclencheur sans sujet s'applique à toute l'organisation et se déclenchera pour chaque objet correspondant de l'organisation. Privilégiez une portée
specificoucategory; réservezallaux automatisations réellement globales. - Écoutez les événements spécifiques plutôt que l'événement générique, lorsque c'est possible. Utilisez
liveness.failed/sanctions.hitplutôt que d'analysercheck.completed, et un événement de décision unique plutôt querequest.status_changed— sauf si vous avez précisément besoin d'un statut qui n'a pas d'événement dédié. - N'écoutez pas à la fois les événements de statut spécifiques et le générique.
request.approvedetrequest.status_changedse déclenchent tous deux à l'approbation ; rattacher deux workflows (ou deux déclencheurs) au même résultat exécute deux fois les effets secondaires. - Considérez le contexte comme un instantané. Le contexte d'événement est capturé à l'envoi. Si votre workflow attend (un nœud
wait, un long délai), relisez l'état en direct dans une action au lieu de vous fier à un contexte périmé pour une décision critique. - Gardez les workflows
check.completedlégers. C'est le déclencheur à la plus haute fréquence ; limitez-le à une catégorie et filtrez tôt, afin de ne pas diffuser sur chaque contrôle de chaque demande. - Surveillez les portées orphelines. Supprimer un formulaire ou un demandeur référencé par une portée
specificrend le déclencheur inactif (il ne se déclenchera pas et affiche un badge « Nécessite une nouvelle liaison »). Rattachez-le à un objet existant pour le réactiver. - Les workflows escaladent, ils ne décident jamais. Si le résultat d'un déclencheur doit modifier le verdict, passez par
escalatevers la révision humaine : il n'existe volontairement aucune action d'approbation ni de rejet. - Attendez-vous à une exécution au moins une fois, à terme. L'envoi est asynchrone et sans attente de réponse ; rendez les effets secondaires des actions idempotents là où c'est important (par exemple en incluant une référence externe, afin qu'une nouvelle émission ne duplique pas les enregistrements en aval).
Questions fréquentes
Un workflow peut-il approuver ou rejeter une vérification ?
Non. L'ensemble des actions ne comporte ni approve ni reject. Le contrôle le plus fort sur l'issue est escalate, qui fait passer la demande en révision humaine via la machine d'état existante, protégée par verrou optimiste et auditée. Le verdict relève de la politique de décision et des réviseurs humains.
Un événement de décision spécifique et request.status_changed se sont tous deux déclenchés : est-ce un bogue ?
Non. L'événement générique request.status_changed est toujours émis en même temps que l'événement spécifique, afin que vous puissiez réagir aux statuts qui n'ont pas d'événement dédié. Écoutez l'un, pas les deux.
Pourquoi mon workflow check.completed ne s'est-il pas exécuté ?
La diffusion par contrôle est protégée : elle n'envoie des tâches que lorsqu'un workflow actif écoute check.completed. Vérifiez que le workflow est actif et que sa portée ou son filtre correspond au contrôle. Notez aussi que les contrôles en erreur sans fait booléen exploitable n'émettent pas les signaux spécifiques liveness.* / sanctions.*.
Pourquoi mon déclencheur score.threshold ne se déclenche-t-il pas ?
Il se déclenche à chaque finalisation scorée, puis le répartiteur le restreint par les bornes scoreMin / scoreMax du déclencheur. S'il ne s'exécute jamais, vérifiez que le score se situe bien dans vos bornes et que la demande a atteint un résultat scoré (une demande annulée avant le scoring n'émet aucun événement de score). Sans bornes définies, il se comporte comme score.updated.
Quelle est la différence entre score.updated et score.threshold ?
Même point d'émission et même contexte. score.updated s'exécute toujours ; score.threshold ne s'exécute que lorsque overallScore se situe dans les bornes scoreMin / scoreMax du déclencheur — un raccourci intégré, pour que vous n'ayez pas à écrire vous-même le filtre sur overallScore.
Comment déclencher un workflow sur un résultat de correspondance faciale ou PEP ?
Utilisez les événements dédiés face_match.passed / face_match.failed et pep.match. Une correspondance PEP déclenche aussi sanctions.hit (le criblage PEP repose sur le criblage des listes de surveillance) ; écoutez donc pep.match si vous ne vous intéressez qu'à l'exposition PEP.
La suppression d'un formulaire désactive-t-elle ses workflows ?
Un workflow dont le déclencheur a une portée specific limitée à ce seul formulaire devient orphelin : il est inactif (il ne se déclenchera pas) et affiche un badge « Nécessite une nouvelle liaison ». Rattachez-le à un objet existant pour le réactiver. Il ne retombe jamais silencieusement sur un déclenchement pour toute l'organisation.
L'envoi est-il synchrone avec mon appel API ? Non. L'envoi se fait sans attente de réponse et s'exécute en dehors du flux principal. L'action d'origine (envoi, changement de statut) réussit indépendamment de la réussite ou de l'échec de l'envoi ou de l'exécution du workflow.
Un workflow peut-il en déclencher un autre ?
Oui : l'action trigger_workflow exécute directement un workflow enregistré choisi, avec une profondeur de chaînage maximale de 5 pour éviter les boucles.
Annexe
Glossaire
- Déclencheur : le nœud d'entrée unique qui déclare l'événement, le sujet, la portée et le filtre qui démarrent un workflow.
- Événement : un
WorkflowTriggerEventque la plateforme émet à un point réel du cycle de vie de la vérification. - Sujet : le type d'objet auquel un déclencheur se rattache :
form,applicant,documentoucheck. - Portée : la façon dont le sujet est restreint :
specific(identifiants),categoryouall. - Contexte : les faits propres à l'événement, capturés à l'envoi et lus par les filtres et les modèles d'action.
- Filtre : un prédicat DSL facultatif qui restreint davantage les événements admissibles.
- Déclencheur orphelin : un déclencheur à portée
specificdont les objets ont été supprimés ; inactif jusqu'à une nouvelle liaison. - Exécution : une
KycWorkflowRun: une exécution d'un workflow, avec unlogen ajout seul.
Énumération complète des événements
type WorkflowTriggerEvent =
| "form.submitted" | "request.created"
| "request.status_changed" | "request.approved" | "request.rejected"
| "request.escalated" | "request.resubmitted" | "request.documents_ready"
| "document.uploaded" | "document.failed" | "check.completed"
| "liveness.passed" | "liveness.failed"
| "face_match.passed" | "face_match.failed"
| "sanctions.hit" | "sanctions.cleared" | "pep.match"
| "kyb.match_found" | "kyb.match_not_found" | "kyb.ubo_completed"
| "reviewer.decision" | "reviewer.assigned" | "reviewer.comment_added"
| "score.threshold" | "score.updated" | "edd.triggered"
| "monitoring.alert" | "verification.expired" | "schedule"
| "inbound.webhook" | "conference.completed";
Cycle de vie d'un envoi
1. Something happens (form submit, check finishes, cron, webhook…)
2. Emitter calls enqueueWorkflowDispatch({ event, …, context })
3. workflow.worker picks it up → dispatchWorkflowEvent(input)
4. Match active workflows trigger.event === input.event
5. Narrow by subject + scope specific ids / category / all (orphans excluded)
6. Evaluate filter (DSL) against input.context (missing fields = false)
7. Create KycWorkflowRun context stashed on the first log entry
8. Execute nodes Condition → Action → Wait → … → End
Exemple de bout en bout
Une automatisation d'accueil complète rattachée à un seul formulaire KYC :
trigger:
event: form.submitted
subject: form
scope: { mode: specific, ids: ["form_onboarding_01H..."] }
# graph (conceptual):
# ▸ Trigger: form.submitted
# → Action: email applicant "We received your submission"
# → Wait: until check.completed (or 24h timeout)
# → Condition: sanctions.isMatch = true
# true → Action: escalate + Action: notify compliance (Slack)
# false → Condition: overallScore >= 80
# true → Action: generate_pdf_report → email applicant
# false → Action: assign_department "manual-review"
# → End
Cet exemple ne fixe jamais le verdict : la seule étape qui influe sur l'issue est escalate, qui oriente vers la révision humaine. Tout le reste relève de la notification, de la collecte de données et de la production de rapports autour de la décision dont la politique et les réviseurs sont responsables.
Workflows d'automatisation
Workflows exécutables déclenchés par des événements, qui acheminent, notifient et orchestrent autour d'une vérification — sans jamais fixer le verdict.
SDK Web
Affichez une vérification Certivant dans votre propre site web avec une seule balise script — en fenêtre modale ou en ligne, avec les événements ready, step, complete, cancel et error, des contrôles stricts d'origine et la prise en charge de la caméra.