# Events

> Read prompts, tool inputs, results, Rule checks and decisions with the Synkro CLI.

Source: https://docs.synkro.sh/guides/events

Use `synkro events` to inspect what someone asked their agent to do, what the
agent tried, which Rules matched, and what happened next.

Run it from your signed-in CLI to see events across your organization.
An approved, human-claimed agent can use the same command to read its own
installation's hooks, requests, decisions, evaluations, Rule matches and errors.
It cannot read another installation by supplying its event or trace ID.
For setup through `synkro onboard`, use `synkro onboard verify` to check the
first Rule and action. See the [Quickstart](/quickstart).

## Start with a decision

```sh
synkro events --type verdict --limit 1 --json
```

`--json` returns complete records. Use `jq`, a JSON command-line tool, to pick
the fields you want:

```sh
synkro events --type verdict --limit 1 --json | jq '.events[] | {
  id: .envelope.id,
  recorded_at: .envelope.recorded_at,
  correlation_id: .envelope.correlation_id,
  action: .envelope.payload.action,
  reason: .envelope.payload.reason,
  decision_latency_ms: .envelope.payload.decision_latency_ms,
  evaluation_status: .envelope.payload.evaluation_status,
  agent_event_id: .envelope.payload.agent_event_id,
  evaluation_event_id: .envelope.payload.evaluation_event_id
}'
```

Example output:

```json
{
  "id": "019fe000-0000-7000-8000-000000000005",
  "recorded_at": "2026-10-07T18:30:00Z",
  "correlation_id": "019fe000-0000-7000-8000-000000000001",
  "action": "block",
  "reason": "Matched semantic rules: project/protect-reports",
  "decision_latency_ms": 866,
  "evaluation_status": "violation",
  "agent_event_id": "019fe000-0000-7000-8000-000000000003",
  "evaluation_event_id": "019fe000-0000-7000-8000-000000000004"
}
```

The action was blocked by `project/protect-reports`. The IDs lead to the
attempted action and its semantic check.

## Common commands

```sh
synkro events --limit 10
synkro events --type verdict --limit 5 --details
synkro events --type consent --limit 10 --json
synkro events --list-types
```

`--details` prints each complete event. `--json` returns a page containing
`events`, `next_cursor`, `as_of` and the applied `filters`. Each event has:

- `envelope`: its ID, type, timestamps, source, related IDs and original payload.
- `typed_payload`: named fields for that event type, such as the tool name,
  session, verdict or Rule match. A `status` of `present` means those fields are
  available.

You do not need `--details` with `--json` to get the full data.

| Event type | What you can inspect |
| --- | --- |
| `harness.hook_received` | The original agent hook, including prompts, tool arguments, edits or responses supplied in that hook. |
| `agent.tool_requested` | Tool name, session, working directory, model, file paths, connected tool and action summary. |
| `agent.tool_returned` | Tool result, outcome and the ID of the corresponding request. |
| `evaluation.created` | Semantic-check input, Rules considered, matches, status and timing. |
| `verdict.created` | Final action, reason, correction guidance, check status and the policy version used. |
| `rule.matched` | Matched Rule ID, content digest, outcome, severity and any file or edit location. |
| `consent.*` | Approval requests, grants, uses, cancellations and expiry. |
| `error.occurred` | Recorded failures related to an action. |

`--tz` changes how text timestamps are displayed and how offset-free filters are read. It does not rewrite stored timestamps. JSON and the JSON payload printed by `--details` retain their recorded timestamp values.

## Follow an action from input to decision

Copy `correlation_id` from a verdict to read the records for that check:

```sh
synkro events --correlation REPLACE_WITH_CORRELATION_UUID --json
```

For a specific record, use `--id`. To find records caused by it, use
`--causation`:

```sh
synkro events --id REPLACE_WITH_EVENT_UUID --details
synkro events --causation REPLACE_WITH_EVENT_UUID --details
```

Follow these fields to move between records:

| Starting record | Field | Leads to |
| --- | --- | --- |
| Verdict | `payload.agent_event_id` | The attempted tool action. |
| Tool request | `causation_id` | The original hook with exact tool arguments. |
| Verdict | `payload.evaluation_event_id` | The semantic check, when one was recorded. |
| Verdict ID | `--causation` | Rule matches and approval requests caused by that verdict. |
| Tool return | `payload.requested_event_id` | The request that produced the result. |

These paths are inside `envelope`. A prompt or tool return can have a different
correlation ID from the action's check. Use the session and request links below
to find those records.

## Read the exact tool input

Read the verdict's `agent_event_id`, then copy that tool request's
`envelope.causation_id`. This is the hook ID:

```sh
synkro events --id REPLACE_WITH_HOOK_UUID --json |
  jq '.events[].envelope.payload'
```

Example output for a Codex command:

```json
{
  "cwd": "/work/acme",
  "model": "gpt-5",
  "turn_id": "turn-1",
  "tool_name": "Bash",
  "session_id": "report-cleanup",
  "tool_input": {
    "command": "rm reports/quarterly-report.csv"
  },
  "tool_use_id": "call-1",
  "hook_event_name": "PreToolUse",
  "permission_mode": "default",
  "transcript_path": null
}
```

For an edit, inspect the supplied path and patch or replacement text. For a
connected tool, inspect its supplied arguments. The raw payload keeps the
agent's field names; `tool_call_summary` is a summary, not a replacement
for this input.

## Read the user's prompts

Prompt text is in the original `UserPromptSubmit` hook. This shows prompt hooks
from the latest page of received hooks:

```sh
synkro events --type harness.hook_received --limit 50 --json | jq '
  .events[]
  | select(.typed_payload.fields.hook_event == "UserPromptSubmit")
  | {
      id: .envelope.id,
      session: .typed_payload.fields.provider_session_id,
      prompt: .envelope.payload.prompt
    }'
```

