Skip to main content

Workflow Manifest Reference

A workflow is defined by a declarative manifest (JSON). It lists the steps to run and maps named outcomes to actions. This page describes its shape.

Top-level structure​

FieldDescription
versionManifest version number. Required.
workflowNameUnique name within the tenant. Required.
metaDisplay settings: label, the human-readable name.
steps[]Ordered list of steps to execute. Must contain at least one decision step.
outcomesMap of named outcome → list of actions to run. Outcome names are snake_case.

No other keys are allowed. In particular, a manifest must not contain a $schema key: the platform validates manifests itself, and a manifest with $schema is refused with an error naming the key.

Step object​

Every step — capture or evaluation — has a uniform shape:

FieldDescription
orderNumeric position; drives execution sequence. Each value may be used once.
nameHuman-readable label.
actionThe building block to run (e.g. a capture component or an evaluation step).
paramsAction-specific settings (allowed documents, Trust Factor configuration, decision rules, event payload).
idOptional key of the step (letters, digits and underscores). Without it, the step is keyed by its action. Give two steps of the same action different ids when an emit_event payload must reference one of them.

A decision step carries a rule table in its params — see Decision rules.

Illustrative example​

A trimmed onboarding manifest — capture a document and a selfie, evaluate trust and uniqueness, decide, then notify the Company's backend:

{
"version": 1,
"workflowName": "onboarding_standard",
"meta": { "label": "Standard Onboarding" },
"steps": [
{ "order": 1, "name": "Document", "action": "document_capture",
"params": {
"allowedDocuments": [{ "type": "identity-card", "countries": ["SVK", "CZE"] }],
"captureBackPage": true
} },
{ "order": 2, "name": "Face capture", "action": "face_capture" },
{ "order": 3, "name": "Trust factor eval", "action": "tf_eval",
"params": { "trustFactorConfig": "onboarding_strict" } },
{ "order": 4, "name": "Face duplicity check", "action": "dedup_face",
"params": { "trustFactorConfig": "onboarding_strict" } },
{ "order": 5, "name": "Document duplicity check", "action": "dedup_document" },
{ "order": 6, "name": "Combine duplicates", "action": "best_duplicate" },
{ "order": 7, "name": "Decide", "action": "decision",
"params": {
"rules": [
{ "when": "tf_eval.result == 'reject' || document_capture.result == 'ocr_quality_failed'",
"outcome": "failed", "terminate": "reject", "reason": "trust_reject" },
{ "when": "tf_eval.result == 'review'", "outcome": "needs_review", "terminate": "review" },
{ "when": "best_duplicate.type == 'blocklist'", "outcome": "blocked", "terminate": "reject" },
{ "when": "best_duplicate.outcome == 'full_hit' && best_duplicate.type == 'customer'",
"outcome": "returning", "terminate": "accept" },
{ "when": "best_duplicate.outcome == 'no_match'", "outcome": "new_unique", "terminate": "accept" }
],
"default": { "outcome": "needs_review", "terminate": "review" }
} },
{ "order": 8, "name": "Notify backend", "action": "emit_event",
"params": {
"event": "onboarding.decided",
"payload": { "outcome": "${decision.outcome}", "trust": "${tf_eval.result}", "channel": "web" }
} }
],
"outcomes": {
"new_unique": { "actions": [
{ "action": "create_new_customer",
"params": { "external_id_template": "${document.personal_number}_${uuid}" } },
{ "action": "repoint_primary_DI" } ] },
"returning": { "actions": [ { "action": "add_new_DI_to_customer" } ] },
"blocked": { "actions": [ { "action": "1N_add_current_to_blocklist" } ] },
"needs_review": { "actions": [ { "action": "1N_move_to_review" } ] },
"failed": { "actions": [] }
}
}

Building blocks​

Capture steps​

Run on the device of the User, through the hosted app, the web components or the mobile SDK:

ActionCaptures
mobile_redirectHands a desktop session over to a phone (skippable). Must be the first step when present.
document_captureIdentity document pages; allowedDocuments and captureBackPage in params.
nfc_captureThe chip of an electronic document. Requires a document_capture step before it.
face_captureSelfie with passive liveness.
smile_capture, multirange_captureSelfie with active liveness (smile, multi-range).
both_palms_captureLeft and right palm for enrolment — palm verification, tenants with palm enabled only.
single_palm_captureOne palm for authentication — palm tenants only.
external_captureData supplied by the integrator instead of a capture component.

