Skip to content

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.

GET /api/workspaces/{workspace_id}/identity-events?from=&to=&type=&contact=&before=&limit=
Authorization: Bearer sf_live_v1_...

Query parameters:

NameTypeNotes
fromISO-8601Inclusive lower bound on occurred_at.
toISO-8601Inclusive upper bound on occurred_at.
typestring | comma-separated listFilter to one or more event types (see below).
contactUUIDFilter to events bound to a specific contact.
beforeISO-8601 cursorPagination cursor for descending-by-occurred_at traversal.
limitinteger (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"
},
...
]
}
FieldTypeNotes
idUUIDStable across replays. Use this as the primary key in your mirror.
tenantIdUUIDYour tenant. Always present.
conversationIdUUIDThe conversation the event belongs to. Join against conversations to recover workspace and channel context.
occurredAtISO-8601When the event happened (runtime clock).
eventTypeenumOne of the values in the Event types table below.
factorCategoryenum | nullknowledge / possession / inherence. Null for non-factor events.
factorTypeenum | nullSpecific factor (e.g. kba_dynamic, magic_link). See FactorType in @standfast/api-types.
factorBoundToPrincipalUUID | nullContact the factor binds to. Always present for factor_added / factor_failed.
riskSignalNameenum | nullSpecific risk signal (e.g. pad_synthetic_indicator, ani_mismatch).
riskSignalWeightnumeric | nullVendor-reported weight, clamped to platform floors at write time.
scopeDimensionstring | nullFor scope_set / assurance_level_reached: which dimension.
scopeValuesunknown | nullJSONB payload, typically a list of permitted values.
evidenceRefstring | nullAudit handle pointing at the underlying mechanism (tool:lookup_orders, magic_link:<uuid>, lockout:per_call:cap=3).
promptVersionstring | nullVersion of the verification-agent system prompt at the time of the event.
createdAtISO-8601DB write time. Almost always equal to occurredAt ± a few milliseconds.
eventTypeMeaningRequired-fields contract
factor_addedA factor was successfully minted.factorCategory, factorType, factorBoundToPrincipal, evidenceRef.
factor_failedA factor attempt failed (wrong answer, bad signature, expired token).Same as above.
kbv_question_askedThe verification specialist asked a KBV question.factorCategory='knowledge', factorBoundToPrincipal, evidenceRef='tool:<name>'.
kbv_question_passedCaller answered correctly.Same shape as asked.
kbv_question_failedCaller’s answer didn’t match.Same shape as asked.
identity_locked_per_callPer-call wrong-answer cap hit.factorBoundToPrincipal, evidenceRef='lockout:per_call:cap=N'.
identity_locked_per_24hPer-identity 24h failure cap hit.Same shape, evidence per_24h:cap=N.
ani_blockedA caller-ID hit the per-ANI lockout.scopeDimension='caller_ani', scopeValues=['+15551234567'].
assurance_level_reachedCaller’s combined factors reached a new assurance level.factorBoundToPrincipal, scopeDimension='assurance_level', scopeValues=['identified'|'high-assurance'].
verification_not_configuredA gate fired but the workspace had no data sources attached.scopeDimension='workspace_id', scopeValues=['<uuid>'].
verification_agent_context_usedThe platform’s internal bypass was used to call a lookup tool during verification.factorBoundToPrincipal, evidenceRef='tool:<name>'.
scope_setThe caller’s permitted-account-id list was set or changed.scopeDimension='account_id', scopeValues=[uuid, ...].
risk_signal_raisedAn automated risk signal fired (PAD, mismatched ANI, suspicious geo).riskSignalName, riskSignalWeight.
risk_signal_resolvedA previously-raised signal cleared.Same.

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=200

There is no forward pagination — you walk back in time until you stop seeing new events.

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.

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.