InterceptDocumentation
Docs/Developers/Webhooks
Reference (illustrative)

Webhooks

Planned Intercept webhook events, payload format, HMAC-SHA256 signature verification with timestamp tolerance, retry schedule and idempotent handling.

Webhooks are designed to notify your systems when something happens that you did not directly request: a human approved a pending transaction, a circuit breaker fired, a guardian paused an agent, or the network flagged a threat. This page describes the planned events, payload format and verification procedure. Webhooks are not live.

Events

Event Sent when
evaluation.approved An evaluation is approved, including after a human approves a requires_approval decision
evaluation.blocked An evaluation is blocked, including after a human rejects a pending approval
evaluation.requires_approval An evaluation needs human approval before it can execute
circuit_breaker.triggered A circuit breaker in the policy fires
agent.paused An agent is paused manually, by a guardian, by a breaker or by pauseOn
agent.resumed A paused agent is resumed
threat.detected A network signal or local detection affects one of your agents

You can subscribe to a subset. High-volume integrations may choose not to receive evaluation.approved, since the synchronous evaluate response already carries the decision.

Payload

JSON
{
  "id": "evt_01HZ9P...",
  "type": "circuit_breaker.triggered",
  "createdAt": "2026-11-15T10:04:55Z",
  "agentId": "agent_7f3c",
  "data": {
    "trigger": "drawdown_pct",
    "threshold": 0.08,
    "observed": 0.083,
    "action": "reduce_only",
    "policyVersion": "0.1"
  }
}

Evaluation events carry the full decision object in data, with the same fields as an evaluate response.

Signature verification

Every request includes an Intercept-Signature header:

text
Intercept-Signature: t=1794737095,v1=5f2b8c...e91a
  • t is the Unix timestamp, in seconds, when the request was signed.
  • v1 is the hex-encoded HMAC-SHA256 of the string "{t}.{raw body}", keyed with your webhook signing secret.

Verify before parsing or acting on the body. Three details matter:

  1. Use the raw body. Re-serializing parsed JSON can change whitespace or key order and break the signature.
  2. Compare in constant time. Use timingSafeEqual, never ===.
  3. Enforce a timestamp tolerance. Reject requests whose t is more than 300 seconds from your clock. This limits replay of captured requests.

The SDK helper is designed to do all three:

TypeScript
const event = intercept.webhooks.verify(rawBody, req.headers["intercept-signature"]); // illustrative

The same check without the SDK, using only Node.js built-ins:

TypeScript
import { createHmac, timingSafeEqual } from "node:crypto";

const TOLERANCE_SEC = 300;

export function verifyInterceptSignature(
  rawBody: string,
  header: string | undefined,
  secret: string,
  nowSec: number = Math.floor(Date.now() / 1000),
): boolean {
  if (!header) return false;

  const parts = Object.fromEntries(
    header.split(",").map((kv) => {
      const i = kv.indexOf("=");
      return [kv.slice(0, i).trim(), kv.slice(i + 1).trim()];
    }),
  );

  const t = Number(parts.t);
  const received = parts.v1;
  if (!Number.isFinite(t) || !received) return false;
  if (Math.abs(nowSec - t) > TOLERANCE_SEC) return false;

  const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");

  const a = Buffer.from(expected, "hex");
  const b = Buffer.from(received, "hex");
  return a.length === b.length && timingSafeEqual(a, b);
}

Respond with any 2xx status once the event is verified and queued. Do slow work asynchronously; a handler that takes longer than the delivery timeout, planned at 10 seconds, counts as a failure.

Secret rotation

During rotation, the header is designed to carry two v1 values, one per active secret. Accept the request if either matches. Retire the old secret after all deliveries signed with it have been received.

Retries

A delivery that does not receive a 2xx within the timeout is retried with exponential backoff and jitter. The proposed schedule:

Attempt Delay after previous attempt
1 Immediate
2 30 seconds
3 2 minutes
4 10 minutes
5 1 hour
6 6 hours
7 24 hours

After the final attempt, the event is marked failed and remains retrievable for replay. An endpoint that fails consistently may be disabled, with notice to the account owner.

Every retry carries a fresh timestamp and signature, so the tolerance check still works on late deliveries.

Idempotent handling

Delivery is at least once. The same event can arrive more than once, for example when your handler succeeded but the response was lost. Events can also arrive out of order: agent.resumed may be delivered before a delayed agent.paused.

  • Deduplicate on id. Record processed event IDs and skip repeats.
  • Order by state, not arrival. For pause state or approval status, fetch the current value, for example with GET /v0/evaluations/{id}, rather than applying events as a sequence.
  • Do not execute from a webhook alone. An evaluation.approved event is a notification. Proceed through execute, which is designed to check the evaluation's current state and is protected by its own idempotency key. See Error Handling.

Webhooks report what Intercept decided; they do not enforce anything themselves. Blocking only works if the integration is enforced.

© InterceptDocumentation describes planned functionality unless stated otherwise.