Appearance
Concepts
The vocabulary the rest of the documentation uses, one section per concept, each with a link to the page that holds the detail. Read it once before the tutorial; come back to it when a term in a reference page is unclear.
Tenants and organisations
A tenant is one customer of a deployment: every table carries a tenant_id, every repository method takes the tenant context first, and Postgres row-level security enforces the boundary because the API and worker connect as the non-owner aletheia_app role. Identity comes from Zitadel: one Zitadel organisation is one tenant, and the project roles admin, analyst and integration map to API permissions (cases:decide, definitions:write, runs:start and so on). Some permissions are held by admin only: subjects:pii (without it personal data in responses is masked), runs:context (without it run context and app payloads are [redacted]), audit:export, runs:write (cancel and replay runs), ops:read (trace links), webhooks:read and webhooks:write, apps:write and apps:publish (installing and uploading apps), and users:manage (team actions); notifications:read and apps:read are also an analyst's. Tenants are provisioned explicitly with pnpm tenant:add; tokens for an unknown organisation are rejected. People sign in to the admin console through Zitadel; machines use a service user's personal access token; applicants never have an account and open collection flows through signed, expiring links. Tenant-wide settings (PUT /tenants/me/settings) hold the default case SLA, business hours and what a breach does, the approval policy, the origins allowed to embed the collection flow, the tenant's collection URL and the console's defaults; an admin can suspend a tenant and revoke its collection links.
See the README's "Authentication and tenancy" and Admin console for the permission each route needs.
Subjects
A subject is the thing a decision is about: a merchant, a seller or a user, with an optional externalId (your identifier), a free-form data payload (for example country, legalName, email) and a risk score once decisions exist. Subjects are created through POST /subjects; everything else (runs, submissions, cases, decisions, events, documents) hangs off a subject, and GET /subjects/:id/timeline merges that history into one stream.
Definitions, versions, publish and approvals
Four kinds of definition describe a tenant's policy: rules, rule sets, workflows and collection flows. Each has a stable key and a version history: saving creates a new draft version, publishing makes that version the one runs use and archives the previous published one. Workflows and rule sets may only reference published rules and sets, which the publish checks enforce. A tenant can require approvals: publishing is then refused with 409 approval_required, the author requests approval for the version, and an approver publishes it (with fourEyes on, the default, the requester cannot approve their own change). Every version, diff, request and approval is audited.
See Admin console: versions and approvals.
The run context and dot paths
A workflow run is one execution of a published workflow for one subject. As steps execute they write into the run's context, a JSON object that starts with subjectId and subject (the subject's data) and gains one key per step under that step's outputKey. Rules, branch conditions and app input mappings all address the context with the same dot paths: subject.<field> is the subject's data and <outputKey>.<field> is a step's output, for example submission.country (what the applicant entered), sanctions.hit (an app's result) or rules.outcome (an earlier evaluation). Nothing is spread to the top level, so a path always says where its value came from.
See Rule reference: data paths.
Steps and signals
A workflow is an entryStepId and a list of steps, each with an id, a type, its own fields and a next. The interpreter (a Temporal workflow in @aletheia-dev/workflow-engine) walks them:
| Step | What it does |
|---|---|
wait_for_collection | Creates a submission for a collection flow and pauses until the applicant submits (default outputKey: submission). The submit hands the answers over through an outbox the worker retries, so a committed submit always reaches the run. |
call_app | Calls one action of an installed app with inputs mapped from the context; the output lands under outputKey. |
evaluate_rules | Runs an ad-hoc list of rules (ruleKeys) or a published rule set (ruleSetKey); writes results and outcome to rules. |
branch | Routes on conditions over the context (when, next) with a default. |
create_case | Opens a case for manual review and pauses until someone decides it; escalates when the SLA passes. |
emit_decision | Records the decision from the rules or the manual decision; terminal. |
Pauses end with signals: collectionSubmitted when the applicant submits, manualDecision when a reviewer decides, appCallback when an asynchronous vendor posts its webhook. The run's status tells you which it is waiting for: waiting_collection, waiting_manual, waiting_callback, then completed, failed or cancelled.
See Admin console: step types and Apps: reference.
Rules and rule sets
A rule evaluates against the run context and produces pass, fail, error or skipped, with a severity (info, warn, block), an optional weight and a type-specific config. The types are comparison, list_lookup, score_threshold, expression (CEL), velocity and app. A rule set is an ordered, versioned group of rules with one aggregation policy: max_severity, sum_weights (weights of failed rules summed, clamped and mapped through bands to approve, manual_review or reject) or first_match. Rules can be dry-run against a stored subject or inline data before they decide anything, and replayed over past evaluations with a backtest.
See Rule reference.
Decisions, cases, SLAs and the audit trail
A decision is the outcome for a subject (approve, reject or manual_review) with its source (automated from the rules, manual from a person), the reasons, and for automated ones the rule set key and version, the risk score and every rule result, so the admin console can show why. A case is the manual-review item a create_case step opens: it moves open, in_review, decided, closed, lives in queues, can be claimed or assigned and carries notes and attachments. Its SLA comes from the step or the tenant default; a breach escalates the priority and shows in the Breached SLA queue. A reviewer can request more information: the applicant gets the form again, pre-filled, the SLA pauses, and their answers repeat the run's checks before the case comes back (unanswered, it is rejected when the link expires). The audit trail is append-only: every state change by a person, a service, an applicant, the system or the workflow itself is an event with actorType, actorId, action, resourceType, resourceId and a payload, readable with GET /audit-events and exportable as CSV or JSONL with a digest.
See Admin console: cases and Audit trail.
Apps and the catalogue
An app wraps one vendor or capability in a WebAssembly module built with @aletheia-dev/app-sdk: a manifest, one or more actions with input and output schemas, a config schema, the secrets it needs and the hosts it calls. A tenant installs an app under its name with its own configuration and secrets; workflows call the install through call_app steps and rules through the app rule type. The worker calls it with timeouts, retries and an idempotency key, the app runner runs each call in a sandbox that holds that tenant's data for that call only, and asynchronous actions complete through a signed webhook. The catalogue (GET /apps) lists the platform apps every release ships and the tenant's own uploads; an app does nothing for a tenant until the tenant installs it (PUT /apps/:name/install).
See Apps.
Collection flows and documents
A collection flow is a JSON-defined form the applicant fills in: steps with typed fields (text, select, number, boolean, file and more), validation and branches between steps. A wait_for_collection step creates a submission for the flow and a signed link; the collection terminal renders it, hosted or embedded in a merchant's page. File fields hold documents, uploaded straight to object storage through presigned URLs and scanned before the submission can be submitted.
See Collection flows and embedding and Documents.
Lists and events
Lists are tenant-managed blocklists and allowlists (string, email, country, number, ip_cidr) that list_lookup rules consult; entries can carry a reason and an expiry and are imported from CSV. Events are append-only facts about a subject (payments, logins, signups) ingested in batches with an idempotency key; velocity rules aggregate them over a window.
See Rule reference: events and lists.