Identity event schema
The identity_events table records every identity-axis transition in a conversation. Tenants who want to mirror this audit data to their own systems (a SIEM, a compliance-management platform, an internal audit warehouse) can poll the platform’s identity-events list endpoint.
This page documents the wire schema. The endpoint is dual-auth (Kinde JWT for dashboard humans, API key for automation). Polling cadence is up to you; events accumulate at the rate of caller verification activity in your workspaces.
Endpoint
Section titled “Endpoint”GET /api/workspaces/{workspace_id}/identity-events?from=&to=&type=&contact=&before=&limit=Authorization: Bearer sf_live_v1_...Query parameters:
| Name | Type | Notes |
|---|---|---|
from | ISO-8601 | Inclusive lower bound on occurred_at. |
to | ISO-8601 | Inclusive upper bound on occurred_at. |
type | string | comma-separated list | Filter to one or more event types (see below). |
contact | UUID | Filter to events bound to a specific contact. |
before | ISO-8601 cursor | Pagination cursor for descending-by-occurred_at traversal. |
limit | integer (1-500) | Page size. Default 100. |
Response:
{ "events": [ { "id": "<uuid>", "tenantId": "<uuid>", "conversationId": "<uuid>", "occurredAt": "2026-05-13T10:14:32Z", "eventType": "factor_added", "factorCategory": "knowledge", "factorType": "kba_dynamic", "factorBoundToPrincipal": "<contact-uuid>", "riskSignalName": null, "riskSignalWeight": null, "scopeDimension": null, "scopeValues": null, "evidenceRef": "kbv:lookup_orders", "promptVersion": "v3", "createdAt": "2026-05-13T10:14:32Z" }, ... ]}Field reference
Section titled “Field reference”| Field | Type | Notes |
|---|---|---|
id | UUID | Stable across replays. Use this as the primary key in your mirror. |
tenantId | UUID | Your tenant. Always present. |
conversationId | UUID | The conversation the event belongs to. Join against conversations to recover workspace and channel context. |
occurredAt | ISO-8601 | When the event happened (runtime clock). |
eventType | enum | One of the values in the Event types table below. |
factorCategory | enum | null | knowledge / possession / inherence. Null for non-factor events. |
factorType | enum | null | Specific factor (e.g. kba_dynamic, magic_link). See FactorType in @standfast/api-types. |
factorBoundToPrincipal | UUID | null | Contact the factor binds to. Always present for factor_added / factor_failed. |
riskSignalName | enum | null | Specific risk signal (e.g. pad_synthetic_indicator, ani_mismatch). |
riskSignalWeight | numeric | null | Vendor-reported weight, clamped to platform floors at write time. |
scopeDimension | string | null | For scope_set / assurance_level_reached: which dimension. |
scopeValues | unknown | null | JSONB payload, typically a list of permitted values. |
evidenceRef | string | null | Audit handle pointing at the underlying mechanism (tool:lookup_orders, magic_link:<uuid>, lockout:per_call:cap=3). |
promptVersion | string | null | Version of the verification-agent system prompt at the time of the event. |
createdAt | ISO-8601 | DB write time. Almost always equal to occurredAt ± a few milliseconds. |
Event types
Section titled “Event types”eventType | Meaning | Required-fields contract |
|---|---|---|
factor_added | A factor was successfully minted. | factorCategory, factorType, factorBoundToPrincipal, evidenceRef. |
factor_failed | A factor attempt failed (wrong answer, bad signature, expired token). | Same as above. |
kbv_question_asked | The verification specialist asked a KBV question. | factorCategory='knowledge', factorBoundToPrincipal, evidenceRef='tool:<name>'. |
kbv_question_passed | Caller answered correctly. | Same shape as asked. |
kbv_question_failed | Caller’s answer didn’t match. | Same shape as asked. |
identity_locked_per_call | Per-call wrong-answer cap hit. | factorBoundToPrincipal, evidenceRef='lockout:per_call:cap=N'. |
identity_locked_per_24h | Per-identity 24h failure cap hit. | Same shape, evidence per_24h:cap=N. |
ani_blocked | A caller-ID hit the per-ANI lockout. | scopeDimension='caller_ani', scopeValues=['+15551234567']. |
assurance_level_reached | Caller’s combined factors reached a new assurance level. | factorBoundToPrincipal, scopeDimension='assurance_level', scopeValues=['identified'|'high-assurance']. |
verification_not_configured | A gate fired but the workspace had no data sources attached. | scopeDimension='workspace_id', scopeValues=['<uuid>']. |
verification_agent_context_used | The platform’s internal bypass was used to call a lookup tool during verification. | factorBoundToPrincipal, evidenceRef='tool:<name>'. |
scope_set | The caller’s permitted-account-id list was set or changed. | scopeDimension='account_id', scopeValues=[uuid, ...]. |
risk_signal_raised | An automated risk signal fired (PAD, mismatched ANI, suspicious geo). | riskSignalName, riskSignalWeight. |
risk_signal_resolved | A previously-raised signal cleared. | Same. |
Pagination
Section titled “Pagination”Events are returned newest-first by occurredAt then by id. To page back, capture the occurredAt of the oldest event in the response and pass it as before on the next request:
GET /api/workspaces/{workspace_id}/identity-events?before=2026-05-13T10:00:00Z&limit=200There is no forward pagination — you walk back in time until you stop seeing new events.
Retention
Section titled “Retention”Identity events are retained for the workspace-configured period (default 24 months, range 3-84). Once an event ages past the retention cutoff it is hard-deleted on the daily sweep. Mirror promptly; the platform does not provide a recovery path for deleted events.
Per-tenant erasure
Section titled “Per-tenant erasure”If a data subject’s contact is purged via the GDPR Art. 17 erasure endpoint, all their identity-events disappear from this list immediately. Your mirror should respect the same erasure request — receive a webhook (when the platform ships them in v1.1) or run a periodic reconciliation pass that deletes mirror rows whose factorBoundToPrincipal no longer exists on the platform.