Evaluation steps​

Run server-side:

ActionEvaluatesResult signal
tf_evalThe Trust Factor configuration named in params.trustFactorConfig.tf_eval.result
dedup_faceFace duplicity check (1:N); takes params.trustFactorConfig.dedup_face.result
dedup_documentDocument duplicity check (1:N).dedup_document.result
best_duplicateCombines the two duplicity checks into one outcome. Takes no parameters.best_duplicate.outcome, .type, .hit
matching1:1 face comparison against a known Customer; takes params.trustFactorConfig.matching.result
palm_matching1:1 palm comparison against a known Customer — palm tenants only.palm_matching.result
decisionThe rule table; ends the workflow or lets it continue.decision.outcome (for emit_event)
emit_eventSends a workflow event webhook built from the results of earlier steps.—

Decision signals​

Values a when condition can test:

SignalValues
tf_eval.resultaccept, review, reject
dedup_face.result, dedup_document.resultno_match, customer, blocklist, concurrent, review
best_duplicate.outcomeno_match, full_hit, similar_face, similar_name, face_missmatch, doc_missmatch, multiple_full_hits
best_duplicate.typecustomer, blocklist, concurrent, review
matching.result, palm_matching.resultmatch, no_match
document_capture.resultok, or ocr_quality_failed when the OCR quality retake budget was exhausted

Decision rules​

A decision step has params.rules (at least one rule) and an optional params.default.

FieldDescription
whenThe condition — see Condition syntax.
outcomeName of an outcome defined in outcomes.
terminateThe terminal state the workflow ends in: accept, review, reject or incomplete. Required on every rule.
reasonOptional reason code, returned with the result.

Rules are evaluated in order and the first matching rule wins: the workflow ends in that rule's terminal state and runs the actions of its outcome. This applies wherever the decision sits — a decision in the middle of the workflow that matches ends the workflow there, and the steps after it do not run.

The default (outcome + terminate) applies when no rule matches. Set its terminate to an empty string "" to continue to the next step instead of ending. Without a default, a decision with no matching rule also continues to the next step.

Condition syntax​

Conditions use a subset of CEL:

  • Comparisons — <step>.result == '<value>' for the signals above, best_duplicate.outcome == '<value>', best_duplicate.type == '<value>', and dedup_document.hit == dedup_face.hit (or !=). Values must be from the signal table; != is accepted only in the hit comparison.
  • && (and), || (or) and parentheses combine comparisons. As in CEL, && binds tighter than ||: a && b || c means (a && b) || c. Use parentheses to group differently, e.g. (a || b) && c.
  • Other operators and functions (!, in, <, …) are not supported. The validation error names the rule index and the offending token.
  • A condition can test only steps that run after the previous decision step (or from the start of the workflow, for the first decision), and each step it tests must appear only once in that span — otherwise the reference is ambiguous and the manifest is refused.
{ "when": "(dedup_face.result == 'concurrent' || dedup_face.result == 'review') && tf_eval.result == 'accept'",
"outcome": "needs_review", "terminate": "review" }

The visual decision builder in the dashboard edits conditions joined with && only. A rule that uses || or parentheses is shown as an unsupported format there and is edited on the JSON tab.

Emit event step​

An emit_event step sends one signed workflow event webhook each time the run reaches it — mid-flow or after the final decision.

{ "order": 8, "name": "Notify backend", "action": "emit_event",
"params": {
"event": "onboarding.decided",
"payload": { "outcome": "${decision.outcome}", "trust": "${tf_eval.result}", "channel": "web" }
} }
ParameterDescription
eventThe event name the Company receives. Required, non-empty.
payloadOptional object. Each value is either a literal (text, number, boolean, null, object or array), sent unchanged, or a reference ${<source>.<field>} that is replaced with the result of an earlier step.

References — the <source> is the step's id, or its action when it has no id:

Source actionFields
tf_evalresult
dedup_face, dedup_documentresult, hit
best_duplicateoutcome, type, hit
matchingresult, score
palm_matchingresult
document_captureresult
decisionoutcome (the matched outcome name), status (the terminal state of the matched rule, or null when the decision continued). Always the latest earlier decision.

