Automatisierungs-WorkflowsAuslöser

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 context des 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:

FeldTypVorhanden, wenn
organizationIdstringimmer
eventWorkflowTriggerEventimmer
kycRequestIdstring | nulldas Ereignis eine bestimmte Verifizierung betrifft (die meisten Ereignisse)
formIdstring | nullform.submitted
formKind"kyc" | "kyb" | nullform.submitted
applicantIdstring | nullantragstellerbezogene Ereignisse
documentTypestring | nulldocument.*
checkKindstring | nullcheck.completed, liveness.*, sanctions.*
contextobjectereignisspezifische Fakten (siehe unten bei jedem Auslöser)
workflowIdstringnur bei einem direkten Aufruf über trigger_workflow
chainDepthnumberVerkettungstiefe 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.

GegenstandGültige EreignisseOptionen für scope.modeBedeutung von category
formform.submittedspecific (Formular-IDs) · category · all"kyc" oder "kyb"
applicantrequest.*, score.threshold, edd.triggered, verification.expiredspecific (Antragsteller-IDs) · all—
documentdocument.uploaded, document.failedspecific · category · allein Dokumenttyp, z. B. "passport"
checkcheck.completed, liveness.*, sanctions.*specific · category · alleine 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ührten ids (z. B. nur für dieses Onboarding-Formular). Wenn jede ID in einem specific-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

KategorieAuslöser
Einstiegform.submitted, request.created
Lebenszyklus / Statusrequest.status_changed, request.approved, request.rejected, request.escalated, request.resubmitted, request.documents_ready
Dokumentedocument.uploaded, document.failed
Verifizierungssignalecheck.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üfungreviewer.decision, reviewer.assigned, reviewer.comment_added, edd.triggered
Risiko / Scorescore.threshold, score.updated
Zeit / Überwachungschedule, verification.expired, monitoring.alert
Extern / Integrationinbound.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.

FeldTypHinweise
typestringAntragstellertyp 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.

FeldTypHinweise
applicant.countrystring | nullISO-3166 Alpha-2, aus der Staatsangehörigkeit des Antragstellers
applicant.typestring"INDIVIDUAL" oder "BUSINESS"
typestringAnfragetyp (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):

FeldTypHinweise
statusstringNeuer Status, z. B. "APPROVED", "REJECTED", "ESCALATED", "PENDING_SECOND_REVIEW"
decisionstring | nullErgebnis des Scorers: "approve" / "reject" / "escalate"
overallScorenumber | nullVertrauenswert 0–100 (höher = geringeres Risiko)
applicant.countrystring | nullISO-3166 Alpha-2
applicant.typestringINDIVIDUAL / 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.

FeldTypHinweise
statusstring"PENDING_UPLOAD"
kindstring"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.

FeldTypHinweise
statusstring"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.

FeldTypHinweise
document.typestringz. B. "passport", "id_card", "drivers_license"
document.sidestring | 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.completed lauscht. 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.

FeldTypHinweise
check.typestringArt der Prüfung, z. B. "sanctions", "face_match", "ocr"
check.statusstring"passed" / "failed" / "error"
check.providerstring | nullAnbieter, der die Prüfung durchgeführt hat
check.scorenumber | nullWert 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.

FeldTypHinweise
liveness.isLivebooleanOb der Antragsteller die Lebenderkennung bestanden hat
liveness.confidencenumber | nullZuverlä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.

FeldTypHinweise
faceMatch.isMatchbooleanOb das Selfie mit dem Gesicht im Dokument übereinstimmt
faceMatch.confidencenumber | nullZuverlä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.

FeldTypHinweise
sanctions.isMatchbooleanWahr bei einem Treffer
sanctions.matchCountnumberAnzahl der Watchlist-Treffer
sanctions.topScorenumber | nullStärkster Trefferwert 0–1
sanctions.listsCheckedstring[]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.

FeldTypHinweise
pep.isMatchbooleanImmer true (das Ereignis wird nur bei einem PEP-Treffer ausgelöst)
pep.matchCountnumberAnzahl 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.

FeldTypHinweise
kyb.statusstring"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.

FeldTypHinweise
kyb.uboCountnumberAnzahl der geprüften wirtschaftlich Berechtigten
kyb.failedUboCountnumberWie 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.

FeldTypHinweise
decisionstringDie Wahl des Prüfers
reviewerIdstringID des entscheidenden Prüfers
stagestring | undefined"first_approval" / "second_approval" bei Abläufen mit zwei Prüfern
viastring | 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.

FeldTypHinweise
assignedUserIdstringDer neue Inhaber bzw. Prüfer
previousUserIdstring | nullDer bisherige Inhaber
teamIdstring | nullDas 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.

FeldTypHinweise
commentIdstringDie ID des erstellten Kommentars
authorIdstringDer kommentierende Prüfer
authorNamestringAnzeigename 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.

FeldTypHinweise
kindstring"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 / scoreMax sind einschließlich zu verstehen. Setzen Sie eine oder beide. Lassen Sie beide leer, verhält sich score.threshold wie score.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.

