Skip to main content

Notifications

AgentXchain now ships a first-class notification contract for governed lifecycle events. This is not another hook phase and it is not a Slack-specific integration. It is a stable event boundary that webhook relays, chat bridges, ticket routers, and audit collectors can consume.

Scope​

This slice is deliberately narrow:

  • top-level governed config: notifications
  • one transport: webhook
  • one evidence file: .agentxchain/notification-audit.jsonl
  • one operator-facing blocker projection: HUMAN_TASKS.md + .agentxchain/human-escalations.jsonl
  • only shipped lifecycle events, not aspirational ones

Delivery is best-effort. Notification failures are audited, but they do not block step, resume, accept-turn, approve-completion, or any other governed command.

Config​

{
"notifications": {
"webhooks": [
{
"name": "ops_webhook",
"url": "https://ops.example.com/agentxchain/events",
"events": [
"run_blocked",
"operator_escalation_raised",
"escalation_resolved",
"phase_transition_pending",
"run_completion_pending",
"run_completed",
"approval_sla_reminder"
],
"timeout_ms": 5000,
"headers": {
"Authorization": "Bearer ${OPS_WEBHOOK_TOKEN}"
}
}
],
"approval_sla": {
"reminder_after_seconds": [3600, 14400, 86400]
}
}
}

Event Types​

The shipped event set is intentionally small and code-backed:

  • run_blocked
  • operator_escalation_raised
  • escalation_resolved
  • human_escalation_raised — emitted when a human-owned blocker is promoted to a first-class escalation record
  • human_escalation_resolved — emitted when a human escalation is resolved via agentxchain unblock
  • phase_transition_pending
  • run_completion_pending
  • run_completed
  • approval_sla_reminder

run_failed is not documented here because the current governed runner does not expose a first-class persistent run-failed operator surface yet. Do not build consumers against undocumented event names.

Local Notifier Floor​

Human escalation events always emit a structured stderr notice regardless of webhook configuration. This ensures operators see escalation signals even with zero webhooks configured:

[agentxchain] ⚠ HUMAN ESCALATION RAISED: hesc_abc12345
Type: needs_oauth
Action: Reconnect Linear OAuth and verify the session.
Unblock: agentxchain unblock hesc_abc12345

On macOS, set AGENTXCHAIN_LOCAL_NOTIFY=1 to also receive native desktop notifications via AppleScript when escalations are raised or resolved.

Approval SLA Reminders​

When a run enters pending_phase_transition or pending_run_completion, the initial notification fires once. If the operator does not act, the approval sits silently by default. Approval SLA reminders add timed follow-up webhook notifications at configurable intervals.

Per DEC-APPROVAL-TIMEOUT-EXEMPT-001, approval-pending states are exempt from timeout enforcement. SLA reminders are a notification feature, not a timeout feature — they never block or mutate governed state.

Config​

{
"notifications": {
"approval_sla": {
"reminder_after_seconds": [3600, 14400, 86400],
"enabled": true
}
}
}
  • reminder_after_seconds: ascending array of positive integers (seconds after the approval was requested). Minimum value: 300 (5 minutes). Maximum 10 entries.
  • enabled: optional boolean, defaults to true. Set to false to disable without removing config.
  • At least one webhook must subscribe to approval_sla_reminder for reminders to fire.

Evaluation​

Reminders are lazily evaluated at operator interaction time:

  • agentxchain status evaluates and fires due reminders as a side effect.
  • The dashboard browser shell calls GET /api/poll immediately on connect and every 60 seconds while the tab is visible. That heartbeat evaluates due reminders exactly once per poll tick.
  • agentxchain step evaluates before it exits on an already-pending approval.
  • agentxchain run evaluates when the run loop enters a pending gate before the operator approves or holds it.

Each threshold fires once per pending approval. If a 1-hour threshold fires, it will not fire again until the approval is resolved and a new approval starts.

Payload​

{
"event_type": "approval_sla_reminder",
"payload": {
"approval_type": "pending_phase_transition",
"requested_at": "2026-04-16T14:00:00.000Z",
"elapsed_seconds": 14400,
"threshold_seconds": 14400,
"reminder_index": 2,
"total_thresholds": 3,
"from_phase": "implementation",
"to_phase": "qa",
"gate": "require_approval"
}
}

Tracking​

Reminder dedup state is tracked in .agentxchain/sla-reminders.json. This file is cleared per approval type when the approval is resolved (approve-transition / approve-completion).

Payload​

Each webhook receives one JSON object:

{
"schema_version": "0.1",
"event_id": "notif_abc123",
"event_type": "run_blocked",
"emitted_at": "2026-04-04T02:00:00.000Z",
"project": {
"id": "agentxchain-dev",
"name": "AgentXchain.dev",
"root": "/abs/path/to/repo"
},
"run": {
"run_id": "run_123",
"status": "blocked",
"phase": "implementation"
},
"turn": {
"turn_id": "turn_456",
"role_id": "dev",
"attempt": 2,
"assigned_sequence": 7
},
"payload": {
"category": "dispatch_error",
"blocked_on": "dispatch:api_proxy_failure",
"typed_reason": "dispatch_error",
"recovery_action": "Resolve the dispatch issue, then run agentxchain step --resume",
"human_escalation": {
"escalation_id": "hesc_abc123",
"type": "needs_credential",
"service": "Anthropic",
"action": "Restore the required Anthropic credential and verify access.",
"resolution_command": "agentxchain unblock hesc_abc123"
}
}
}

Top-level fields:

  • schema_version
  • event_id
  • event_type
  • emitted_at
  • project
  • run
  • turn
  • payload

turn is null when the event is run-scoped and no retained or targeted turn exists.

When a run_blocked event is human-owned, the payload also includes a human_escalation object. The same blocker is recorded durably in .agentxchain/human-escalations.jsonl and mirrored into HUMAN_TASKS.md.

Audit Evidence​

Every delivery attempt appends one JSON line to .agentxchain/notification-audit.jsonl.

Recorded fields include:

  • event_id
  • event_type
  • notification_name
  • transport
  • delivered
  • status_code
  • timed_out
  • duration_ms
  • message

This audit file is included in agentxchain export and counted in export summary as notification_audit_entries. agentxchain verify export validates that count against the exported .agentxchain/notification-audit.jsonl content.

The live local dashboard also exposes this audit through GET /api/notifications. The Notifications view summarizes configured webhook targets, approval SLA reminder config, aggregate failure/timeout counts, and the newest delivery attempts so operators do not need to tail the JSONL file by hand.

That endpoint is live-only. agentxchain replay export <export.json> does not expose historical notification telemetry through /api/notifications; replay mode returns replay_mode: true with a live-only message instead of stale audit rows. The exported artifact still contains .agentxchain/notification-audit.jsonl for reportability and verification, but the dashboard notification surface is intentionally scoped to the live local workspace.

Relationship To Hooks​

Hooks remain the extensibility surface for policy and custom side effects. Notifications are different:

  • hooks are tied to internal lifecycle phases such as before_validation and after_acceptance
  • notifications are tied to durable governed events such as run_blocked and run_completed
  • hooks can be blocking or advisory depending on phase
  • notifications are always advisory and best-effort

If you want Slack, email, or ticketing delivery, build that on top of this webhook contract instead of hard-coding transport-specific assumptions into governed state transitions.