Docs navigation
Generic Webhook
POST every lead event as JSON to any HTTPS endpoint you control. Optionally sign the request body with HMAC-SHA-256 so your receiver can verify authenticity.
What this is
Generic Webhook is LeadRails's escape hatch. Point it at any URL on any host and we'll POST the lead-event JSON to it. Use this when no curated adapter matches your destination — internal APIs, custom CRMs, n8n self-hosted, anything that speaks JSON-over-HTTP.
In LeadRails
- Sign in → Destinations → New
- Adapter type:
Generic Webhook - URL: your receiver's full URL (must be HTTPS; we SSRF-guard against private IPs at create AND delivery time)
- Click Create
Signing requests so your receiver can trust them
Generic Webhook is the only adapter that supports outbound HMAC signing. Your receiver verifies each request by recomputing the signature with a shared secret. Without this, anyone could POST to your URL — signing makes that infeasible.
Generate a signing secret
- Open the destination you just created → click Edit → expand Advanced
- Scroll to Signing secrets → click New signing secret
- Give it a label (e.g.
prod-2026-q2), click Generate secret - Copy the secret IMMEDIATELY — it's shown once and then hashed; you can never retrieve it again
How to verify on the receiver side
v1=base64(HMAC-SHA-256(secret, X-LeadRails-Timestamp + "." + sha256_hex(body)))
LeadRails sends X-LeadRails-Signature: v1=<base64>
and X-LeadRails-Timestamp: <ISO-8601> headers. Your
receiver:
- read both headers
- compute
HMAC-SHA-256(your_secret, timestamp + "." + sha256_hex(raw_request_body)) - base64-encode the result
- prefix with
v1= - compare with the header value using a constant-time string compare
Reject the request if the comparison fails or the timestamp is more than 5 minutes old (to prevent replay).
Multiple active secrets (zero-downtime rotation)
- You can have multiple active signing secrets at once. LeadRails signs with the most recently created one.
- During rotation: (1) create a new secret in LeadRails, (2) deploy your receiver to accept either the old OR new secret, (3) revoke the old secret in LeadRails. No downtime.
Test it
- Sources → your source → Settings → ensure a route exists from this source to the Generic Webhook destination
- Click Send test event in the SourceTestEvent card
- Your receiver gets a POST within seconds; the result panel deep-links to the Jobs page so you can confirm the 2xx
Request shape
Default body shape (when no per-route mapping is configured):
{
"event_id": "evt_xxx",
"normalized_lead": { "name": "...", "email": "...", "phone": "...", ... },
"raw_event": { ...the customer's original payload... },
"sent_at": "ISO-8601 timestamp"
} - With per-route mapping: build any custom shape via the Routes → Mapping editor
- Method:
POSTby default (configurable toPUTorPATCH) - Content-Type:
application/json
Reliability
- Retries: 5 attempts via the in-DB retry counter (NOT Cloudflare Queue's retry — we override that)
- Backoff schedule: outbox sweeper checks every minute; exponential backoff between attempts
- Permanent failures (4xx, except 408/429): no retry, alert fires
- Transient failures (5xx, timeout, network): retried up to the budget; alert if budget exhausted
- Rate limits (429): respects your
Retry-Afterheader if present
Common issues
unsafe_url: private_ip- URL resolved to a private IP at create or delivery time. LeadRails blocks SSRF; expose your service on a public-routable host (e.g. via Cloudflare Tunnel if it's internal).
unsafe_url: must_use_https- HTTP not allowed. Use HTTPS.
- Permanent failure but it works in curl
- Your receiver returned 4xx. Check the Jobs page for the response body; we capture it for diagnosis.
- Signature verification fails
-
Your receiver is probably hashing the body string instead of the raw
bytes. SHA-256 hex of
raw_body_bytes, notJSON.stringify(parsed_body).