Auslöser
Vollständige Referenz für jeden Auslöser von Certivant-Automatisierungs-Workflows — wann jedes Ereignis ausgelöst wird, sein Datenkontext, Beispielnutzlasten, die Konfiguration von Gegenstand und Umfang sowie DSL-Filtermuster.
Ein Auslöser ist der einzige Einstiegsknoten jedes Automatisierungs-Workflows. Er legt fest, welches Ereignis den Workflow startet, welche Objekte dieses Ereignis betreffen muss (den Gegenstand + Umfang), sowie einen optionalen Filter, der die qualifizierenden Ereignisse weiter eingrenzt. Sendet die Plattform ein passendes Ereignis, wird ein KycWorkflowRun erzeugt und der Workflow läuft Knoten für Knoten ab.
Diese Seite dokumentiert jeden Auslöser, den die Engine tatsächlich erzeugt, und stützt sich dabei auf die Stellen der Plattform, an denen Ereignisse entstehen. Jeder Eintrag nennt, wann er ausgelöst wird, den genauen context, den er trägt, eine echte Beispielnutzlast und wie Sie ihn eingrenzen und filtern.
Der Katalog der Auslöser ist ein fester Enum in der Engine (
WorkflowTriggerEvent). Die Auswahl im Editor, diese Seite, das API-Validierungsschema und der Dispatcher lesen alle aus derselben Liste. Jeder Auslöser, den Sie auswählen können, wird also tatsächlich von der Plattform erzeugt — keiner ist nur Dekoration. Einige Fähigkeiten, für die es noch keinen Erzeugungspunkt gibt, sind bewusst aus der Auswahl herausgehalten und unter Auslöser auf der Roadmap mit der funktionierenden Alternative aufgeführt, damit Sie niemals einen Workflow bauen, der stillschweigend nie läuft.
So lösen Auslöser aus
Auslöser werden nicht abgefragt. Jeder entspricht einer realen Stelle der Plattform, an der Code den Dispatcher aufruft:
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
Drei Eigenschaften sind für Integratoren wichtig:
- Fire-and-forget. Die Auslösung ist so gekapselt, dass ein Fehler nie die ursprüngliche Aktion bricht. Löst ein Dokument-Upload einen Workflow aus und wirft die Auslösung einen Fehler, gelingt der Upload trotzdem. Behandeln Sie Nebenwirkungen von Workflows als irgendwann ausgeführt, nicht synchron zum API-Aufruf.
- Ein Ereignis, viele Läufe. Ein einziges Ereignis kann jeden aktiven Workflow starten, der dazu passt. Nutzen Sie Gegenstand + Umfang (und Filter), damit dieser Fan-out beabsichtigt bleibt.
- Der Kontext wird einmal erfasst. Das Objekt
contextdes Ereignisses wird im ersten Protokolleintrag des Laufs hinterlegt. Bedingungsknoten, Aktionsvorlagen und Platzhalter für Serienbriefe lesen aus dieser Momentaufnahme — sie fragen den Live-Zustand nicht erneut ab.
Der Auslöse-Envelope
Jede Erzeugung liefert ein WorkflowEventInput. Felder neben organizationId und event sind optional und hängen vom Ereignis ab:
| Feld | Typ | Vorhanden, wenn |
|---|---|---|
organizationId | string | immer |
event | WorkflowTriggerEvent | immer |
kycRequestId | string | null | das Ereignis eine bestimmte Verifizierung betrifft (die meisten Ereignisse) |
formId | string | null | form.submitted |
formKind | "kyc" | "kyb" | null | form.submitted |
applicantId | string | null | antragstellerbezogene Ereignisse |
documentType | string | null | document.* |
checkKind | string | null | check.completed, liveness.*, sanctions.* |
context | object | ereignisspezifische Fakten (siehe unten bei jedem Auslöser) |
workflowId | string | nur bei einem direkten Aufruf über trigger_workflow |
chainDepth | number | Verkettungstiefe von Workflow zu Workflow (max. 5) |
subject, scope, filter, formIds, schedule, scoreMin und scoreMax sind Eigenschaften des Auslösers, den Sie konfigurieren — nicht des Laufzeitereignisses. Der Dispatcher nutzt sie, um zu entscheiden, ob ein eingehendes Ereignis zu Ihrem Workflow passt.
Die Auslöserdefinition
So sieht die Form aus, die im Feld trigger eines Workflows (WorkflowTrigger) gespeichert wird:
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
}
Gegenstand + Umfang
Der Gegenstand ist die Art des Objekts, das der Auslöser beobachtet; der Umfang grenzt ihn auf bestimmte Objekte, eine Kategorie oder alles ein. Dadurch löst ein Auslöser nicht versehentlich organisationsweit aus.
| Gegenstand | Gültige Ereignisse | Optionen für scope.mode | Bedeutung von category |
|---|---|---|---|
form | form.submitted | specific (Formular-IDs) · category · all | "kyc" oder "kyb" |
applicant | request.*, score.threshold, edd.triggered, verification.expired | specific (Antragsteller-IDs) · all | — |
document | document.uploaded, document.failed | specific · category · all | ein Dokumenttyp, z. B. "passport" |
check | check.completed, liveness.*, sanctions.* | specific · category · all | eine Prüfart, z. B. "sanctions" |
| (keiner) | schedule, inbound.webhook, monitoring.alert, kyb.*, reviewer.decision, conference.completed | — | organisationsweit |
Umfangsmodi:
specific— greift nur für die aufgeführtenids(z. B. nur für dieses Onboarding-Formular). Wenn jede ID in einemspecific-Umfang auf ein gelöschtes Objekt zeigt, ist der Auslöser verwaist: Der Dispatcher weigert sich, ihn auszulösen (er bleibt inaktiv, statt stillschweigend auf „alle“ zurückzufallen), und der Editor zeigt ein Abzeichen „Neu zuordnen erforderlich“.category— greift für eine Klasse von Objekten (alle KYC-Formulare, alle Reisepässe, alle Sanktionsprüfungen).all— greift für jedes Objekt der Gegenstandsart in der Organisation.
Ein Auslöser ohne subject behält das bisherige Verhalten: organisationsweit, optional eingegrenzt durch formIds für form.submitted. Neue Workflows sollten immer Gegenstand und Umfang setzen.
Filter (DSL)
filter ist ein optionales boolesches Prädikat in derselben Ausdrucksgrammatik wie die DSL der Entscheidungsrichtlinie. Es wird gegen den context des Ereignisses ausgeführt. Ein leerer Filter greift bei jedem Ereignis dieses Typs. Siehe Filtern mit der DSL.
Kategorien der Auslöser
| Kategorie | Auslöser |
|---|---|
| Einstieg | form.submitted, request.created |
| Lebenszyklus / Status | request.status_changed, request.approved, request.rejected, request.escalated, request.resubmitted, request.documents_ready |
| Dokumente | document.uploaded, document.failed |
| Verifizierungssignale | 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 |
| Manuelle Prüfung | reviewer.decision, reviewer.assigned, reviewer.comment_added, edd.triggered |
| Risiko / Score | score.threshold, score.updated |
| Zeit / Überwachung | schedule, verification.expired, monitoring.alert |
| Extern / Integration | inbound.webhook, conference.completed |
Referenz der Auslöser
Jeder Eintrag unten folgt demselben Aufbau: Wann er ausgelöst wird, Datenkontext, Beispielereignis und Beispiel für die Verwendung im Workflow. Jede Nutzlast entspricht der echten Dispatch-Form, die der erzeugende Code liefert.
Einstieg
form.submitted
„Ein Formular wird eingereicht“ · Gegenstand form
Wann er ausgelöst wird. Ein öffentliches, gehostetes Verifizierungsformular wird von einem Antragsteller eingereicht. Er wird von der öffentlichen Formularroute in dem Moment erzeugt, in dem eine Einreichung ihre KycRequest anlegt. Das begleitende Ereignis request.created wird gleichzeitig erzeugt.
Datenkontext.
| Feld | Typ | Hinweise |
|---|---|---|
type | string | Antragstellertyp bei der Einreichung, z. B. "INDIVIDUAL" |
Auf oberster Ebene: formId, formKind ("kyc"/"kyb"), kycRequestId, applicantId.
Beispielereignis.
{
"organizationId": "org_01H...",
"event": "form.submitted",
"kycRequestId": "req_01J...",
"formId": "form_01H...",
"formKind": "kyc",
"context": { "type": "INDIVIDUAL" }
}
Beispiel für die Verwendung im Workflow. Einen Workflow mit Willkommens-E-Mail und Dokumentenanforderung an ein bestimmtes Onboarding-Formular binden:
trigger:
event: form.submitted
subject: form
scope: { mode: specific, ids: ["form_01H..."] }
request.created
„Eine Verifizierung wird angelegt“ · Gegenstand applicant
Wann er ausgelöst wird. Eine Verifizierungsanfrage wird angelegt — über POST /api/v1/kyc/requests (201) oder als Begleitereignis zu einem gehosteten form.submitted. Er wird ausgelöst, bevor ein Dokument hochgeladen oder eine Prüfung durchgeführt wird. Verwenden Sie ihn daher für die Weiterleitung bei der Aufnahme, nicht für Ergebnisse.
Datenkontext.
| Feld | Typ | Hinweise |
|---|---|---|
applicant.country | string | null | ISO-3166 Alpha-2, aus der Staatsangehörigkeit des Antragstellers |
applicant.type | string | "INDIVIDUAL" oder "BUSINESS" |
type | string | Anfragetyp (INDIVIDUAL / BUSINESS) |
Beispielereignis.
{
"organizationId": "org_01H...",
"event": "request.created",
"kycRequestId": "req_01J...",
"applicantId": "app_01J...",
"context": {
"applicant": { "country": "CA", "type": "INDIVIDUAL" },
"type": "INDIVIDUAL"
}
}
Beispiel für die Verwendung im Workflow. Geschäftliche Antragsteller in eine KYB-Aufnahmewarteschlange leiten:
trigger:
event: request.created
subject: applicant
scope: { mode: all }
filter: 'applicant.type = "BUSINESS"'
Lebenszyklus / Status
Diese Ereignisse werden erzeugt, nachdem die Orchestrierung ein Ergebnis festgeschrieben hat. Alle tragen denselben Entscheidungskontext. Die spezifischen Ereignisse (approved/rejected/escalated) werden zusätzlich zu dem generischen request.status_changed erzeugt. Hören Sie daher auf eines davon — nicht auf beide —, um doppelte Ausführung zu vermeiden.
Gemeinsamer Datenkontext (request.approved, request.rejected, request.escalated, request.status_changed):
| Feld | Typ | Hinweise |
|---|---|---|
status | string | Neuer Status, z. B. "APPROVED", "REJECTED", "ESCALATED", "PENDING_SECOND_REVIEW" |
decision | string | null | Ergebnis des Scorers: "approve" / "reject" / "escalate" |
overallScore | number | null | Vertrauenswert 0–100 (höher = geringeres Risiko) |
applicant.country | string | null | ISO-3166 Alpha-2 |
applicant.type | string | INDIVIDUAL / BUSINESS |
request.approved
„Eine Verifizierung wird genehmigt“ · Gegenstand applicant
Wann er ausgelöst wird. Die Orchestrierung schließt die Anfrage als APPROVED ab.
{
"organizationId": "org_01H...",
"event": "request.approved",
"kycRequestId": "req_01J...",
"applicantId": "app_01J...",
"context": {
"status": "APPROVED",
"decision": "approve",
"overallScore": 88,
"applicant": { "country": "US", "type": "INDIVIDUAL" }
}
}
Beispiel für die Verwendung im Workflow. Einen Verifizierungsbericht nur bei Genehmigungen mit hoher Zuverlässigkeit erzeugen und per E-Mail versenden:
trigger:
event: request.approved
subject: applicant
scope: { mode: all }
filter: 'overallScore >= 80'
request.rejected
„Eine Verifizierung wird abgelehnt“ · Gegenstand applicant
Wann er ausgelöst wird. Die Orchestrierung schließt die Anfrage als REJECTED ab. Der Kontext ist identisch mit request.approved, mit status: "REJECTED" und decision: "reject".
Beispiel für die Verwendung im Workflow. Bei einer Ablehnung den zuständigen Vorgesetzten benachrichtigen und den Antragsteller markieren:
trigger:
event: request.rejected
subject: applicant
scope: { mode: all }
request.escalated
„Eine Verifizierung wird eskaliert“ · Gegenstand applicant
Wann er ausgelöst wird. Die Orchestrierung (oder eine escalate-Aktion eines vorherigen Workflows) überführt die Anfrage in ESCALATED / manuelle Prüfung. Der Kontext ist identisch mit dem gemeinsamen Aufbau, mit status: "ESCALATED".
Beispiel für die Verwendung im Workflow. Immer dann, wenn eine Anfrage eskaliert wird, eine Live-ID-Konferenz planen:
trigger:
event: request.escalated
subject: applicant
scope: { mode: all }
request.status_changed
„Ein Verifizierungsstatus ändert sich“ · Gegenstand applicant
Wann er ausgelöst wird. Wird bei jeder abgeschlossenen Statusänderung immer zusammen mit einem spezifischen Entscheidungsereignis erzeugt. Verwenden Sie ihn, um auf Status zu reagieren, für die es kein eigenes Ereignis gibt (z. B. PENDING_SECOND_REVIEW, CANCELLED).
Beispiel für die Verwendung im Workflow. Nur auf eine Übergabe an einen zweiten Prüfer reagieren:
trigger:
event: request.status_changed
subject: applicant
scope: { mode: all }
filter: 'status = "PENDING_SECOND_REVIEW"'
request.resubmitted
„Eine Verifizierung wird erneut eingereicht“ · Gegenstand applicant
Wann er ausgelöst wird. Ein Prüfer schickt eine Anfrage über das Dashboard zur erneuten Einreichung zurück; die Anfrage kehrt zu PENDING_UPLOAD zurück.
Datenkontext.
| Feld | Typ | Hinweise |
|---|---|---|
status | string | "PENDING_UPLOAD" |
kind | string | "resubmit" |
{
"organizationId": "org_01H...",
"event": "request.resubmitted",
"kycRequestId": "req_01J...",
"context": { "status": "PENDING_UPLOAD", "kind": "resubmit" }
}
Beispiel für die Verwendung im Workflow. Dem Antragsteller seinen Upload-Link erneut senden und einen Erinnerungs-Timer starten.
request.documents_ready
„Alle erforderlichen Dokumente sind hochgeladen“ · Gegenstand applicant
Wann er ausgelöst wird. Der Antragsteller hat gerade das letzte Element seiner Checkliste der erforderlichen Dokumente hochgeladen, daher wechselt die Anfrage in DOCUMENTS_READY. Er wird von beiden Upload-Wegen erzeugt — der Dokumenten-API des Dashboards und der gehosteten Verifizierungssitzung — und nur bei dem Upload, der die Menge der ausstehenden Dokumente leert. Dies ist das konkrete Signal „der Antragsteller hat jeden erforderlichen Schritt abgeschlossen“.
Datenkontext.
| Feld | Typ | Hinweise |
|---|---|---|
status | string | "DOCUMENTS_READY" |
{
"organizationId": "org_01H...",
"event": "request.documents_ready",
"kycRequestId": "req_01J...",
"context": { "status": "DOCUMENTS_READY" }
}
Beispiel für die Verwendung im Workflow. Den zugewiesenen Prüfer benachrichtigen, sobald ein Fall zur Prüfung bereit ist:
trigger:
event: request.documents_ready
subject: applicant
scope: { mode: all }
Dokumente
document.uploaded
„Ein Dokument wird hochgeladen“ · Gegenstand document
Wann er ausgelöst wird. Jedes Mal, wenn ein Dokument hochgeladen wird — über die API des Dashboards oder eine gehostete Verifizierungssitzung. Ein Ereignis pro Dokument.
Datenkontext.
| Feld | Typ | Hinweise |
|---|---|---|
document.type | string | z. B. "passport", "id_card", "drivers_license" |
document.side | string | null | "front" / "back" (null, wenn nicht zutreffend) |
Auf oberster Ebene: documentType, kycRequestId.
{
"organizationId": "org_01H...",
"event": "document.uploaded",
"kycRequestId": "req_01J...",
"documentType": "passport",
"context": { "document": { "type": "passport", "side": "front" } }
}
Beispiel für die Verwendung im Workflow. Nur bei Uploads eines Adressnachweises auslösen:
trigger:
event: document.uploaded
subject: document
scope: { mode: category, category: "utility_bill" }
document.failed
„Ein Dokument besteht die Prüfung nicht“ · Gegenstand document
Wann er ausgelöst wird. Die Orchestrierung markiert ein Dokument als failed (OCR konnte nicht laufen, die Echtheitsprüfung hat es abgelehnt usw.). Ein Ereignis pro fehlgeschlagenem Dokument. Gleiche Kontextform wie bei document.uploaded.
{
"organizationId": "org_01H...",
"event": "document.failed",
"kycRequestId": "req_01J...",
"documentType": "drivers_license",
"context": { "document": { "type": "drivers_license", "side": "back" } }
}
Beispiel für die Verwendung im Workflow. Automatisch eine erneute Aufnahme jedes fehlgeschlagenen Dokuments anfordern.
Verifizierungssignale
check.completed
„Eine Verifizierungsprüfung wird abgeschlossen“ · Gegenstand check
Wann er ausgelöst wird. Einmal für jede einzelne Prüfung, die während eines Orchestrierungslaufs abgeschlossen wird (OCR, Gesichtsabgleich, Lebenderkennung, Sanktionen, Echtheit des Dokuments, NFC-Auslesung …). Dies ist das generische Signal mit hoher Häufigkeit — für ein klares Ergebnis verwenden Sie besser liveness.* / sanctions.*.
Geschützter Fan-out. Da pro Anfrage mehrere Prüfungen abgeschlossen werden, fragt die Plattform zuerst ab, ob die Organisation irgendeinen aktiven Workflow hat, der auf
check.completedlauscht. Ohne einen solchen Hörer werden keine Auslösejobs erzeugt. Die Wasserlinie je Lauf verhindert, dass eine erneute Prüfung die Prüfungen eines früheren Laufs noch einmal erzeugt.
Datenkontext.
| Feld | Typ | Hinweise |
|---|---|---|
check.type | string | Art der Prüfung, z. B. "sanctions", "face_match", "ocr" |
check.status | string | "passed" / "failed" / "error" |
check.provider | string | null | Anbieter, der die Prüfung durchgeführt hat |
check.score | number | null | Wert des Anbieters, sofern zutreffend |
Auf oberster Ebene: 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 }
}
}
Beispiel für die Verwendung im Workflow. Auf jede abgeschlossene Sanktionsprüfung reagieren, unabhängig vom Ergebnis (für ein klares Bestanden/Nicht-bestanden sind die dedizierten Ereignisse sanctions.* / face_match.* weiter unten besser):
trigger:
event: check.completed
subject: check
scope: { mode: category, category: "sanctions" }
liveness.passed / liveness.failed
„Lebenderkennung bestanden“ / „Lebenderkennung fehlgeschlagen“ · Gegenstand check (
liveness)
Wann er ausgelöst wird. Nach der Orchestrierung, wenn die Lebenderkennung einen eindeutigen booleschen Wert isLive gespeichert hat. passed, wenn isLive wahr ist, failed, wenn er falsch ist. Eine fehlerhafte oder mehrdeutige Lebenderkennung erzeugt keines von beiden.
Datenkontext.
| Feld | Typ | Hinweise |
|---|---|---|
liveness.isLive | boolean | Ob der Antragsteller die Lebenderkennung bestanden hat |
liveness.confidence | number | null | Zuverlässigkeit 0–1 |
{
"organizationId": "org_01H...",
"event": "liveness.failed",
"kycRequestId": "req_01J...",
"checkKind": "liveness",
"context": { "liveness": { "isLive": false, "confidence": 0.31 } }
}
Beispiel für die Verwendung im Workflow. Bei einem Fehlschlag der Lebenderkennung mit niedriger Zuverlässigkeit eskalieren:
trigger:
event: liveness.failed
subject: check
scope: { mode: all }
filter: 'liveness.confidence < 0.5'
face_match.passed / face_match.failed
„Gesichtsabgleich bestanden“ / „Gesichtsabgleich fehlgeschlagen“ · Gegenstand check (
face_match)
Wann er ausgelöst wird. Nach der Orchestrierung, wenn die Gesichtsprüfung (Selfie ↔ Porträt im Dokument) einen eindeutigen booleschen Wert isMatch gespeichert hat. passed, wenn isMatch wahr ist, failed, wenn er falsch ist. Eine fehlerhafte oder mehrdeutige Prüfung erzeugt keines von beiden.
Datenkontext.
| Feld | Typ | Hinweise |
|---|---|---|
faceMatch.isMatch | boolean | Ob das Selfie mit dem Gesicht im Dokument übereinstimmt |
faceMatch.confidence | number | null | Zuverlässigkeit 0–1 |
{
"organizationId": "org_01H...",
"event": "face_match.failed",
"kycRequestId": "req_01J...",
"checkKind": "face_match",
"context": { "faceMatch": { "isMatch": false, "confidence": 0.42 } }
}
Beispiel für die Verwendung im Workflow. Jeden Fehlschlag des Gesichtsabgleichs zur manuellen Prüfung eskalieren:
trigger:
event: face_match.failed
subject: check
scope: { mode: all }
sanctions.hit / sanctions.cleared
„Sanktionstreffer“ / „Sanktionsprüfung ohne Treffer“ · Gegenstand check (
sanctions)
Wann er ausgelöst wird. Nach der Orchestrierung, aus dem gespeicherten Ergebnis der Sanktions- bzw. Watchlist-Prüfung. hit, wenn ein Treffer den Schwellenwert erreicht (oder überhaupt ein Treffer vorliegt); sonst cleared. Ein Treffer, der speziell eine politisch exponierte Person betrifft, löst zusätzlich das dedizierte Ereignis pep.match aus. Hören Sie darauf, wenn Sie sich nur für PEP-Exponierung interessieren.
Datenkontext.
| Feld | Typ | Hinweise |
|---|---|---|
sanctions.isMatch | boolean | Wahr bei einem Treffer |
sanctions.matchCount | number | Anzahl der Watchlist-Treffer |
sanctions.topScore | number | null | Stärkster Trefferwert 0–1 |
sanctions.listsChecked | string[] | Geprüfte Listen (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"] }
}
}
Beispiel für die Verwendung im Workflow. Bei einem starken Sanktionstreffer eskalieren und die Compliance-Abteilung benachrichtigen:
trigger:
event: sanctions.hit
subject: check
scope: { mode: all }
filter: 'sanctions.topScore >= 0.85'
pep.match
„PEP-Treffer gefunden“ · Gegenstand check (
sanctions)
Wann er ausgelöst wird. Nach der Orchestrierung, wenn das Ergebnis der Sanktions- bzw. Watchlist-Prüfung mindestens einen Treffer enthält, dessen Entitätstyp eine politisch exponierte Person ist. Er basiert auf derselben Prüfung wie sanctions.*, wird aber nur ausgelöst, wenn ein PEP-Eintrag vorliegt — ein reiner Watchlist-Treffer ohne PEP-Eintrag löst ihn nicht aus.
Datenkontext.
| Feld | Typ | Hinweise |
|---|---|---|
pep.isMatch | boolean | Immer true (das Ereignis wird nur bei einem PEP-Treffer ausgelöst) |
pep.matchCount | number | Anzahl der PEP-Treffer |
{
"organizationId": "org_01H...",
"event": "pep.match",
"kycRequestId": "req_01J...",
"checkKind": "sanctions",
"context": { "pep": { "isMatch": true, "matchCount": 1 } }
}
Beispiel für die Verwendung im Workflow. Immer dann, wenn eine PEP erkannt wird, die erweiterte Sorgfaltsprüfung (Enhanced Due Diligence) automatisch auslösen und an die Compliance-Abteilung weiterleiten:
trigger:
event: pep.match
subject: check
scope: { mode: all }
kyb.match_found / kyb.match_not_found
„KYB-Unternehmenstreffer gefunden“ / „KYB-Treffer nicht gefunden“ · organisationsweit
Wann er ausgelöst wird. Nur für Anfragen vom Typ BUSINESS, nachdem die Orchestrierung das Ergebnis der Prüfung im Unternehmensregister geschrieben hat. verified → kyb.match_found; failed → kyb.match_not_found. Jeder andere Status (ausstehend/fehlend) ist mehrdeutig und erzeugt kein Ereignis.
Datenkontext.
| Feld | Typ | Hinweise |
|---|---|---|
kyb.status | string | "verified" oder "failed" |
{
"organizationId": "org_01H...",
"event": "kyb.match_not_found",
"kycRequestId": "req_01J...",
"context": { "kyb": { "status": "failed" } }
}
Beispiel für die Verwendung im Workflow. Zusätzliche Gründungsdokumente anfordern, wenn kein Registereintrag gefunden wird.
kyb.ubo_completed
„UBO-Verifizierung abgeschlossen“ · organisationsweit
Wann er ausgelöst wird. Nur für Anfragen vom Typ BUSINESS, nachdem die Orchestrierung jeden Datensatz zu den wirtschaftlich Berechtigten (UBO) des Unternehmens aufgelöst hat (keiner mehr pending). Er enthält die Anzahl der geprüften UBOs und wie viele davon nicht bestanden haben, sodass ein Workflow nach UBO-Risiko verzweigen kann, ohne erneut abzufragen. Er wird nur ausgelöst, wenn das Unternehmen tatsächlich UBO-Datensätze hat und der KYB-Datensatz in diesem Lauf berührt wurde.
Datenkontext.
| Feld | Typ | Hinweise |
|---|---|---|
kyb.uboCount | number | Anzahl der geprüften wirtschaftlich Berechtigten |
kyb.failedUboCount | number | Wie viele dieser UBOs die Verifizierung nicht bestanden haben |
{
"organizationId": "org_01H...",
"event": "kyb.ubo_completed",
"kycRequestId": "req_01J...",
"context": { "kyb": { "uboCount": 3, "failedUboCount": 1 } }
}
Beispiel für die Verwendung im Workflow. Eskalieren, wenn ein wirtschaftlich Berechtigter die Prüfung nicht besteht:
trigger:
event: kyb.ubo_completed
filter: 'kyb.failedUboCount > 0'
Manuelle Prüfung
reviewer.decision
„Ein Prüfer trifft eine manuelle Entscheidung“ · organisationsweit
Wann er ausgelöst wird. Ein menschlicher Prüfer hält im Dashboard eine manuelle Entscheidung fest — Einzelgenehmigung, eine der beiden Stufen eines Vier-Augen-Ablaufs oder eine Sammelaktion.
Datenkontext.
| Feld | Typ | Hinweise |
|---|---|---|
decision | string | Die Wahl des Prüfers |
reviewerId | string | ID des entscheidenden Prüfers |
stage | string | undefined | "first_approval" / "second_approval" bei Abläufen mit zwei Prüfern |
via | string | undefined | "bulk", wenn Teil einer Sammelaktion |
{
"organizationId": "org_01H...",
"event": "reviewer.decision",
"kycRequestId": "req_01J...",
"context": { "decision": "approve", "reviewerId": "usr_01H...",
"stage": "second_approval" }
}
Beispiel für die Verwendung im Workflow. Jede manuelle Entscheidung im Audit-Protokoll festhalten und dem Antragsteller nach einer abschließenden (zweiten) Genehmigung Bescheid geben.
reviewer.assigned
„Eine Anfrage wird einem Prüfer zugewiesen“ · organisationsweit
Wann er ausgelöst wird. Eine Anfrage wird über die Aktion „Neu zuweisen“ im Dashboard einem neuen Inhaber bzw. Prüfer zugewiesen. Der ursprüngliche Inhaber, der bei der Anlage der Anfrage gesetzt wird, löst dieses Ereignis nicht aus — nur eine ausdrückliche Neuzuweisung tut das. Das Ereignis markiert daher eine echte Änderung der Weiterleitung.
Datenkontext.
| Feld | Typ | Hinweise |
|---|---|---|
assignedUserId | string | Der neue Inhaber bzw. Prüfer |
previousUserId | string | null | Der bisherige Inhaber |
teamId | string | null | Das Team, zu dem die Anfrage nun gehört |
{
"organizationId": "org_01H...",
"event": "reviewer.assigned",
"kycRequestId": "req_01J...",
"context": { "assignedUserId": "usr_new...", "previousUserId": "usr_old...",
"teamId": "team_01H..." }
}
Beispiel für die Verwendung im Workflow. Dem neu zugewiesenen Prüfer eine Benachrichtigung mit einem direkten Link zum Fall senden.
reviewer.comment_added
„Ein Prüfer fügt einen Kommentar hinzu“ · organisationsweit
Wann er ausgelöst wird. Ein Prüfer veröffentlicht einen Kommentar zu einem Fall (der Kommentarverlauf des Falls). Der Text des Kommentars wird absichtlich nicht in den Ereigniskontext aufgenommen — er kann vertrauliche Notizen enthalten —, daher enthält das Ereignis nur Metadaten.
Datenkontext.
| Feld | Typ | Hinweise |
|---|---|---|
commentId | string | Die ID des erstellten Kommentars |
authorId | string | Der kommentierende Prüfer |
authorName | string | Anzeigename des Verfassers |
{
"organizationId": "org_01H...",
"event": "reviewer.comment_added",
"kycRequestId": "req_01J...",
"context": { "commentId": "cmt_01J...", "authorId": "usr_01H...",
"authorName": "Jordan Lee" }
}
Beispiel für die Verwendung im Workflow. Neue Fallkommentare für das Compliance-Team in einen Slack-Kanal spiegeln.
edd.triggered
„Erweiterte Sorgfaltsprüfung (EDD) wird ausgelöst“ · Gegenstand applicant
Wann er ausgelöst wird. Die erweiterte Sorgfaltsprüfung (Enhanced Due Diligence) wird für eine Anfrage gestartet (derzeit über die manuelle EDD-Aktion).
Datenkontext.
| Feld | Typ | Hinweise |
|---|---|---|
kind | string | "edd" |
{
"organizationId": "org_01H...",
"event": "edd.triggered",
"kycRequestId": "req_01J...",
"context": { "kind": "edd" }
}
Beispiel für die Verwendung im Workflow. Beim Start der EDD Nachweise zur Herkunft der Mittel anfordern und den Fall der Compliance-Abteilung zuweisen.
Risiko / Score
Beide Score-Ereignisse werden aus dem Entscheidungspfad ausgelöst, sobald der Scorer einen Vertrauenswert für den Lauf geschrieben hat (eine Anfrage, die ohne Score abgeschlossen wird — etwa wenn sie vor der Bewertung storniert wurde —, erzeugt keines von beiden). Sie tragen den vollständigen Entscheidungskontext, sodass overallScore, decision, status und applicant.* allesamt für Filter verfügbar sind.
score.updated
„Der Vertrauenswert wird aktualisiert“ · Gegenstand applicant
Wann er ausgelöst wird. Bei jeder (erneuten) Bewertung beim Abschluss — einschließlich einer erneuten Analyse, die den Score neu berechnet. Verwenden Sie ihn, um unabhängig vom Ergebnis auf den neuen Vertrauenswert zu reagieren.
Datenkontext. Der gemeinsame Entscheidungskontext (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" }
}
}
Beispiel für die Verwendung im Workflow. Jede Anfrage markieren, deren Score in einen Bereich für manuelle Prüfung fällt:
trigger:
event: score.updated
subject: applicant
scope: { mode: all }
filter: 'overallScore >= 50 AND overallScore < 70'
score.threshold
„Der Vertrauenswert überschreitet einen Schwellenwert“ · Gegenstand applicant
Wann er ausgelöst wird. Derselbe Erzeugungspunkt wie bei score.updated, doch der Dispatcher grenzt ihn über die eigenen Grenzen scoreMin / scoreMax des Auslösers ein (er liest context.overallScore). Legen Sie die Grenzen am Auslöser fest, und der Workflow läuft nur, wenn der Score innerhalb davon liegt — eine Erleichterung gegenüber einem von Hand geschriebenen Filter auf overallScore.
Datenkontext. Identisch mit 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" }
}
}
Beispiel für die Verwendung im Workflow. Nur bei niedrigen Scores laufen lassen, indem am Auslöser eine obere Grenze gesetzt wird:
trigger:
event: score.threshold
subject: applicant
scope: { mode: all }
scoreMax: 50 # fire only when overallScore <= 50
scoreMin/scoreMaxsind einschließlich zu verstehen. Setzen Sie eine oder beide. Lassen Sie beide leer, verhält sichscore.thresholdwiescore.updated.
Zeit / Überwachung
schedule
„Nach einem Zeitplan“ · organisationsweit, nicht an eine Anfrage gebunden
Wann er ausgelöst wird. Nach dem eigenen Cron-Ausdruck des Workflows (festgelegt im Feld schedule des Auslösers). Der Lauf ist auf Organisationsebene angesiedelt — kycRequestId ist null —, verwenden Sie ihn daher für Stapel- oder Wartungsarbeiten, nicht für Logik je Antragsteller.
Der Cron ist ein Standardausdruck mit 5 Feldern (Minute Stunde Tag Monat Wochentag), ausgewertet in UTC, und darf höchstens alle 5 Minuten auslösen: Die Veröffentlichung lehnt * * * * * oder */2 * * * * ab.
Datenkontext.
| Feld | Typ | Hinweise |
|---|---|---|
firedAt | string | Auslösezeitpunkt im ISO-8601-Format |
cron | string | Der Cron-Ausdruck, der ausgelöst hat |
{
"organizationId": "org_01H...",
"event": "schedule",
"kycRequestId": null,
"context": { "firedAt": "2026-05-31T09:00:00.000Z", "cron": "0 9 * * 1" }
}
Beispiel für die Verwendung im Workflow. Jeden Montag um 09:00 eine wöchentliche Zusammenfassung der Verifizierungen erstellen und in Slack veröffentlichen:
trigger:
event: schedule
schedule: "0 9 * * 1"
verification.expired
„Eine Verifizierung läuft ab“ · Gegenstand applicant
Wann er ausgelöst wird. Aus dem Ablauf-Cron, wenn eine Anfrage ihr Ablauffenster überschreitet.
Datenkontext.
| Feld | Typ | Hinweise |
|---|---|---|
status | string | "EXPIRED" |
{
"organizationId": "org_01H...",
"event": "verification.expired",
"kycRequestId": "req_01J...",
"context": { "status": "EXPIRED" }
}
Beispiel für die Verwendung im Workflow. Dem Antragsteller einen neuen Link per E-Mail senden und den Inhaber benachrichtigen, wenn eine Verifizierung abläuft.
monitoring.alert
„Ein Überwachungsalarm wird ausgelöst“ · organisationsweit
Wann er ausgelöst wird. Aus der laufenden erneuten Sanktionsprüfung, wenn ein zuvor freigegebener Betroffener neu mit einer Watchlist übereinstimmt (kontinuierliche Überwachung).
Datenkontext.
| Feld | Typ | Hinweise |
|---|---|---|
alertType | string | Die Art des Überwachungsalarms |
severity | string | Schweregrad des Alarms |
topScore | number | null | Stärkster neuer Trefferwert |
{
"organizationId": "org_01H...",
"event": "monitoring.alert",
"kycRequestId": "req_01J...",
"context": { "alertType": "sanctions_rescreen", "severity": "high",
"topScore": 0.88 }
}
Beispiel für die Verwendung im Workflow. Bei einem Überwachungsalarm mit hohem Schweregrad die Anfrage eskalieren und einen Compliance-Fall eröffnen.
Extern / Integration
inbound.webhook
„Ein eingehender Webhook trifft ein“ · organisationsweit, beliebige Nutzlast
Wann er ausgelöst wird. Ihr System sendet einen POST an den eingehenden Webhook-Endpunkt des Workflows. Der context ist das JSON, das Sie senden; die Form bestimmen Sie also vollständig selbst. Er ist nicht an eine Anfrage gebunden, sofern Ihre Nutzlast nicht eine ID mitliefert, die Ihr Workflow verwendet.
Endpunkt. POST /api/v1/kyc/workflows/inbound, authentifiziert mit einem API-Schlüssel mit dem Scope workflows:trigger. Kein kyc:*-Scope enthält ihn: Aktivieren Sie beim Erstellen oder Bearbeiten des Schlüssels Automatisierungs-Workflows → Auslösen. Ihr Tarif muss Automatisierungs-Workflows enthalten (sonst 402). Senden Sie context (Ihr JSON-Objekt, maximal 64 KB) und optional eventId (Ihre eigene ID: ein erneuter Versuch mit derselben eventId startet denselben Workflow nie zweimal). Der Aufruf antwortet mit 202, die Ausführungen laufen im Hintergrund. Ein auf bestimmte Workflows beschränkter Schlüssel startet nur diese.
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"}}'
Datenkontext. Beliebig — der gesendete JSON-Body, unverändert.
{
"organizationId": "org_01H...",
"event": "inbound.webhook",
"context": { "externalRef": "user-9281", "tier": "vip", "source": "crm" }
}
Beispiel für die Verwendung im Workflow. Lassen Sie Ihr CRM eine erneute Verifizierungscharge für markierte Kunden starten; filtern Sie nach einem Feld, das Sie mitgesendet haben:
trigger:
event: inbound.webhook
filter: 'tier = "vip"'
conference.completed
„Eine Live-Konferenz ist abgeschlossen“ · organisationsweit
Wann er ausgelöst wird. Eine Live-Videokonferenz zur ID-Verifizierung endet, und der Moderator (Notar bzw. Prüfer) hält eine Entscheidung fest. Er wird einmal erzeugt, und ein Schutz verhindert, dass eine gleichzeitige Entscheidung ihn doppelt auslöst.
Datenkontext.
| Feld | Typ | Hinweise |
|---|---|---|
decision | string | Die vom Moderator festgehaltene Entscheidung |
status | string | Der daraus resultierende Status der Anfrage |
{
"organizationId": "org_01H...",
"event": "conference.completed",
"kycRequestId": "req_01J...",
"context": { "decision": "approve", "status": "APPROVED" }
}
Beispiel für die Verwendung im Workflow. Nach einer Konferenz die signierte Verifizierungsbescheinigung erstellen und dem Antragsteller per E-Mail senden.
Filtern mit der DSL
Der Filter des Auslösers (filter) und jeder Bedingungsknoten verwenden die Ausdrucksgrammatik der Richtlinie — ein boolesches Prädikat ohne Funktionsaufrufe:
- Felder: der flach gemachte Kontext des Ereignisses (die Felder in der Tabelle Datenkontext jedes Auslösers).
- Operatoren:
=!=>>=<<=,IN/NOT IN. - Verknüpfungen:
ANDORNOT, mit Gruppierung über( ). - Literale: Zahlen,
"Zeichenketten",true/false/nullund Listen[...].
Der Gleichheitsoperator ist ein einzelnes
=(nicht==). Fehlende Felder sind ausfallsicher: Ein fehlendes Feld wird als falsch ausgewertet statt einen Fehler zu werfen — ein Filter, dersanctions.topScoreauf einem Ereignis ohne Sanktionsbezug verwendet, greift also einfach nicht.
Welche Felder belegt sind, hängt vom Ereignis ab: Ein Ereignis sanctions.* trägt sanctions.*, ein Ereignis liveness.* trägt liveness.*, und Entscheidungsereignisse tragen overallScore / decision / status / applicant.*.
| Feld | Typ | Verfügbar bei |
|---|---|---|
overallScore | number | Entscheidungs- / Statusereignisse |
decision | string | Entscheidungs- / Statusereignisse |
status | string | Status- / Lebenszyklusereignisse |
applicant.country | string | Einstiegs- / Entscheidungsereignisse |
applicant.type | string | Einstiegs- / Entscheidungsereignisse |
document.type | string | document.* |
document.side | string | document.* |
liveness.isLive | boolean | liveness.* |
liveness.confidence | number | liveness.* |
faceMatch.isMatch | boolean | face_match.* |
faceMatch.confidence | number | face_match.* |
sanctions.isMatch | boolean | sanctions.* |
sanctions.topScore | number | sanctions.* |
sanctions.matchCount | number | sanctions.* |
pep.isMatch | boolean | pep.match |
pep.matchCount | number | pep.match |
kyb.status | string | kyb.match_* |
kyb.uboCount | number | kyb.ubo_completed |
kyb.failedUboCount | number | kyb.ubo_completed |
reviewerId | string | reviewer.decision |
Beispielprädikate:
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
Im Editor drücken Sie Strg+Leertaste in jedem Ausdrucksfeld, um die kontextabhängige Autovervollständigung und ein Hilfe-Popover mit Feldern, Operatoren und fertigen Beispielen zu öffnen.
Vergleichstabelle
| Auslöser | Kategorie | Gegenstand | An Anfrage gebunden | Häufigkeit | Hinweise |
|---|---|---|---|---|---|
form.submitted | Einstieg | form | ja | pro Einreichung | Nur gehostete Formulare |
request.created | Einstieg | applicant | ja | pro Anfrage | Bevor eine Prüfung läuft |
request.status_changed | Lebenszyklus | applicant | ja | pro Statusänderung | Generisch; ergänzt spezifische Ereignisse |
request.approved | Lebenszyklus | applicant | ja | pro Abschluss | |
request.rejected | Lebenszyklus | applicant | ja | pro Abschluss | |
request.escalated | Lebenszyklus | applicant | ja | pro Abschluss | |
request.resubmitted | Lebenszyklus | applicant | ja | pro erneuter Einreichung | |
request.documents_ready | Lebenszyklus | applicant | ja | pro abgeschlossener Checkliste | Alle erforderlichen Dokumente hochgeladen |
document.uploaded | Dokumente | document | ja | pro Dokument | |
document.failed | Dokumente | document | ja | pro fehlgeschlagenem Dokument | |
check.completed | Signale | check | ja | pro Prüfung (hoch) | Geschützt; eines pro Prüfung |
liveness.passed | Signale | check | ja | pro Anfrage | Nur eindeutige Fälle |
liveness.failed | Signale | check | ja | pro Anfrage | Nur eindeutige Fälle |
face_match.passed | Signale | check | ja | pro Anfrage | Nur eindeutige Fälle |
face_match.failed | Signale | check | ja | pro Anfrage | Nur eindeutige Fälle |
sanctions.hit | Signale | check | ja | pro Anfrage | Watchlist-Treffer |
sanctions.cleared | Signale | check | ja | pro Anfrage | |
pep.match | Signale | check | ja | pro Anfrage | Nur PEP-Einträge |
kyb.match_found | Signale | — | ja | pro Unternehmensanfrage | Nur BUSINESS |
kyb.match_not_found | Signale | — | ja | pro Unternehmensanfrage | Nur BUSINESS |
kyb.ubo_completed | Signale | — | ja | pro Unternehmensanfrage | Nur BUSINESS; UBOs aufgelöst |
reviewer.decision | Prüfung | — | ja | pro manueller Entscheidung | Stufen + Sammelaktion |
reviewer.assigned | Prüfung | — | ja | pro Neuzuweisung | Nur ausdrückliche Neuzuweisung |
reviewer.comment_added | Prüfung | — | ja | pro Kommentar | Text nicht im Kontext |
edd.triggered | Prüfung | applicant | ja | pro EDD-Start | |
score.updated | Risiko | applicant | ja | pro (erneuter) Bewertung | Wird mit der Entscheidung ausgelöst |
score.threshold | Risiko | applicant | ja | pro (erneuter) Bewertung | Eingegrenzt durch scoreMin/scoreMax |
schedule | Zeit | — | nein | Cron | Lauf auf Organisationsebene |
verification.expired | Zeit | applicant | ja | pro Ablauf | Aus dem Ablauf-Cron |
monitoring.alert | Überwachung | — | ja | pro Alarm | Kontinuierliches Screening |
inbound.webhook | Extern | — | optional | pro POST | Beliebige Nutzlast |
conference.completed | Extern | — | ja | pro Konferenz | Live-ID-Sitzungen |
Auslöser auf der Roadmap
Einige häufig angefragte Auslöser sind nicht in der Auswahl enthalten, da die Plattform noch kein Signal hat, um sie zu erzeugen — würde man sie als wählbare Optionen anbieten, entstünden Auslöser, die nie feuern. Sie sind hier mit der funktionierenden Alternative aufgeführt, damit die Lücke klar benannt ist. (Alles in der Auswahl dagegen wird tatsächlich erzeugt — einschließlich score.threshold, Gesichtsabgleich, PEP, UBO und der Prüferereignisse, die in diesem Release verdrahtet wurden.)
| Gewünschter Auslöser | Warum er nicht erzeugt wird | Stattdessen verwenden |
|---|---|---|
| Betrugssignal erkannt | Es gibt noch keinen eigenständigen Prüftyp für Forensik bzw. Echtheit (Forensik ist derzeit ein Faktor der Risikobewertung, keine eigene Prüfung, die ein Ergebnis erzeugt). | Auf document.failed verzweigen oder auf die Entscheidungsereignisse / score.threshold für das daraus resultierende Risiko. |
| Daten des Unternehmensregisters aktualisiert | KYB ist eine einmalige Verifizierung; es gibt keinen laufenden Job zum erneuten Abruf oder zur Überwachung des Registers, von dem aus man auslösen könnte. | Verifizierung über einen schedule erneut ausführen und auf kyb.match_* / kyb.ubo_completed verzweigen. |
| E-Mail- / SMS-Link angeklickt | Es gibt keine Infrastruktur zur Nachverfolgung von geöffneten E-Mails oder angeklickten Links. | form.submitted / document.uploaded als konkrete Signale verwenden, dass „der Antragsteller gehandelt hat“. |
Dies sind echte Roadmap-Punkte, keine Lücken in der Dokumentation — die Enum der Auslöser in der Engine ist die einzige Quelle der Wahrheit, und diese Tabelle zeigt, was sie noch nicht enthält.
Bewährte Vorgehensweisen
- Legen Sie immer Gegenstand + Umfang fest. Ein Auslöser ohne Gegenstand gilt organisationsweit und feuert für jedes passende Objekt in der Organisation. Bevorzugen Sie den Umfang
specificodercategory; reservieren Sieallfür wirklich organisationsweite Automatisierungen. - Hören Sie nach Möglichkeit auf spezifische statt auf generische Ereignisse. Verwenden Sie
liveness.failed/sanctions.hitstattcheck.completedauszuwerten, und ein einzelnes Entscheidungsereignis stattrequest.status_changed— es sei denn, Sie benötigen gezielt einen Status ohne eigenes Ereignis. - Hören Sie nicht sowohl auf die spezifischen als auch auf die generischen Statusereignisse.
request.approvedundrequest.status_changedfeuern beide bei einer Genehmigung; binden Sie zwei Workflows (oder zwei Auslöser) an dasselbe Ergebnis, werden Nebenwirkungen doppelt ausgeführt. - Behandeln Sie den Kontext als Momentaufnahme. Der Kontext des Ereignisses wird bei der Auslösung erfasst. Wartet Ihr Workflow (ein Warteknoten, eine lange Verzögerung), lesen Sie den Live-Zustand innerhalb einer Aktion erneut, statt bei einer kritischen Entscheidung dem veralteten Kontext zu vertrauen.
- Halten Sie Workflows für
check.completedschlank. Es ist der Auslöser mit der höchsten Häufigkeit; grenzen Sie ihn auf eine Kategorie ein und filtern Sie früh, damit Sie nicht über jede Prüfung jeder Anfrage fächern. - Achten Sie auf verwaiste Umfänge. Das Löschen eines Formulars bzw. Antragstellers, auf den ein
specific-Umfang verweist, macht den Auslöser inaktiv (er feuert nicht mehr und zeigt ein Abzeichen „Neu zuordnen erforderlich“). Ordnen Sie ihn einem bestehenden Objekt neu zu, um ihn zu reaktivieren. - Workflows eskalieren, entscheiden aber nie. Wenn das Ergebnis eines Auslösers das Urteil ändern soll, leiten Sie es über
escalatean die manuelle Prüfung weiter — es gibt bewusst keine Aktion zum Genehmigen oder Ablehnen. - Rechnen Sie mit mindestens einmaliger, irgendwann erfolgender Ausführung. Die Auslösung ist asynchron und fire-and-forget; machen Sie die Nebenwirkungen von Aktionen dort idempotent, wo es darauf ankommt (z. B. indem Sie eine externe Referenz mitführen, damit eine erneute Auslösung keine nachgelagerten Datensätze doppelt anlegt).
Häufige Fragen
Kann ein Workflow eine Verifizierung genehmigen oder ablehnen?
Nein. Der Satz an Aktionen enthält kein approve / reject. Die stärkste Steuerung des Ergebnisses ist escalate, das die Anfrage über die bestehende, optimistisch gesperrte und protokollierte Zustandsmaschine in die manuelle Prüfung überführt. Das Urteil liegt bei der Entscheidungsrichtlinie und den menschlichen Prüfern.
Ein spezifisches Entscheidungsereignis und request.status_changed haben beide gefeuert — ist das ein Fehler?
Nein. Das generische request.status_changed wird immer zusammen mit dem spezifischen Ereignis erzeugt, damit Sie auf Status reagieren können, für die es kein eigenes Ereignis gibt. Hören Sie auf eines davon, nicht auf beide.
Warum wurde mein Workflow für check.completed nicht ausgeführt?
Die Fan-out-Erzeugung je Prüfung ist geschützt: Sie löst nur aus, wenn ein aktiver Workflow auf check.completed lauscht. Prüfen Sie, dass der Workflow aktiv ist und sein Umfang bzw. Filter zur Prüfung passt. Beachten Sie außerdem, dass fehlerhafte Prüfungen ohne verwertbaren booleschen Fakt die spezifischen Signale liveness.* / sanctions.* nicht erzeugen.
Warum feuert mein Auslöser score.threshold nicht?
Er feuert bei jedem bewerteten Abschluss, und der Dispatcher grenzt ihn anschließend über die Grenzen scoreMin / scoreMax des Auslösers ein. Wird er nie ausgeführt, prüfen Sie, ob der Score tatsächlich innerhalb Ihrer Grenzen liegt und ob die Anfrage ein bewertetes Ergebnis erreicht hat (eine vor der Bewertung stornierte Anfrage erzeugt überhaupt kein Score-Ereignis). Ohne gesetzte Grenzen verhält er sich wie score.updated.
Worin unterscheiden sich score.updated und score.threshold?
Gleicher Erzeugungspunkt und gleicher Kontext. score.updated läuft immer; score.threshold läuft nur, wenn overallScore innerhalb der Grenzen scoreMin / scoreMax des Auslösers liegt — eine eingebaute Erleichterung, damit Sie den Filter auf overallScore nicht selbst schreiben müssen.
Wie löse ich bei einem Gesichtsabgleich- oder PEP-Ergebnis aus?
Verwenden Sie die dedizierten Ereignisse face_match.passed / face_match.failed und pep.match. Ein PEP-Treffer löst auch sanctions.hit aus (das PEP-Screening läuft über das Watchlist-Screening), daher hören Sie auf pep.match, wenn Sie sich nur für die PEP-Exponierung interessieren.
Deaktiviert das Löschen eines Formulars dessen Workflows?
Ein Workflow, dessen Auslöser specific-Umfang nur auf dieses Formular hat, wird verwaist: Er wird inaktiv (feuert nicht) und zeigt ein Abzeichen „Neu zuordnen erforderlich“. Ordnen Sie ihn einem bestehenden Objekt neu zu, um ihn zu reaktivieren. Er fällt nie stillschweigend auf eine organisationsweite Auslösung zurück.
Ist die Auslösung synchron zu meinem API-Aufruf? Nein. Die Auslösung ist fire-and-forget und läuft außerhalb des Hauptpfads. Die ursprüngliche Aktion (Upload, Statusänderung) gelingt unabhängig davon, ob die Auslösung oder der Lauf des Workflows gelingt.
Kann ein Workflow einen anderen Workflow auslösen?
Ja — die Aktion trigger_workflow führt einen ausgewählten gespeicherten Workflow direkt aus, begrenzt auf eine maximale Verkettungstiefe von 5, um Schleifen zu verhindern.
Anhang
Glossar
- Auslöser — der einzige Einstiegsknoten, der Ereignis, Gegenstand, Umfang und Filter festlegt, die einen Workflow starten.
- Ereignis — ein
WorkflowTriggerEvent, das die Plattform an einem realen Punkt des Verifizierungslebenszyklus erzeugt. - Gegenstand — die Objektart, an die ein Auslöser gebunden ist:
form,applicant,documentodercheck. - Umfang — wie der Gegenstand eingegrenzt wird:
specific(IDs),categoryoderall. - Kontext — die ereignisspezifischen Fakten, die bei der Auslösung erfasst und von Filtern und Aktionsvorlagen gelesen werden.
- Filter — ein optionales DSL-Prädikat, das weiter eingrenzt, welche Ereignisse passen.
- Verwaister Auslöser — ein Auslöser mit
specific-Umfang, dessen Objekte gelöscht wurden; inaktiv, bis er neu zugeordnet wird. - Lauf — ein
KycWorkflowRun: eine Ausführung eines Workflows mit einem nur anfügbarenlog.
Vollständige Enum der Ereignisse
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";
Ablauf einer Auslösung
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
Durchgängiges Beispiel
Eine vollständige Onboarding-Automatisierung, an ein KYC-Formular gebunden:
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
Dies legt das Ergebnis nie fest: Der einzige ergebnisbeeinflussende Schritt ist escalate, der an die manuelle Prüfung weiterleitet. Alles Übrige sind Benachrichtigungen, Datenerfassung und Berichterstattung rund um die Entscheidung, die die Richtlinie und die Prüfer verantworten.
Automatisierungs-Workflows
Ereignisgesteuerte, ausführbare Workflows, die um eine Verifizierung herum weiterleiten, benachrichtigen und koordinieren — ohne jemals das Ergebnis festzulegen.
Web-SDK
Zeigen Sie eine Certivant-Verifizierung mit einem einzigen Script-Tag in Ihrer eigenen Website an — als Modal oder inline, mit den Ereignissen ready, step, complete, cancel und error, strengen Origin-Prüfungen und Kameraunterstützung.