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
| Feature | Description |
|---|---|
| Real-time delivery | Leads are sent instantly when received |
| Custom headers | Add authentication headers, API keys, or custom headers |
| Retry on failure | Automatic retries with exponential backoff |
| Request logging | Full request/response logging for debugging |
| JSON payload mapping | Customize the payload structure for each endpoint |
| Multiple endpoints | Send leads to multiple webhooks simultaneously |
Setting Up a Webhook
- Go to Settings → Integrations → Webhooks
- Click Add Webhook Endpoint
- Configure your webhook:
| Field | Description | Required |
|---|---|---|
| Name | Friendly name for this webhook | Yes |
| URL | Your endpoint URL (must be HTTPS) | Yes |
| Events | Which events trigger this webhook | Yes |
| Headers | Custom HTTP headers to include | No |
| Payload Template | Custom JSON payload structure | No |
| Active | Enable/disable without deleting | Yes |
- Click Save
- Use Test Webhook to verify your endpoint works
Authentication Options
API Key in Header
Header Name: Authorization
Header Value: your-api-key-hereBearer Token
Header Name: Authorization
Header Value: Bearer your-token-hereBasic 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-valueWebhook 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
| Variable | Description |
|---|---|
{{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
| Event | Description |
|---|---|
lead.created | New lead received |
lead.updated | Lead data updated |
lead.status_changed | Lead status changed |
lead.enriched | Lead enriched with additional data |
lead.routed | Lead routed to destination |
Verifying Webhook Signatures
Each webhook request includes a signature header for security verification.
Headers Sent
| Header | Description |
|---|---|
X-Ledly-Signature | HMAC-SHA256 signature |
X-Ledly-Timestamp | Unix timestamp of request |
X-Ledly-Webhook-Id | Unique webhook configuration ID |
X-Ledly-Delivery-Id | Unique 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:
| Attempt | Delay | Cumulative Time |
|---|---|---|
| 1st retry | 1 minute | 1 minute |
| 2nd retry | 5 minutes | 6 minutes |
| 3rd retry | 30 minutes | 36 minutes |
| 4th retry | 2 hours | 2.5 hours |
| 5th retry | 24 hours | 26.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 Settings → Webhooks → Logs.
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
- Create a Zapier Zap with Webhooks by Zapier as the trigger
- Choose Catch Hook
- Copy the webhook URL
- Add it to Ledly as a webhook endpoint
- Send a test to configure your Zap
Slack
Post leads to a Slack channel:
- Create a Slack Incoming Webhook
- Add the webhook URL to Ledly
- 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)
- Create a new Scenario with Webhooks module
- Choose Custom webhook
- Copy the webhook URL
- Add it to Ledly
- Send a test to define the data structure
n8n
- Create a workflow with Webhook trigger node
- Set method to POST
- Copy the production URL
- 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:
- Add multiple webhook endpoints
- Each receives the same events
- Failures are independent (one failing doesn’t affect others)
- 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
- Check the webhook is Active
- Verify the URL is correct and accessible
- Ensure your firewall allows Ledly IPs
- Check the webhook logs for errors
Invalid Signature Errors
- Verify you’re using the correct webhook secret
- Ensure you’re using the raw request body (not parsed JSON)
- Check timestamp validation isn’t too strict
Timeouts
- Acknowledge webhook immediately, process async
- Optimize endpoint performance
- Check for network issues
Duplicate Events
- Implement idempotency using
X-Ledly-Delivery-Id - Store processed delivery IDs
- 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.XXXContact support for the current IP list, as these may change.