Notifications
cliq can notify external systems when pipeline events occur. Notifications are event-driven and fire-and-forget — failures are logged to orchestrator.log but never block the pipeline. Use them to keep your team informed and to keep Jira tickets in sync with pipeline status.
Two channels are supported out of the box: Slack (via Incoming Webhooks) and Jira (comments and ticket transitions). An automatic A2A event bridge also streams events to calling agents when pipelines are initiated via the A2A protocol.
Events
Section titled “Events”cliq fires eight notification events across the pipeline lifecycle:
| Event | When it fires | Context available |
|---|---|---|
on_start | Pipeline begins execution | instance_id, req_key |
on_phase_start | A phase begins execution | instance_id, req_key, phase, phase_type |
on_phase_complete | A phase finishes | instance_id, req_key, phase, phase_type, outcome, iteration |
on_complete | All phases finish successfully | instance_id, req_key |
on_escalate | Pipeline halts — human intervention required | instance_id, req_key, phase, reason |
on_cancel | Pipeline cancelled by user (Ctrl-C, SIGTERM, SIGHUP) | instance_id, req_key, reason |
on_pull | External content is pulled into the workspace (before the phase activates) | instance_id, req_key, pull_name, pull_url, pull_source_type, pull_size_bytes, pull_file_count, status |
on_push | A file is pushed to an external destination (after phase completion) | instance_id, req_key, phase, push_file, push_destination, push_mode, push_external_url, status, error_message |
Event context
Section titled “Event context”Every notification handler receives a NotificationContext object:
{ "instance_id": "cliq-a1b2c3", "req_key": "PROJ-123", "phase": "reviewer", "phase_type": "gate", "outcome": "done", "iteration": 2, "reason": "Gate 'reviewer' exhausted its iteration budget (3 iterations)", "project_dir": "/path/to/project"}Not all fields are present for every event. phase, phase_type, outcome, and iteration are populated for phase-level events. reason is populated for on_escalate and on_cancel.
Channels
Section titled “Channels”| Channel | What it does |
|---|---|
slack | Posts a formatted message via Incoming Webhook |
jira | Adds a comment to the originating Jira ticket. On escalation, transitions the ticket to a configurable status |
Configuration
Section titled “Configuration”Configure notifications in settings.json (global or project). Each event lists which channels to notify. Set up Slack and Jira credentials first — see Integrations.
{ "notifications": { "on_start": { "channels": ["slack"] }, "on_phase_start": { "channels": ["slack"] }, "on_phase_complete": { "channels": ["slack", "jira"] }, "on_complete": { "channels": ["slack", "jira"] }, "on_escalate": { "channels": ["slack", "jira"] }, "on_cancel": { "channels": ["slack"] } }}Leave channels as an empty array [] — or omit the event entirely — to disable notifications for that event.
Per-event overrides
Section titled “Per-event overrides”You can override channel-specific settings for individual events. This is useful for routing escalations to a different Slack channel or changing the Jira transition:
{ "notifications": { "on_start": { "channels": ["slack"] }, "on_complete": { "channels": ["slack", "jira"] }, "on_escalate": { "channels": ["slack", "jira"], "slack": { "webhook_url": "https://hooks.slack.com/services/DIFFERENT/HOOK/for-incidents" }, "jira": { "transition_to": "Blocked" } } }}The slack.webhook_url override only applies to on_escalate in this example. All other events use the default webhook from integrations.slack.
Per-event override reference
Section titled “Per-event override reference”| Override | Type | Description |
|---|---|---|
slack.webhook_url | string | Send this event to a different Slack webhook |
jira.transition_to | string | Jira transition name to apply for this event (only used by on_escalate) |
Retry behavior
Section titled “Retry behavior”Webhook calls (both Slack and Jira) retry up to 3 times with exponential backoff (1 s, 3 s delays) on transient failures:
- HTTP 429 (Too Many Requests)
- HTTP 5xx (Server errors)
- Network errors (ECONNRESET, ECONNREFUSED, etc.)
Permanent failures (4xx other than 429) are not retried.
A2A event bridge
Section titled “A2A event bridge”When a pipeline is initiated via the A2A protocol, notification events are automatically written to .cliq/signals/_a2a_events as JSON Lines. The server process polls this file and relays events to the calling agent over the A2A stream as TaskStatusUpdateEvent messages with structured DataPart payloads.
This is not a user-configurable channel — it activates automatically for A2A-initiated pipelines whenever the .cliq/signals/_a2a_task marker file exists. No changes to notifications settings are needed.
Each line in the events file is a JSON object:
{ "event": "on_phase_complete", "instance_id": "cliq-a1b2c3", "req_key": "PROJ-123", "phase": "developer", "phase_type": "standard", "outcome": "done", "iteration": 1, "ts": "2026-03-26T14:30:00.000Z"}All six events (on_start, on_phase_start, on_phase_complete, on_complete, on_escalate, on_cancel) are written to the bridge regardless of which channels are configured in notifications. See A2A — Real-time Visibility for details on what the calling agent receives.
Set up the Slack webhook in Integrations — Slack. Once configured, add "slack" to the channels array for each event you want.
Slack message examples
Section titled “Slack message examples”on_start:
:rocket: cliq pipeline started (cliq-a1b2c3) Req: PROJ-123
on_phase_start:
:arrow_forward: Phase started: developer (standard) (cliq-a1b2c3) Req: PROJ-123
on_phase_complete:
:checkered_flag: Phase completed: developer — done (cliq-a1b2c3) Req: PROJ-123
on_complete:
:white_check_mark: cliq pipeline completed (cliq-a1b2c3) Req: PROJ-123
on_escalate:
:rotating_light: cliq escalation (cliq-a1b2c3)
Pipeline halted — human intervention required
Req: PROJ-123 Phase: reviewer Reason: Gate ‘reviewer’ exhausted its iteration budget (3 iterations)…
Run
cliq runafter fixing the issue.
on_cancel:
:stop_sign: cliq pipeline cancelled (cliq-a1b2c3) Req: PROJ-123 Reason: Pipeline cancelled by user (SIGINT)
on_pull (success):
:inbox_tray: Pull completed: research-docs from url (4096 bytes, 3 files) — (cliq-a1b2c3) Req: PROJ-123
on_pull (failure):
:x: Pull failed: research-docs from url (cliq-a1b2c3) Req: PROJ-123 Error: HTTP 403 Forbidden
on_push (success):
:outbox_tray: Push completed: report.md → gdoc://abc123 (mode: replace) — (cliq-a1b2c3) Req: PROJ-123 URL: https://docs.google.com/document/d/abc123
on_push (failure):
:x: Push failed: report.md → gdoc://abc123 (cliq-a1b2c3) Req: PROJ-123 Error: Content scanner blocked: API key detected
Set up Jira credentials in Integrations — Jira. Once configured, add "jira" to the channels array. Jira notifications only fire when the req source is a Jira ticket (e.g., cliq req -s jira:PROJ-123). Note: on_start is a no-op for Jira — the handler skips it since there’s nothing useful to post before work begins.
Jira behavior per event
Section titled “Jira behavior per event”on_start — Skipped (no-op).
on_phase_start — Adds a comment:
cliq phase started: developer (standard) (cliq-a1b2c3).
on_phase_complete — Adds a comment:
cliq phase completed: developer — done (cliq-a1b2c3).
on_complete — Adds a comment:
cliq pipeline completed (cliq-a1b2c3).
All phases finished successfully.
on_escalate — Adds a comment with the failure reason, then transitions the ticket:
cliq escalation (cliq-a1b2c3)
Pipeline halted at phase ‘reviewer’. Human intervention required.
Reason: Gate ‘reviewer’ exhausted its iteration budget (3 iterations)…
Then transitions the ticket to the configured status (default: "Blocked").
on_cancel — Adds a comment:
cliq pipeline cancelled (cliq-a1b2c3).
Reason: Pipeline cancelled by user (SIGINT)
(Optional) Change the escalation transition
Section titled “(Optional) Change the escalation transition”By default, escalation moves the ticket to “Blocked”. To change it:
{ "on_escalate": { "channels": ["jira"], "jira": { "transition_to": "Needs Review" } }}The value must match an available transition name on your Jira board. If the transition doesn’t exist, the comment is still posted and a warning is logged listing the available transitions.
Configuration examples
Section titled “Configuration examples”Slack-only — all events
Section titled “Slack-only — all events”{ "integrations": { "slack": { "webhook_url": "https://hooks.slack.com/services/T.../B.../..." } }, "notifications": { "on_start": { "channels": ["slack"] }, "on_phase_start": { "channels": ["slack"] }, "on_phase_complete": { "channels": ["slack"] }, "on_complete": { "channels": ["slack"] }, "on_escalate": { "channels": ["slack"] }, "on_cancel": { "channels": ["slack"] }, "on_pull": { "channels": ["slack"] }, "on_push": { "channels": ["slack"] } }}Jira-only — completion and escalation
Section titled “Jira-only — completion and escalation”{ "integrations": { "jira": { "base_url": "https://your-org.atlassian.net", "api_token": "your-api-token" } }, "notifications": { "on_complete": { "channels": ["jira"] }, "on_escalate": { "channels": ["jira"] } }}Combined — Slack for everything, Jira for key events
Section titled “Combined — Slack for everything, Jira for key events”{ "integrations": { "slack": { "webhook_url": "https://hooks.slack.com/services/T.../B.../..." }, "jira": { "base_url": "https://your-org.atlassian.net", "api_token": "your-api-token" } }, "notifications": { "on_start": { "channels": ["slack"] }, "on_phase_start": { "channels": ["slack"] }, "on_phase_complete": { "channels": ["slack", "jira"] }, "on_complete": { "channels": ["slack", "jira"] }, "on_escalate": { "channels": ["slack", "jira"], "slack": { "webhook_url": "https://hooks.slack.com/services/INCIDENT/HOOK" }, "jira": { "transition_to": "Blocked" } }, "on_cancel": { "channels": ["slack"] } }}Per-phase notifications — track phase progress
Section titled “Per-phase notifications — track phase progress”If your workflow has many phases and you want granular Slack updates as each phase starts and finishes:
{ "notifications": { "on_start": { "channels": ["slack"] }, "on_phase_start": { "channels": ["slack"] }, "on_phase_complete": { "channels": ["slack"] }, "on_complete": { "channels": ["slack", "jira"] }, "on_escalate": { "channels": ["slack", "jira"] }, "on_cancel": { "channels": ["slack"] } }}This gives you a real-time Slack thread showing each phase entering and leaving execution — helpful for longer pipelines where you want to see progress without opening the dashboard.
Verification
Section titled “Verification”Test your notification integrations before running a pipeline:
cliq doctor test slack # sends a test message to your webhookcliq doctor test jira # verifies Jira authenticationcliq doctor test jira --ticket PROJ-123 # also verifies access to a specific ticketTroubleshooting
Section titled “Troubleshooting”| Issue | Fix |
|---|---|
| ”Notification (slack): failed — HTTP 403” | The webhook URL is invalid or the Slack app was deleted. Regenerate the webhook in Slack. Run cliq doctor test slack to verify. |
| ”Notification (jira): failed — HTTP 401” | Jira credentials are wrong. Verify base_url, email, and api_token in integrations.jira. Run cliq doctor test jira to verify. |
| ”Notification (jira): failed to fetch transitions” | The Jira credentials are wrong, or the ticket doesn’t exist. Verify your credentials and run cliq doctor test jira to confirm. |
| ”Notification (jira): transition ‘Blocked’ not available” | The “Blocked” status doesn’t exist on your Jira board. Check which transitions are available and set transition_to accordingly. The log message lists the available transitions. |
| No notifications firing at all | Check that channels arrays are not empty and that integration credentials are set. Notifications read from .cliq/resolved_settings.json — if cliq run hasn’t been executed yet, this file won’t exist. Run cliq doctor test all to verify all integrations. |
| Phase events not appearing | Make sure on_phase_start and/or on_phase_complete are configured in notifications. These events are opt-in — they don’t fire unless you add channels for them. |
Jira comments not appearing for on_start | This is expected. The Jira handler skips on_start since there’s nothing to report before work begins. Use Slack for start notifications. |