Hooks and Webhooks
Rocky fires a lifecycle event at each notable point in a run. Attach a shell script or an HTTP webhook to any event. Use it to send a notification, write an audit record, or drive your own integration.
How a hook is dispatched
Section titled “How a hook is dispatched”Every event follows the same path, whichever kind of hook you attach to it.
a lifecycle event fires │ │ Rocky builds the event context as JSON: │ { event, run_id, pipeline, timestamp, duration_ms, │ metadata } │ ┌─────┴──────────────────────┐ ▼ ▼ ┌───────────────────┐ ┌───────────────────────┐ │ shell hook │ │ webhook │ │ [[hook.on_*]] │ │ [hook.webhooks.on_*] │ ├───────────────────┤ ├───────────────────────┤ │ sh -c "<command>" │ │ HTTP POST to `url` │ │ context on STDIN │ │ body from a preset or │ │ │ │ your body_template │ └─────────┬─────────┘ └───────────┬───────────┘ │ the hook fails │ `secret` is set ▼ ▼ on_failure decides: Rocky signs the body: warn → log, continue X-Rocky-Signature: ignore → continue quietly sha256=<hex digest> abort → `rocky hooks test` onlyLifecycle events
Section titled “Lifecycle events”Rocky fires 18 events, grouped into six phases:
Pipeline phase
Section titled “Pipeline phase”| Event | When | Use case |
|---|---|---|
pipeline_start |
Pipeline begins | Slack notification |
discover_complete |
Source discovery finishes | Log connector/table counts |
compile_complete |
Compilation finishes | Validate types before execution |
pipeline_complete |
Pipeline succeeds | Success notification, metrics push |
pipeline_error |
Pipeline fails | PagerDuty alert, incident creation |
Table phase
Section titled “Table phase”| Event | When | Use case |
|---|---|---|
before_materialize |
Before table copy | Audit logging |
after_materialize |
After table copy | Publish to data catalog |
materialize_error |
Table copy fails | Per-table alerting |
Model phase
Section titled “Model phase”| Event | When | Use case |
|---|---|---|
before_model_run |
Before compiled model runs | Feature flag checks |
after_model_run |
After compiled model runs | Lineage metadata push |
model_error |
Model execution fails | Debug notification |
Quality phase
Section titled “Quality phase”| Event | When | Use case |
|---|---|---|
before_checks |
Quality checks begin | Mute downstream alerts during expected check windows |
check_result |
A quality check completes | Per-check threshold alerting |
after_checks |
All checks finish | Aggregate summary to dashboards, pass-rate summary |
drift_detected |
Schema drift found | Schema change notification |
anomaly_detected |
Row count anomaly | Data quality alert |
State phase
Section titled “State phase”| Event | When | Use case |
|---|---|---|
state_synced |
State store synced | Backup confirmation |
Budget phase
Section titled “Budget phase”| Event | When | Use case |
|---|---|---|
budget_breach |
Observed run cost or duration exceeds a limit declared in [budget] |
Page oncall on overspend |
Which events fire for which pipeline type
Section titled “Which events fire for which pipeline type”pipeline_start, pipeline_complete and pipeline_error fire when rocky run executes a whole pipeline of any type: replication, transformation, quality, snapshot or load. They do not fire for rocky run --model X, for rocky run --select with several models, or for a transformation node run under --dag. Under rocky run --dag, quality, snapshot and load nodes each fire these hooks per node, and transformation nodes fire none. A run that is cancelled (Ctrl-C or SIGTERM) or that panics can fire pipeline_start with no end event. The other events depend on what the pipeline does.
| Event | Replication | Transformation | Quality, snapshot, load |
|---|---|---|---|
pipeline_start, pipeline_complete, pipeline_error |
Yes | Yes | Yes |
discover_complete, table phase, drift_detected, check events |
Yes | No | No |
compile_complete, model phase |
No | Yes | No |
pipeline_complete carries duration_ms for every pipeline type. metadata.table_count is set for a replication run only. For a transformation run, read the model count from metadata.model_count on compile_complete.
Shell hooks
Section titled “Shell hooks”A shell hook runs a command and pipes the event context to its stdin as JSON:
[[hook.on_pipeline_complete]]command = "bash scripts/slack-notify.sh"timeout_ms = 5000on_failure = "warn"The script receives JSON like:
{ "event": "pipeline_complete", "run_id": "run-20260402-143000-000", "pipeline": "raw", "timestamp": "2026-04-02T14:30:00Z", "duration_ms": 45200, "metadata": { "table_count": 20 }}Failure handling
Section titled “Failure handling”| Mode | Behavior |
|---|---|
abort |
Mark the hook result as abort |
warn |
Log a warning and continue (default) |
ignore |
Silently continue |
Today rocky run ignores every hook outcome. A failing hook is logged and the run continues. Only rocky hooks test acts on abort.
A hook cannot gate a run today. Do not use a hook for a deploy freeze or an approval gate. Use hooks for notifications and audit records.
Security: trust the command source
Section titled “Security: trust the command source”Rocky passes the command string to sh -c verbatim. It delivers the event context to your script as JSON on stdin, never interpolated into the command line. The runtime values Rocky exposes, such as run_id and event, therefore cannot inject shell metacharacters into your command.
Even so, never build a hook command by string-formatting untrusted input. That covers a webhook payload, a Fivetran response, a value pulled from a row, and anything else you do not fully control. Use a static command, or template it from values you control yourself.
To react to dynamic input, pass it through the JSON context to a script you wrote. Let that script decide how to quote and validate it.
Webhooks
Section titled “Webhooks”A webhook sends an HTTP request instead of running a shell command:
[hook.webhooks.on_pipeline_error]url = "https://hooks.slack.com/services/T.../B.../xxx"preset = "slack"secret = "${WEBHOOK_SECRET}"Built-in presets
Section titled “Built-in presets”| Preset | Service | Body format |
|---|---|---|
slack |
Slack Incoming Webhook | Slack Block Kit JSON |
pagerduty |
PagerDuty Events API v2 | PD event payload |
datadog |
Datadog Events API | DD event JSON |
teams |
Microsoft Teams Webhook | Adaptive Card JSON |
Each preset supplies a default body template and the headers that service expects. Override any field in your config.
HMAC signing
Section titled “HMAC signing”When you set secret, Rocky signs the request body with HMAC-SHA256 and sends the digest in a header:
X-Rocky-Signature: sha256=<hex-encoded digest>Rocky only adds the header. Verifying it is the receiving service’s job, and Rocky cannot tell whether it happens. To verify, recompute HMAC-SHA256 over the raw request body with the same secret. Compare your result against the header’s lower-case hex digest.
Body templates
Section titled “Body templates”A custom body template uses Mustache-style syntax:
body_template = """{ "text": "Pipeline {{event}}: {{metadata.tables_copied}} tables copied in {{duration_ms}}ms"}"""Supported: {{field}}, {{metadata.key}}, {{#if field}}...{{/if}}.
Testing hooks
Section titled “Testing hooks”Check your hook configuration without running a real pipeline:
# List all configured hooksrocky hooks list
# Fire a test eventrocky hooks test on_pipeline_startrocky hooks test sends a synthetic event context, so you can confirm your script runs and reads the JSON correctly.