/v1/governance: actor mappings (link opaque actor IDs to PII, with pseudonymize and delete operations), archives (list completed audit-log archives and download archive objects from object storage), and audit logs (immutable, hash-chained history with a read-only integrity check). With AUTH_PROVIDER=plugin-auth in multi-tenant mode, tenant identity comes from the JWT; Matcher rejects startup when MULTI_TENANT_ENABLED=true and PLUGIN_AUTH_ENABLED=false. workos currently resolves verified requests to the configured default tenant, so do not use it for tenant selection.
Every governance route is scoped to the caller’s tenant. Actor-mapping reads are split into two authorization tiers: the list (which omits
displayName and email) versus the single-record de-anonymization read, so identity resolution stays separable from browse access.Actor mappings
An actor mapping links an opaque
actorId (for example user:550e8400-e29b-41d4-a716-446655440000) to human-readable PII (displayName, email). Outside local, development, and test environments, set ACTOR_PII_ENCRYPTION_KEY to a base64-encoded 32-byte key before using actor mappings. If it is unset, Matcher continues to run, but PII-bearing mapping operations (upsert, single-record read, and pseudonymization) return an encryptor-required error; the PII-free list and delete paths do not require an encryptor. Mapping PII is never stored in plaintext. List rows omit the mapping PII fields (displayName, email) by design, but they do return the actorId itself. On upsert, Matcher trims leading and trailing whitespace and rejects empty or whitespace-only IDs and IDs longer than 255 characters. It does not impose an opaque-ID format or redact the value, so an actorId that itself contains PII (such as an email address) appears in list rows as its stored value and is preserved by pseudonymization; use opaque identifiers if list access must stay PII-free. The PUT response and the single-record GET return cleartext identity. Only the single-record GET is gated behind the deanonymize permission: the PUT response is gated by write access alone and echoes the full stored record, including any stored field the caller did not submit, so treat actor-mapping write access as PII-revealing. Audit logs can retain the raw actorId, which can be an email address.
List actor mappings
Cursor-paginated rows that omitdisplayName and email. Filter by an actor-ID prefix.
actorId (prefix filter), limit (default 25, capped at 100), and cursor.
Upsert an actor mapping
Creates or updates the PII for an actor ID.PUT is idempotent — the same call creates the record on first use and updates it thereafter. At least one of displayName or email must be supplied.
Get one actor mapping (de-anonymize)
Returns the cleartext PII for a single actor ID. This is the de-anonymization primitive, so it is gated behind the narrowerdeanonymize permission rather than plain read.
Pseudonymize
Replaces the mapping’sdisplayName and email with [REDACTED] while preserving the record and its actorId link. This scrubs PII from the mapping only: immutable audit records keep the raw actorId they were written with (which can itself be an email address), so historical audit logs and archived files are not redacted. Responds 204 No Content.
Delete a mapping
Permanently removes the mapping. Responds204 No Content.
Pseudonymize keeps the record (PII scrubbed); delete removes it entirely. Choose pseudonymize when you must retain the audit linkage, delete when the record itself must not persist.
Archives
The archival worker is disabled by default (
ARCHIVAL_WORKER_ENABLED=false). When you enable it and configure archival storage, aging audit-log partitions are compressed and moved to object storage. Archive retrieval routes are registered when archival object storage is available, independently of whether the worker is enabled. The list endpoint returns completed archives; the download endpoint issues time-limited URLs for archive objects.
List archives
Offset-paginated. Filter by date range.from, to (YYYY-MM-DD or RFC 3339), limit (1–200, default 20), and offset. Only COMPLETE archives are listed — in-progress and failed archives are never surfaced.
Download an archive
Returns a presigned URL plus the checksum for integrity verification.Audit logs
Instrumented governance workflows write immutable, per-tenant audit records. Each record is linked into a tamper-evident SHA-256 hash chain (
recordHash = SHA-256(prevHash || canonical content)), so inconsistent changes are detectable. Use the verify endpoint below for the server-side integrity verdict.
List audit logs
Cursor-paginated, with rich filters.actor, action, entity_type, date_from, date_to (YYYY-MM-DD or RFC 3339), limit (1–200, default 20), and cursor.
actor filters on the record’s actorId value: the raw actor identifier captured when the record was written (an email address in this example). Audit records store that identifier as-is; it is not required to be an actor-mapping actorId.
When a diff exceeds the outbox payload cap, changes carries a truncation-marker envelope instead of the full diff, and truncated becomes true with originalSize reporting the pre-truncation byte size.
Verify the audit chain
Re-verifies that every inspected record links to the previous one and matches its stored hash. Verification walks a contiguous span from the chain start, bounded bymaxRecords, so intact speaks only for that inspected span. At the HTTP endpoint, a supplied maxRecords must be 1–10,000 (values outside that range return 422); when omitted, Matcher uses the 10,000-record default. The check is strictly read-only: it detects tampering, never mutating a record.
intact is true when the whole inspected span is unbroken; if a break is found, firstBrokenSeq reports the tenantSeq of the first failing record and verifiedCount reports how many held before it. truncated is true when the chain holds more records than the maxRecords inspection bound allowed.
Get one audit log
You can also list an entity’s history directly with
GET /v1/governance/entities/{entityType}/{entityId}/audit-logs (cursor-paginated), which is convenient when you already know the entity you are auditing.
