Webhooks
Subscribe to run, approval, budget, and policy events. HMAC-SHA256 signatures with rotation grace window. Standard delivery semantics — at-least-once, exponential backoff, dead-letter after 8.5 minutes of failures.
Rungate posts webhooks for state changes the operator (or your agent) wants to act on. Subscriptions are configured in the dashboard at app.rungate.dev/webhooks or via the admin API.
Events
| Event | Fires when |
|---|---|
run.started | First call in a run is dispatched. |
run.completed | Run closed normally (idle timeout or explicit completion). |
run.blocked | Run stopped by a budget cap or policy violation. |
step.failed_over | A call failed over from primary to fallback provider. |
approval.pending | Tool call hit a gate; awaiting human review. |
approval.approved | Gate resolved positively. |
approval.rejected | Gate resolved negatively. |
budget.alert | Run crossed the 80% alert threshold. |
budget.exceeded | Run exceeded the hard cap. |
policy.drift | Agent attempted an action outside the policy allowlist. |
account.updated | Account name, email, slug, or status changed. |
admin_token.created | New admin token issued. |
agent.created | New agent created and initial token issued. |
Payload shape
Consistent across events:
{
"event": "approval.pending",
"delivered_at": "2026-04-26T14:12:20Z",
"data": {
"gate_id": "gate_8xk2m",
"run_id": "run_customer_refund_2026_04_26",
"agent_id": "agt_claude_code_prod",
"rule": "refund:over-$500",
"proposed_action": {
"tool": "issue_refund",
"args": { "amount_usd": 1240.00 }
},
"expires_at": "2026-04-26T15:48:12Z"
}
}
Per-event data shapes are documented in the
OpenAPI spec under the
WebhookPayload* schemas.
Signature verification
Every delivery carries an HMAC-SHA256 signature over the raw request body, with a timestamp to prevent replay. Verify before trusting any payload.
X-Rungate-Timestamp: 1737126732
X-Rungate-Signature: v1=1e4b0f... # current secret
X-Rungate-Signature: v1=9c3a87... # previous secret (rotation grace) Two signatures may be present during a rotation grace window so you can roll the signing secret without dropped deliveries. Accept if either signature matches.
Verification (Python)
import hmac, hashlib
def verify(body: bytes, timestamp: str, signatures: list[str], secret: str) -> bool:
signed = timestamp.encode() + b"." + body
expected = "v1=" + hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return any(hmac.compare_digest(expected, sig) for sig in signatures) Verification (TypeScript)
import { createHmac, timingSafeEqual } from 'node:crypto';
function verify(body: Buffer, timestamp: string, signatures: string[], secret: string): boolean {
const signed = Buffer.concat([Buffer.from(timestamp + '.'), body]);
const expected = 'v1=' + createHmac('sha256', secret).update(signed).digest('hex');
const expectedBuf = Buffer.from(expected);
return signatures.some((sig) => {
const sigBuf = Buffer.from(sig);
return sigBuf.length === expectedBuf.length && timingSafeEqual(sigBuf, expectedBuf);
});
} Reject stale timestamps (more than 5 minutes old) to prevent replay attacks even if a signature leaks.
Delivery semantics
- At-least-once. Webhooks may deliver more than
once. Make your handler idempotent — key off
data.run_id+event+delivered_atfor deduplication. - Retries. On 4xx/5xx responses or timeouts, Rungate retries with exponential backoff for up to ~8.5 minutes total before marking the delivery dead-letter. Dead-letter events surface in the dashboard.
- 2xx response. Any 2xx is success. Empty body is fine.
- 4xx terminal. Specific 4xx codes
(
400,401,403,404,410,422) are treated as terminal — Rungate stops retrying. Use this if you want to deliberately drop deliveries (return 410 from a decommissioned endpoint). - Timeout. 10 seconds per delivery attempt.
Rotating the signing secret
From the dashboard or admin API, rotate the secret. Rungate signs new deliveries with the new secret AND the previous secret for a grace window (default 24 hours), so your verifier can update without dropped deliveries:
- Rotate the secret. Both signatures now appear in headers.
- Update your verifier to use the new secret.
- After the grace window ends, only the new signature is sent.
Common patterns
Slack notification on approval gates
Subscribe to approval.pending. On receipt, post into
a Slack channel with approve/reject buttons that call back to
your handler. Your handler hits Rungate's
POST /api/approval-requests/{id}/approve (or
/reject).
PagerDuty on dead-letter
Subscribe to budget.exceeded + run.blocked
in production. Page on either event firing for a critical agent.
Audit log
Subscribe to all events. Dump payloads to your existing audit
pipeline (BigQuery, Snowflake, S3 + Athena). Run-level grouping
is preserved by data.run_id.