Audit Log

The audit log is an immutable record of every significant action in a workspace: who did what, when, from which IP, on which object, and at what risk level. Every event carries a HMAC-SHA256 MAC field that proves the record has not been tampered with. Events are retained for 7 years. The audit log cannot be cleared, truncated, or modified by any role.


When to use

Use the audit log to:

  • Investigate a failed or unexpected run
  • Verify who approved or rejected an approval step
  • Confirm when a secret was last rotated or accessed
  • Respond to a security incident by reconstructing the sequence of events
  • Satisfy compliance requirements (SOC2 B2 evidence)
  • Review GDPR erasure completeness

Key concepts

Event — a single audit record. Every event has a globally unique eventId with prefix evt_ and is time-sortable.

Risk levels — each event is assigned a risk level:

  • low — routine operational events (run started, workflow draft saved)
  • medium — configuration or data access events (agent updated, connection updated)
  • high — sensitive operations (secret accessed, authz denied, connection accessed)
  • critical — highest-impact operations (member erased, gateway policy changed)

MAC field — a HMAC-SHA256 digest computed over the canonical event body at write time. The MAC is stored alongside the event and cannot be altered retroactively. Use it to verify event integrity.

PersonID — a field on every event that links the actor to a data subject identity for GDPR purposes. On POST /workspace/members/{userId}/erase, all personId values for that user are replaced with a tombstone across the event history. The event data itself is preserved.

Audit digest — a scheduled email summarising audit events. Configurable frequency (daily, weekly, monthly), recipients, and risk level filter. Sent via SES.


Event structure

FieldDescription
eventIdevt_* prefixed, time-sortable unique ID
eventTypeDot-separated event name (e.g. run.started)
actorIdID of the user, agent, system, or integration that caused the event
actorKinduser | agent | system | integration
actorNameHuman-readable actor name
personIdGDPR-erasable link to data subject
objectTypeThe type of resource affected (e.g. workflow, secret)
objectIdID of the affected resource
objectNameName of the affected resource
risklow | medium | high | critical
runIdAssociated run ID if applicable
workflowIdAssociated workflow ID
workflowNameAssociated workflow name
environmentWorkspace environment at event time
ipActor IP address
sessionIdSession ID for user events
reasonReason for authz denial (on authz.denied events)
policyMatchedDLP policy that triggered (on policy.violation events)
authMethodAuthentication method used
connectionRefConnection involved (on connection events)
secretRefSecret involved (on secret events)
versionBeforePrevious value (on update events)
versionAfterNew value (on update events)
occurredAtISO 8601 timestamp
extraArbitrary additional context
TTLplatform database TTL — 7 years from event creation
macHMAC-SHA256 digest over canonical event body

Full event taxonomy

Runs (5 events)

run.started, run.canceled, run.completed, run.failed, run.deleted

Workflows (3 events)

workflow.edited, workflow.published, workflow.draft_saved

Secrets (4 events)

secret.created, secret.updated, secret.rotated, secret.accessed (HIGH)

Connections (2 events)

connection.updated, connection.accessed (HIGH)

Agents (5 events)

agent.updated, agent.memory_set, agent.memory_deleted, agent.listed, agent.read

Workspace (4 events)

workspace.updated, workspace.created, workspace.environment_updated, workspace.notification_updated

Members (4 events)

member.invited, member.updated, member.removed, member.erased (CRITICAL)

API keys (2 events)

api_key.created, api_key.revoked

Approvals (4 events)

approval.granted, approval.rejected, approval.reassigned, approval.sla_breach

Grants (2 events)

grant.approved, grant.denied

Auth (4 events)

user.login, user.workspace_switched, auth.failed, authz.denied (HIGH)

Bus (9 events)

bus.message_published, bus.message_delivered, bus.message_fanout, bus.message_delivery_failed, bus.cycle_blocked, bus.subscription_created, bus.subscription_updated, bus.subscription_deleted, bus.api_key_created, bus.api_key_revoked

Gateway and DLP (4 events)

gateway_policy.created, gateway_policy.updated, gateway_policy.deleted, policy.violation

Datastores (4 events)

datastore.created, datastore.updated, datastore.deleted, datastore.object_deleted

SOC2 enumeration events (4 events)

secret.listed, connection.listed, connection.read, api_key.listed


How it works

Querying audit events

# List recent events, filtered to high and critical risk
curl "https://api.provenanceone.ai/audit?risk=high,critical&limit=50" \
  -H "x-api-key: YOUR_KEY"

# Get a specific event
curl "https://api.provenanceone.ai/audit/{eventId}" \
  -H "x-api-key: YOUR_KEY"

The GET /audit endpoint accepts query parameters:

  • eventType — filter by event type (e.g. secret.accessed)
  • actorId — filter by actor
  • risk — filter by risk level (comma-separated)
  • from / to — date range (ISO 8601)
  • workflowId — filter to a specific workflow
  • runId — filter to a specific run