Example output:

```json
{
  "id": "019fe000-0000-7000-8000-000000000000",
  "session": "report-cleanup",
  "prompt": "Clean up temporary report files. Keep quarterly-report.csv."
}
```

To read all received hooks for one agent session on a page, copy
`provider_session_id` and `harness` from its hook's `typed_payload.fields`:

```sh
synkro events --type harness.hook_received --limit 50 --json |
  jq --arg session 'REPLACE_WITH_PROVIDER_SESSION_ID' --arg harness 'codex' '
    .events[]
    | select(.typed_payload.fields.provider_session_id == $session
        and .typed_payload.fields.harness == $harness)
    | {recorded_at: .envelope.recorded_at, hook: .envelope.payload}'
```

Use your agent's harness value. The results contain the prompts and tool
calls it supplied through hooks.

## Inspect a semantic check

Copy `evaluation_event_id` from the verdict:

```sh
synkro events --id REPLACE_WITH_EVALUATION_UUID --details
```

The record includes `candidate_rule_ids`, `violated_rule_ids`, `status`,
`error_code`, `latency_ms`, `timeout_ms` and `response_reason`.

Its `request` field is JSON stored as a string. Decode it to see the action
evidence, session context, recent prompts and Rule text and examples used for
that check:

```sh
synkro events --id REPLACE_WITH_EVALUATION_UUID --json |
  jq '.events[].typed_payload.fields.request | fromjson'
```

To focus on the prompts, tool evidence and Rules:

```sh
synkro events --id REPLACE_WITH_EVALUATION_UUID --json | jq '
  .events[].typed_payload.fields.request | fromjson
  | {recent_prompts, evidence, rules, omitted_rules, truncated_fields}'
```

Each entry in `rules` contains its ID, title, natural-language description and
the examples included in the check. `recent_prompts` shows the prompts actually
included. `omitted_rules` and `truncated_fields` report omissions or shortening
in that request.

Compare those inputs with the recorded result:

```sh
synkro events --id REPLACE_WITH_EVALUATION_UUID --json | jq '
  .events[].typed_payload.fields
  | {status, candidate_rule_ids, violated_rule_ids, response_reason,
     latency_ms, timeout_ms, error_code}'
```

`response_reason` is `null` when no explanation was recorded. This event exposes
the semantic input and interpreted result, not the raw model-service HTTP
response. The verdict records Synkro's final action.

## Inspect matched Rules and approval

```sh
synkro events --correlation REPLACE_WITH_CORRELATION_UUID --type rule.matched --details
synkro events --correlation REPLACE_WITH_CORRELATION_UUID --type consent --details
```

Rule matches include `rule_id`, `rule_digest`, `action`, `kind`, `severity` and,
when applicable, `path`, `hunk_ordinal`, `content_coverage` and `match_count`.
The verdict's `ruleset_digest` identifies the policy version used for that
decision.

To follow an approval beyond the original check, copy its `consent_id` and
filter consent events by that ID:

```sh
synkro events --type consent --limit 50 --json |
  jq --arg consent 'REPLACE_WITH_CONSENT_UUID' '
    .events[] | select(.envelope.payload.consent_id == $consent)'
```

## Read the tool's result

Copy the request ID from the verdict's `agent_event_id`:

```sh
synkro events --type agent.tool_returned --limit 50 --json |
  jq --arg request 'REPLACE_WITH_AGENT_EVENT_UUID' '
    .events[] | select(.envelope.payload.requested_event_id == $request)
    | .envelope.payload'
```

`outcome` records the return status; `tool_result` contains the interpreted
result. For the original response, use the return's `envelope.correlation_id`
to find its hook:

```sh
synkro events --correlation REPLACE_WITH_RETURN_CORRELATION_UUID --type harness.hook_received --json |
  jq '.events[].envelope.payload'
```

Use the tool return to check execution status. If no return was recorded, the
execution status is unknown.

## Filter by time and type

```sh
synkro events --type verdict --from 2026-09-22 --to 2026-09-22 --tz UTC
synkro events --type '*.created' --exclude billing --limit 20
synkro events --from 2026-09-24T00:58:00 --tz America/Chicago --details
```

Change the dates for your test. A date-only `--to` includes that whole day. A time-specific `--to` excludes that exact ending time.

A timestamp without an offset uses `--tz`, or your local timezone by default.
Use an explicit offset, such as `2026-09-24T00:58:00-05:00`, when the local time
is ambiguous around a daylight saving change.

The default is the latest 50 records in local time. `--time-field occurred` filters by event time; results still sort by recorded time, newest first.

The `jq` examples filter one returned page, not your entire history. Check the
unfiltered page's `next_cursor` if the record you want is older:

```sh
synkro events --type harness.hook_received --limit 50 --json
synkro events --cursor REPLACE_WITH_NEXT_CURSOR --json
```

Repeat with each returned cursor until `next_cursor` is `null`. Do not combine
`--cursor` with new filters. Apply the same `jq` expression to each page as needed.

## Read the check status

A `verdict.created` record contains the decision. Its reason and check status
distinguish a completed Rule check from paused enforcement or an error.

If a hook is rejected before evaluation, look for `error.occurred` and the
failed `cli.hook_transport` stage. The raw hook remains available; this rejected
check has no verdict or Rule match. Use the trace ID in the warning with
`synkro trace REPLACE_WITH_TRACE_ID`.

A `fix` Rule records `fix` in `rule.matched` and `block` in `verdict.created`.
The Rule supplies correction guidance; the verdict records that the original
action was refused. Read both records when distinguishing `fix` from `block`.

For setup, see [Verify a decision](/onboarding/verify-events). For all filters, run `synkro events --help`.