Rules:

  • A reference must be the whole value of a top-level payload key. A reference embedded in text or nested inside an object is refused.
  • The referenced step must come before the emit_event step, and the source must be unambiguous: when two earlier steps share the same key, give them different ids.
  • Only the results above can be referenced. Personal data, images, document data, IP addresses, device identifiers and session tokens cannot be placed in an event.
  • A reference to a step that did not run in this session is sent as null.
  • An emit_event step cannot come before the first capture step.

Behaviour:

  • The event is sent when the step runs, not at the end of the session, on every data tier. It is sent only when the Company has at least one webhook endpoint configured; otherwise the step does nothing and the run continues.
  • The step has no condition — it fires whenever the run reaches it. For per-outcome behaviour, place it after the decision step and put ${decision.outcome} into the payload. An emit_event step placed after a decision that ends the workflow still runs.
  • Delivery runs in the background: it never delays the session result, and retries follow the webhook delivery guarantees. Each event has its own eventId, kept across retries, and identifies the step that sent it.
  • An event is not transactional with the outcome actions: it can arrive before the outcome's records (for example a new Customer) exist. Use the workflow result webhook or the Customer API for the final state.

Outcome actions​

Run when the workflow ends in the named outcome:

ActionEffect
create_new_customerCreates a Customer from the identity and adds the person to the Customer watchlist. Accepts params.external_id_template.
create_blocked_customerCreates the Customer directly in blocked state. Accepts params.external_id_template.
update_identified_customerUpdates the data of the Customer the session was started for (personal data update, authentication).
add_new_DI_to_customerAttaches the identity to the matched Customer (merge).
repoint_primary_DIMakes this identity the primary identity of the Customer.
1N_add_current_to_watchlist, 1N_add_current_to_blocklist, 1N_move_to_reviewPlaces the person in the Customer, Blocklist or Review watchlist.
reject_concurrents, merge_rejected_concurrentsResolves other in-flight verifications of the same person that were held for review — rejects them, or rejects and merges them into the new Customer.

Deprecated: emit_event outcome action​

The emit_event outcome action, with its outcome-level events[] list, is replaced by the emit_event step. It can no longer be added when authoring a workflow. Manifests that still use it keep working, and the dashboard marks the affected outcomes as deprecated; a future release will refuse them. To migrate, remove the action and the events[] list from the outcome and add an emit_event step after the last decision step with the same event name and payload, plus "outcome": "${decision.outcome}" if your backend needs to know which outcome fired.

External identifier template​

create_new_customer and create_blocked_customer accept an optional external_id_template that composes the external_id of the new Customer — the identifier your systems use to refer to the person (see Customer API → External identifier). Without a template the external_id equals the Customer identifier, as before.

{ "action": "create_new_customer",
"params": { "external_id_template": "${document.personal_number}_${uuid}" } }
TokenValue
${document.personal_number}The personal number read from the document, exactly as printed (including separators).
${document.document_number}The document number.
${uuid}A freshly generated UUID.

Rules:

  • Only the tokens above are allowed. Fields that are not unique per person (names, date of birth, nationality) are deliberately unavailable — a template built from them could give two different people the same identifier.
  • The rendered value must match ^[a-zA-Z0-9._-]{1,64}$ and be unique within the tenant; otherwise the action fails.
  • If a token cannot be resolved at run time (for example the document carries no personal number), the workflow ends incomplete with the reason external_id_unresolved. There is no silent fallback to a UUID, because a record your systems cannot match is worse than a failed run.
  • Templates are validated when the manifest is saved.

A personal number embedded in an identifier is personal data: it is wiped on anonymization together with the rest of the record.

Validation​

On save, a manifest is checked for:

  • Structure — required fields present, no unknown keys (including $schema), unique order values, at least one decision step.
  • Steps — every action is from the catalog and its params are valid; every referenced Trust Factor configuration exists; mobile_redirect is first and nfc_capture follows a document_capture; palm steps are used only by tenants with palm verification enabled.
  • Decisions — every condition follows the condition syntax, tests only known signals of steps in its scope, and every outcome a rule or default names is defined in outcomes.
  • Events — every emit_event step has an event name and only valid, unambiguous references to earlier steps.

All problems are reported at once, each with the path of the offending field. The dashboard runs these checks continuously while you edit.

See also​