Read prompts, tool inputs, results, Rule checks and decisions with the Synkro CLI.
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.
Start with a decision
synkro events --type verdict --limit 1 --json
--json returns complete records. Use jq, a JSON command-line tool, to pick
the fields you want:
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:
{
"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
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. Astatusofpresentmeans 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:
synkro events --correlation REPLACE_WITH_CORRELATION_UUID --json
For a specific record, use --id. To find records caused by it, use
--causation:
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:
synkro events --id REPLACE_WITH_HOOK_UUID --json |
jq '.events[].envelope.payload'
Example output for a Codex command:
{
"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:
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:
{
"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:
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:
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:
synkro events --id REPLACE_WITH_EVALUATION_UUID --json |
jq '.events[].typed_payload.fields.request | fromjson'
To focus on the prompts, tool evidence and Rules:
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:
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
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:
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:
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:
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
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:
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. For all filters, run synkro events --help.