CatchIntentCatchIntent
Integrations

Webhooks

Get Lead and pipeline-run events pushed to your own systems the moment they happen.

Webhooks deliver events to your systems in real time, one signed request per event. Use them to react immediately: push a delivered lead into a workflow, sync a status change, or trigger automation in n8n, Zapier, or your own backend.

This is the machine surface. For people, use the notification channels.

Events

EventFires when
lead.deliveredA new, verified lead is saved
lead.status_changedA lead changes status, for example pushed to a CRM
agent.run.completedA pipeline run completes successfully (legacy name; will be pipeline.run.completed in the next API version)
agent.run.failedA pipeline run fails before completing

Set up an endpoint

Add the endpoint

In your workspace settings, add your endpoint URL and select the events it should receive. You can scope an endpoint to a single Product, or leave it across all Products in the workspace.

Save the signing secret

The secret is shown once. Store it now. You use it to verify every request. Rotating it invalidates the old one immediately.

Optionally filter lead events

For lead.delivered and lead.status_changed you can require a minimum warmth score, limit to certain signal types, or limit to certain statuses. Pipeline run events always fire.

Payload

Every event is wrapped in the same envelope. data differs by event. This is a lead.delivered payload:

{
  "id": "0190f3...",
  "event": "lead.delivered",
  "createdAt": "2026-05-15T10:30:00.000Z",
  "workspaceId": "0190ab...",
  "data": {
    "lead": {
      "id": "0190...",
      "productId": "0190...",
      "name": "Jane Prospect",
      "title": "VP of Sales",
      "company": "Acme Inc",
      "headline": "VP of Sales at Acme Inc",
      "location": "San Francisco, CA",
      "linkedinUrl": "https://www.linkedin.com/in/jane-prospect",
      "email": null,
      "warmthScore": 82,
      "reasonLine": "Acme closed a Series B last week",
      "status": "new",
      "strategyType": "funding_signals",
      "agentId": "0190...",
      "createdAt": "2026-05-15T10:30:00.000Z"
    },
    "agent": { "id": "0190...", "name": "ICP Agent" },
    "sourceRunId": "0190..."
  }
}

name, title, company, headline, location, email, warmthScore, reasonLine, and agentId can be null. The agent and agentId fields are legacy and may be removed in the next API version — prefer productId and the strategyType (signal type) field for routing.

data is different for the other events:

  • lead.status_changed: { lead, fromStatus, toStatus }
  • agent.run.completed: { sourceRunId, agentId, agentName, strategyType, leadsFetched, leadsDelivered, costUsd }
  • agent.run.failed: { sourceRunId, agentId, agentName, strategyType, errorMessage }

Headers

HeaderValue
X-CatchIntent-Signaturet=<unix-seconds>,v1=<hex hmac-sha256>
X-CatchIntent-EventThe event type
X-CatchIntent-Event-IdStable id for the event. Use it as an idempotency key

Verify the signature

Recompute the HMAC over timestamp.rawBody with your signing secret and compare in constant time. Reject requests with an old timestamp.

const crypto = require('node:crypto');

function verify(rawBody, header, secret, toleranceSeconds = 300) {
  const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
  const t = Number(parts.t);
  if (Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false;
  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${t}.${rawBody}`)
    .digest('hex');
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}

Capture the raw request body before parsing JSON. Re-serializing changes the bytes and breaks the signature.

Delivery and retries

A 2xx marks the delivery successful. Timeouts, 429, and 5xx are retried with exponential backoff up to six attempts. Other 4xx responses fail immediately. Delivery is at least once, so make your endpoint idempotent by deduping on X-CatchIntent-Event-Id. Every attempt is logged and can be replayed from the dashboard.

Always verify the signature and dedupe on the event id before acting. Treat an unverified request as hostile.

On this page