Skip to main content
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

EventDescription
campaign.startedA campaign has transitioned from draft to running and is actively queueing messages
message.sentA single sequence step has been successfully delivered to a recipient’s mail server
message.bouncedA delivery attempt failed permanently (hard bounce) or temporarily exceeded retry limits
reply.receivedAn 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.
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:
{
  "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:
{
  "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.
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.
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 and update your CRM — all without any human in the loop.