IntegrationsWebhooks

Custom Webhooks

Send leads to any HTTP endpoint in real-time. Connect Ledly with Zapier, Slack, your own API, marketing automation tools, Student Information Systems (SIS), or any system that accepts HTTP requests.

Custom webhooks give you complete flexibility to integrate Ledly with any system, without waiting for native integrations.

Features

FeatureDescription
Real-time deliveryLeads are sent instantly when received
Custom headersAdd authentication headers, API keys, or custom headers
Retry on failureAutomatic retries with exponential backoff
Request loggingFull request/response logging for debugging
JSON payload mappingCustomize the payload structure for each endpoint
Multiple endpointsSend leads to multiple webhooks simultaneously

Setting Up a Webhook

  1. Go to SettingsIntegrationsWebhooks
  2. Click Add Webhook Endpoint
  3. Configure your webhook:
FieldDescriptionRequired
NameFriendly name for this webhookYes
URLYour endpoint URL (must be HTTPS)Yes
EventsWhich events trigger this webhookYes
HeadersCustom HTTP headers to includeNo
Payload TemplateCustom JSON payload structureNo
ActiveEnable/disable without deletingYes
  1. Click Save
  2. Use Test Webhook to verify your endpoint works

Authentication Options

API Key in Header

Header Name: Authorization
Header Value: your-api-key-here

Bearer Token

Header Name: Authorization
Header Value: Bearer your-token-here

Basic Auth

Header Name: Authorization
Header Value: Basic base64(username:password)

Custom Headers

Add any custom headers your endpoint requires:

Header Name: X-Custom-Header
Header Value: your-value

Webhook Payload

Default Payload

When a lead is received, Ledly sends a JSON payload:

{
  "event": "lead.created",
  "timestamp": "2024-12-26T12:00:00Z",
  "webhook_id": "wh_abc123",
  "data": {
    "id": "lead_xyz789",
    "email": "[email protected]",
    "first_name": "John",
    "last_name": "Doe",
    "phone": "+1234567890",
    "company": "Example University",
    "program": "MBA",
    "lead_source": "website",
    "utm_source": "google",
    "utm_medium": "cpc",
    "utm_campaign": "fall-2025",
    "created_at": "2024-12-26T12:00:00Z",
    "custom_fields": {
      "start_date": "Fall 2025",
      "campus": "Main Campus"
    }
  }
}

Custom Payload Templates

Customize the payload structure for each webhook. Use template variables:

{
  "contact": {
    "email": "{{email}}",
    "name": "{{first_name}} {{last_name}}",
    "phone": "{{phone}}"
  },
  "source": "Ledly",
  "campaign": "{{utm_campaign}}",
  "metadata": {
    "lead_id": "{{id}}",
    "timestamp": "{{created_at}}"
  }
}

Available Variables

VariableDescription
{{id}}Ledly lead ID
{{email}}Email address
{{first_name}}First name
{{last_name}}Last name
{{phone}}Phone number
{{company}}Company/University
{{job_title}}Job title
{{lead_source}}Lead source
{{utm_source}}UTM source
{{utm_medium}}UTM medium
{{utm_campaign}}UTM campaign
{{utm_content}}UTM content
{{utm_term}}UTM term
{{created_at}}ISO timestamp
{{custom.field_name}}Custom field value

Event Types

EventDescription
lead.createdNew lead received
lead.updatedLead data updated
lead.status_changedLead status changed
lead.enrichedLead enriched with additional data
lead.routedLead routed to destination

Verifying Webhook Signatures

Each webhook request includes a signature header for security verification.

Headers Sent

HeaderDescription
X-Ledly-SignatureHMAC-SHA256 signature
X-Ledly-TimestampUnix timestamp of request
X-Ledly-Webhook-IdUnique webhook configuration ID
X-Ledly-Delivery-IdUnique delivery attempt ID

Verification Code

const crypto = require('crypto');
 
function verifyWebhook(payload, signature, timestamp, secret) {
  // Check timestamp is within 5 minutes
  const now = Math.floor(Date.now() / 1000);
  if (Math.abs(now - parseInt(timestamp)) > 300) {
    return false; // Replay attack protection
  }
 
  const signedPayload = `${timestamp}.${payload}`;
  const expected = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');
 
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}
 
