> ## Documentation Index
> Fetch the complete documentation index at: https://docs.leadterra.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Register and Handle Leadterra Webhooks for Campaign Events

> Set up Leadterra webhooks to receive real-time event notifications for campaign sends, bounces, and inbound replies in your app or agent.

Webhooks let your application receive instant notifications when something meaningful happens in a Leadterra campaign — a message is delivered, a prospect replies, or a send bounces. Instead of polling the API on a schedule, you register an HTTPS endpoint and Leadterra POSTs a structured event payload to it the moment the event occurs. This makes webhooks the foundation of any real-time agent workflow or CRM integration built on Leadterra.

## Available events

| Event              | Description                                                                              |
| ------------------ | ---------------------------------------------------------------------------------------- |
| `campaign.started` | A campaign has transitioned from `draft` to `running` and is actively queueing messages  |
| `message.sent`     | A single sequence step has been successfully delivered to a recipient's mail server      |
| `message.bounced`  | A delivery attempt failed permanently (hard bounce) or temporarily exceeded retry limits |
| `reply.received`   | An inbound reply has been received and classified by Leadterra                           |

You can subscribe to any combination of these events in a single webhook registration. Subscribing to all four events from one endpoint is a common pattern for full campaign observability.

## Register a webhook

Call `POST /v1/webhooks` with your endpoint URL and the list of events you want to receive. Your URL must be publicly reachable over HTTPS.

```bash theme={null}
curl -X POST https://app.leadterra.co/v1/webhooks \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/leadterra",
    "events": [
      "campaign.started",
      "message.sent",
      "message.bounced",
      "reply.received"
    ]
  }'
```

A successful response confirms registration and returns the webhook's unique ID:

```json theme={null}
{
  "webhook": {
    "id": "wh_01HZC7RQNP4XT2KM",
    "url": "https://your-app.com/webhooks/leadterra",
    "events": [
      "campaign.started",
      "message.sent",
      "message.bounced",
      "reply.received"
    ],
    "status": "active",
    "createdAt": "2025-06-10T16:00:00Z"
  }
}
```

Save the `id` if you need to update or delete the webhook registration later.

## Webhook payload format

Every event Leadterra sends to your endpoint follows the same envelope structure: a top-level `event` type, an ISO 8601 `timestamp`, and an event-specific `data` object. Here is an example payload for the `reply.received` event:

```json theme={null}
{
  "event": "reply.received",
  "timestamp": "2025-06-11T09:14:35Z",
  "webhookId": "wh_01HZC7RQNP4XT2KM",
  "data": {
    "campaignId": "camp_01HZA1TPNQ8RD4KF",
    "lead": {
      "email": "ava@acmecorp.com",
      "firstName": "Ava",
      "lastName": "Chen",
      "company": "Acme Corp",
      "jobTitle": "VP Sales"
    },
    "reply": {
      "id": "rpl_01HZB3MNPW6XT8QA",
      "classification": "interested",
      "receivedAt": "2025-06-11T09:14:32Z",
      "threadSnippet": "Hey, this actually looks relevant — can we jump on a call Thursday afternoon?"
    }
  }
}
```

For `message.bounced` events, the `data` object includes a `bounceType` field (`hard` or `soft`) and a `bounceReason` string from the receiving mail server. For `message.sent`, it includes the `sequenceStep` index and the `messageId` for tracing.

## Webhook security

Leadterra signs every outbound webhook request with an `X-Leadterra-Signature` header. The value is an HMAC-SHA256 digest of the raw request body, keyed with your webhook's signing secret. Always verify this signature before processing the payload to ensure the request genuinely originated from Leadterra and hasn't been tampered with.

```javascript theme={null}
import crypto from "crypto";

function verifySignature(rawBody, signatureHeader, signingSecret) {
  const expected = crypto
    .createHmac("sha256", signingSecret)
    .update(rawBody)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signatureHeader)
  );
}
```

Your webhook's signing secret is returned when you register the endpoint. Store it in an environment variable — never commit it to source control.

## Retries and delivery

If your endpoint returns a non-2xx HTTP status code or fails to respond within 10 seconds, Leadterra marks the delivery as failed and retries it automatically using exponential backoff. The retry schedule is approximately:

* **1st retry** — 1 minute after the initial failure
* **2nd retry** — 5 minutes
* **3rd retry** — 30 minutes
* **4th retry** — 2 hours
* **5th retry** — 8 hours

After five failed attempts, the event is marked as undeliverable and will not be retried again. You can inspect failed deliveries and manually replay them from the Leadterra dashboard. To avoid duplicate processing during retries, treat the `data.reply.id` or `data.messageId` field as an idempotency key in your handler.

<Tip>
  Webhook events are an ideal trigger for automated agent workflows. For example, wire `reply.received` with `classification: "interested"` to kick off a meeting-booking agent, or use `message.bounced` with `bounceType: "hard"` to automatically add the address to your [suppression list](/guides/suppression) and update your CRM — all without any human in the loop.
</Tip>