Investigating a failed run

  1. Navigate to Runs and open the failed run.
  2. The run detail page links to Audit → Run trail. This view shows all audit events with runId matching the run, in chronological order.
  3. Alternatively, call GET /audit?runId={runId} to retrieve the same events via API.
  4. Look for run.failed to see the failure reason, and authz.denied or policy.violation events that may have caused the failure.

Audit digest setup

  1. Go to Settings → Audit Digest.
  2. Enable the digest and set the frequency (daily, weekly, monthly).
  3. Choose dayOfWeek (weekly), dayOfMonth (monthly), and hourUTC for delivery time.
  4. Set includeRiskLevels to filter which risk levels are included in the digest.
  5. Add recipient email addresses.
  6. Click Save. The digest is sent via SES on the configured schedule.

Configuration options

Audit digest fields

FieldTypeRequiredDefaultDescription
enabledbooleanYesfalseWhether the digest is active
frequencyenumYesdaily | weekly | monthly
dayOfWeekintegerWeekly only0 (Sunday) to 6 (Saturday)
dayOfMonthintegerMonthly only1 to 28
hourUTCintegerYesHour (0–23) for delivery
includeRiskLevelsarrayYesSubset of low, medium, high, critical
recipientsarrayYesEmail addresses to receive the digest

Examples

Retrieve all secret.accessed events in the last 30 days

curl "https://api.provenanceone.ai/audit?eventType=secret.accessed&from=2026-04-01T00:00:00Z" \
  -H "x-api-key: YOUR_KEY"

Check who approved a specific approval

curl "https://api.provenanceone.ai/audit?eventType=approval.granted&runId={runId}" \
  -H "x-api-key: YOUR_KEY"

Verify event integrity using the MAC field

Each event response includes a mac field. To verify:

  1. Retrieve the event: GET /audit/{eventId}
  2. Recompute the HMAC-SHA256 over the canonical event body using the platform key management key
  3. Compare the computed value to the stored mac field

A mismatch indicates the event record has been altered.


Common mistakes

  • Filtering audit logs to low risk only. Most security-relevant events are high or critical. If your audit digest only includes low risk events, you will miss secret access, authz denials, and member erasures.
  • Not monitoring bus.cycle_blocked events. These indicate routing loops in the Bus configuration that are silently dropping messages.
  • Assuming personId erasure deletes event data. GDPR erasure replaces the personId field but does not delete the audit event. The action history is preserved for compliance; only the data subject identifier is removed.
  • Querying with too broad a date range. Seven years of events is a large dataset. Always filter by eventType, risk, or workflowId to narrow queries.

Troubleshooting

GET /audit returns 403 — the API key or JWT does not have the audit:read scope. Update the API key's scopes in Settings, or use an admin or editor JWT.

Audit digest emails are not arriving — check that the SES sender domain is verified for your workspace region and that recipients are not on the SES suppression list. Verify the digest is enabled and the hourUTC has passed.

Cannot find audit events for a specific run — ensure you are querying with the correct runId. Run IDs are case-sensitive. Also confirm the run actually started (a run that was rejected before starting will only have a run.failed event with no steps).

mac field is missing on older events — events created before MAC signing was introduced may not have a mac field. This is expected for legacy events; all new events include the MAC.


Security and permissions

Operationadmineditorviewer
Read audit eventsYesYesNo
Configure audit digestYesNoNo
Export via API (audit:read scope)YesYesNo

Audit events cannot be deleted, modified, or filtered out by any role or API key. The immutability is enforced at the storage layer. The mac field provides cryptographic proof of integrity for each event.



FAQ

How long are audit logs retained?

Audit events are retained for 7 years. Each event has a platform database TTL set 7 years from the event's `occurredAt` timestamp. Events older than 7 years are automatically expired from the primary store. There is no mechanism for early deletion; the 7-year floor exists for compliance purposes.

Can audit logs be tampered with?

No. Every audit event includes a `mac` field: a HMAC-SHA256 digest computed over the canonical event body at write time. The MAC is stored with the event and is recomputable using the platform key management key. Any alteration to the event body would produce a MAC mismatch. This provides cryptographic evidence of integrity for each event record.

What is the MAC field?

The `mac` field is a HMAC-SHA256 message authentication code computed over the canonical serialisation of the audit event body. It is stored alongside the event and serves as tamper evidence. If an audit event were altered after being written, recomputing the MAC would yield a different value than the stored `mac`. This is the primary SOC2 B2 control for audit log immutability.

How do I investigate a failed run?

Open the run in the Runs page and look at the step-level error. From there, navigate to the run's audit trail (linked from the run detail page, or via `GET /audit?runId={runId}`). Look for `run.failed` for the failure reason, `authz.denied` if an authorisation check blocked a step, and `policy.violation` if a gateway DLP rule was triggered. The audit trail shows the full sequence of events in the order they occurred.

Can I export audit logs?

Yes. Use `GET /audit` with the `audit:read` scope to retrieve events via the API. You can filter by event type, actor, risk level, and date range. Paginate through results using the response cursor. For scheduled summaries, configure the audit digest to send email reports to your compliance or security team. There is no one-click bulk export UI, but the API provides full programmatic access.