// Express middleware
app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
  const signature = req.headers['x-ledly-signature'];
  const timestamp = req.headers['x-ledly-timestamp'];
 
  if (!verifyWebhook(req.body, signature, timestamp, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
 
  const event = JSON.parse(req.body);
  // Process event...
 
  res.status(200).send('OK');
});

Retry Policy

If your endpoint returns a non-2xx status code or times out, Ledly automatically retries:

AttemptDelayCumulative Time
1st retry1 minute1 minute
2nd retry5 minutes6 minutes
3rd retry30 minutes36 minutes
4th retry2 hours2.5 hours
5th retry24 hours26.5 hours
⚠️

After 5 failed attempts, the delivery is marked as failed. You can manually retry failed deliveries from the webhook logs.

Timeout

Webhook requests timeout after 30 seconds. Ensure your endpoint responds quickly. For long-running operations, acknowledge the webhook immediately and process asynchronously.

Request Logging

All webhook requests are logged for 30 days. View logs in SettingsWebhooksLogs.

Each log entry includes:

  • Request timestamp
  • HTTP status code
  • Response time
  • Request headers and body
  • Response headers and body (first 10KB)
  • Retry attempt number

Common Integrations

Zapier

  1. Create a Zapier Zap with Webhooks by Zapier as the trigger
  2. Choose Catch Hook
  3. Copy the webhook URL
  4. Add it to Ledly as a webhook endpoint
  5. Send a test to configure your Zap

Slack

Post leads to a Slack channel:

  1. Create a Slack Incoming Webhook
  2. Add the webhook URL to Ledly
  3. Use a custom payload template:
{
  "text": "New lead received!",
  "blocks": [
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "*New Lead*\n{{first_name}} {{last_name}}\n{{email}}\n{{phone}}"
      }
    },
    {
      "type": "context",
      "elements": [
        {
          "type": "mrkdwn",
          "text": "Source: {{lead_source}} | Campaign: {{utm_campaign}}"
        }
      ]
    }
  ]
}

Make (Integromat)

  1. Create a new Scenario with Webhooks module
  2. Choose Custom webhook
  3. Copy the webhook URL
  4. Add it to Ledly
  5. Send a test to define the data structure

n8n

  1. Create a workflow with Webhook trigger node
  2. Set method to POST
  3. Copy the production URL
  4. Add it to Ledly with appropriate headers

Custom API

For your own API, ensure it:

  • Accepts POST requests
  • Parses JSON body
  • Returns 2xx status on success
  • Responds within 30 seconds
  • Optionally verifies signatures

Multiple Endpoints

Send leads to multiple systems simultaneously:

  1. Add multiple webhook endpoints
  2. Each receives the same events
  3. Failures are independent (one failing doesn’t affect others)
  4. Each can have different payload templates

Plan Limits:

  • Education ($899/mo): 10 webhook endpoints
  • Enterprise ($1,999/mo): Unlimited endpoints

Testing Webhooks

Test Button

Click Test Webhook to send a sample payload to your endpoint. This uses test data and doesn’t affect your lead counts.

Webhook Tester Tools

Use these tools to inspect webhook payloads:

Troubleshooting

Webhook Not Receiving Data

  1. Check the webhook is Active
  2. Verify the URL is correct and accessible
  3. Ensure your firewall allows Ledly IPs
  4. Check the webhook logs for errors

Invalid Signature Errors

  1. Verify you’re using the correct webhook secret
  2. Ensure you’re using the raw request body (not parsed JSON)
  3. Check timestamp validation isn’t too strict

Timeouts

  1. Acknowledge webhook immediately, process async
  2. Optimize endpoint performance
  3. Check for network issues

Duplicate Events

  1. Implement idempotency using X-Ledly-Delivery-Id
  2. Store processed delivery IDs
  3. Skip already-processed events
const processedDeliveries = new Set();
 
app.post('/webhook', (req, res) => {
  const deliveryId = req.headers['x-ledly-delivery-id'];
 
  if (processedDeliveries.has(deliveryId)) {
    return res.status(200).send('Already processed');
  }
 
  processedDeliveries.add(deliveryId);
  // Process event...
 
  res.status(200).send('OK');
});

IP Allowlist

If your firewall requires allowlisting, Ledly webhooks originate from:

52.22.XXX.XXX
52.22.XXX.XXX
⚠️

Contact support for the current IP list, as these may change.