FeldTypHinweise
firedAtstringAuslösezeitpunkt im ISO-8601-Format
cronstringDer 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.

FeldTypHinweise
statusstring"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.

FeldTypHinweise
alertTypestringDie Art des Überwachungsalarms
severitystringSchweregrad des Alarms
topScorenumber | nullStä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.

FeldTypHinweise
decisionstringDie vom Moderator festgehaltene Entscheidung
statusstringDer 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: AND OR NOT, mit Gruppierung über ( ).
  • Literale: Zahlen, "Zeichenketten", true / false / null und 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, der sanctions.topScore auf 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.*.

FeldTypVerfügbar bei
overallScorenumberEntscheidungs- / Statusereignisse
decisionstringEntscheidungs- / Statusereignisse
statusstringStatus- / Lebenszyklusereignisse
applicant.countrystringEinstiegs- / Entscheidungsereignisse
applicant.typestringEinstiegs- / Entscheidungsereignisse
document.typestringdocument.*
document.sidestringdocument.*
liveness.isLivebooleanliveness.*
liveness.confidencenumberliveness.*
faceMatch.isMatchbooleanface_match.*
faceMatch.confidencenumberface_match.*
sanctions.isMatchbooleansanctions.*
sanctions.topScorenumbersanctions.*
sanctions.matchCountnumbersanctions.*
pep.isMatchbooleanpep.match
pep.matchCountnumberpep.match
kyb.statusstringkyb.match_*
kyb.uboCountnumberkyb.ubo_completed
kyb.failedUboCountnumberkyb.ubo_completed
reviewerIdstringreviewer.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öserKategorieGegenstandAn Anfrage gebundenHäufigkeitHinweise
form.submittedEinstiegformjapro EinreichungNur gehostete Formulare
request.createdEinstiegapplicantjapro AnfrageBevor eine Prüfung läuft
request.status_changedLebenszyklusapplicantjapro StatusänderungGenerisch; ergänzt spezifische Ereignisse
request.approvedLebenszyklusapplicantjapro Abschluss
request.rejectedLebenszyklusapplicantjapro Abschluss
request.escalatedLebenszyklusapplicantjapro Abschluss
request.resubmittedLebenszyklusapplicantjapro erneuter Einreichung
request.documents_readyLebenszyklusapplicantjapro abgeschlossener ChecklisteAlle erforderlichen Dokumente hochgeladen
document.uploadedDokumentedocumentjapro Dokument
document.failedDokumentedocumentjapro fehlgeschlagenem Dokument
check.completedSignalecheckjapro Prüfung (hoch)Geschützt; eines pro Prüfung
liveness.passedSignalecheckjapro AnfrageNur eindeutige Fälle
liveness.failedSignalecheckjapro AnfrageNur eindeutige Fälle
face_match.passedSignalecheckjapro AnfrageNur eindeutige Fälle
face_match.failedSignalecheckjapro AnfrageNur eindeutige Fälle
sanctions.hitSignalecheckjapro AnfrageWatchlist-Treffer
sanctions.clearedSignalecheckjapro Anfrage
pep.matchSignalecheckjapro AnfrageNur PEP-Einträge
kyb.match_foundSignale—japro UnternehmensanfrageNur BUSINESS
kyb.match_not_foundSignale—japro UnternehmensanfrageNur BUSINESS
kyb.ubo_completedSignale—japro UnternehmensanfrageNur BUSINESS; UBOs aufgelöst
reviewer.decisionPrüfung—japro manueller EntscheidungStufen + Sammelaktion
reviewer.assignedPrüfung—japro NeuzuweisungNur ausdrückliche Neuzuweisung
reviewer.comment_addedPrüfung—japro KommentarText nicht im Kontext
edd.triggeredPrüfungapplicantjapro EDD-Start
score.updatedRisikoapplicantjapro (erneuter) BewertungWird mit der Entscheidung ausgelöst
score.thresholdRisikoapplicantjapro (erneuter) BewertungEingegrenzt durch scoreMin/scoreMax
scheduleZeit—neinCronLauf auf Organisationsebene
verification.expiredZeitapplicantjapro AblaufAus dem Ablauf-Cron
monitoring.alertÜberwachung—japro AlarmKontinuierliches Screening
inbound.webhookExtern—optionalpro POSTBeliebige Nutzlast
conference.completedExtern—japro KonferenzLive-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öserWarum er nicht erzeugt wirdStattdessen verwenden
Betrugssignal erkanntEs 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 aktualisiertKYB 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 angeklicktEs 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 specific oder category; reservieren Sie all für wirklich organisationsweite Automatisierungen.
  • Hören Sie nach Möglichkeit auf spezifische statt auf generische Ereignisse. Verwenden Sie liveness.failed / sanctions.hit statt check.completed auszuwerten, und ein einzelnes Entscheidungsereignis statt request.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.approved und request.status_changed feuern 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.completed schlank. 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 escalate an 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, document oder check.
  • Umfang — wie der Gegenstand eingegrenzt wird: specific (IDs), category oder all.
  • 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ügbaren log.

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.