Skip to content

Audit events

Every workflow action emits a structured JSON audit event. You don’t add logging — it’s already there. The gateway records what happened, when, and who did it, automatically.

audit:
sink: stdout

Four sink options:

SinkBehavior
stdoutWrites one JSON line per event to stdout. Default.
fileAppends one JSON line per event to a file.
memoryStores events in memory. Useful for testing.
noneDrops all events. Disables auditing.
audit:
sink: file
path: ./audit/events.jsonl

One JSON line per event, appended to the file. The gateway creates the file if it doesn’t exist.

Set up log rotation externally (logrotate, your OS equivalent). The gateway opens the file for each write, so rotation works without restarting the process.

audit:
sink: memory

Keeps events in process memory. Useful in tests where you want to assert on what events were emitted without touching the filesystem.

Every event has the same structure:

{
"id": "evt_a1b2c3d4e5f6789012345678abcdef01",
"timestamp": "2025-06-15T14:32:08.123456Z",
"workflow_id": "wf_d4e5f6a1b2c3",
"correlation_id": "cor_f6a1b2c3d4e5789012345678abcdef01",
"actor": "agent@default",
"event_type": "workflow.started",
"payload": {}
}
FieldTypeDescription
idstringUnique event ID (evt_ prefix + UUID)
timestampstringISO 8601 timestamp (UTC)
workflow_idstring or nullThe workflow instance this event belongs to
correlation_idstringGroups related events in the same operation (cor_ prefix + UUID)
actorstring or nullWho triggered the action (principal subject)
event_typestringWhat happened (see below)
payloadobjectEvent-specific data
Event typeWhen it fires
workflow.startedA new workflow instance is created via workflow.start
workflow.transitionedA workflow successfully moves to a new state
workflow.completedA workflow reaches a terminal state
workflow.timed_outA workflow exceeded its timeoutMs and auto-transitioned
Event typeWhen it fires
transition.requestedA workflow.submit call is received
transition.rejectedA submission is rejected (guard failure, version conflict, actor mismatch, etc.)
transition.branchedA transition’s branch condition matched, changing the target state
Event typeWhen it fires
chain.stepOne step in a deterministic chain completed
chain.completedA deterministic chain finished (reached a non-deterministic state, terminal, or depth limit)
chain.failedA deterministic chain stopped because an executor failed
Event typeWhen it fires
executor.startedAn executor begins running
executor.succeededAn executor completed successfully
executor.failedAn executor failed after all retries
executor.retryingAn executor is retrying after a failure
fallback.selectedThe reliability layer is trying a fallback executor
Event typeWhen it fires
guard.evaluatedA guard was evaluated (payload includes pass/fail result)
{
"id": "evt_a1b2c3d4e5f6789012345678abcdef01",
"timestamp": "2025-06-15T14:32:08.123456Z",
"workflow_id": "wf_d4e5f6a1b2c3",
"correlation_id": "cor_f6a1b2c3d4e5789012345678abcdef01",
"actor": "agent@default",
"event_type": "workflow.started",
"payload": {
"definitionId": "deploy_pipeline",
"input": {
"service": "payments",
"environment": "staging"
}
}
}
{
"id": "evt_b2c3d4e5f6a1789012345678abcdef02",
"timestamp": "2025-06-15T14:32:09.456789Z",
"workflow_id": "wf_d4e5f6a1b2c3",
"correlation_id": "cor_a1b2c3d4e5f6789012345678abcdef02",
"actor": "agent@default",
"event_type": "transition.requested",
"payload": {
"transition": "deploy",
"fromState": "ready_to_deploy",
"expectedVersion": 4,
"arguments": { "confirm": true }
}
}
{
"id": "evt_c3d4e5f6a1b2789012345678abcdef03",
"timestamp": "2025-06-15T14:32:09.789012Z",
"workflow_id": "wf_d4e5f6a1b2c3",
"correlation_id": "cor_a1b2c3d4e5f6789012345678abcdef02",
"actor": "agent@default",
"event_type": "transition.rejected",
"payload": {
"transition": "deploy",
"code": "GUARD_REJECTED",
"message": "One or more guards rejected the transition."
}
}
{
"id": "evt_d4e5f6a1b2c3789012345678abcdef04",
"timestamp": "2025-06-15T14:32:10.123456Z",
"workflow_id": "wf_d4e5f6a1b2c3",
"correlation_id": "cor_a1b2c3d4e5f6789012345678abcdef02",
"actor": "agent@default",
"event_type": "executor.succeeded",
"payload": {
"executorKind": "cli",
"durationMs": 1234,
"transition": "deploy"
}
}
{
"id": "evt_e5f6a1b2c3d4789012345678abcdef05",
"timestamp": "2025-06-15T14:32:11.456789Z",
"workflow_id": "wf_d4e5f6a1b2c3",
"correlation_id": "cor_f6a1b2c3d4e5789012345678abcdef01",
"actor": null,
"event_type": "chain.completed",
"payload": {
"steps": 3,
"fromState": "lint",
"toState": "ready_to_deploy",
"reason": "non_deterministic_state"
}
}

Audit events are structured JSON, one line per event. They’re designed to be piped into whatever you already use:

  • Vector — use a file source pointing at your audit log, or capture stdout with a stdin source.
  • Prometheus — parse events into metrics (transitions per second, failure rate, latency histograms).
  • Elasticsearch / OpenSearch — index the JSONL directly.
  • CloudWatch / Datadog / Splunk — forward the log file or stdout stream through their agents.

The correlation_id field ties related events together across a single operation. The workflow_id groups everything for one workflow instance. Between the two, you can reconstruct the full story of any workflow run.