webhooks:inbound
Only landing-page leads (
source = landing_page) are delivered or polled out. Leads you sync into Mailr via
POST /api/v1/tracking/lead are never fanned back out — that would round-trip a lead straight into the CRM it
came from.Subscribe
POST /api/v1/webhooks/subscribe registers a public HTTPS URL for lead.created deliveries.
200 OK
https only, and the hostname must resolve to public addresses — at
registration and again at delivery time. event defaults to lead.created (the only supported value).
To remove a subscription, DELETE /api/v1/webhooks/subscribe?id=<id> or ?target_url=<url> (org-scoped).
Rotate a lost secret
POST /api/v1/webhooks/rotate mints a replacement signing secret and returns it exactly once. Identify the
subscription by id or by target_url (plus optional event, default lead.created).
curl
The lead.created delivery
Mailr POSTs the canonical enriched lead to your endpoint. It’s the same object shape the poll endpoint returns,
plus a mailr_lead_id:
custom_fields entry is also flattened to a top-level custom_<field_id> key (semicolon-joined) for
easy CRM mapping. Mailr does not inspect your response status — a delivery is considered attempted once the
request completes. Non-2xx responses are ignored; timeouts and blocked hosts are recorded in Mailr’s server
logs only (not in your webhook log) and are not retried. Poll GET /api/v1/integrations/leads to reconcile
anything your endpoint dropped.
Signature verification
A subscription with asigning_secret receives two headers, in the same shape Stripe and Slack use:
The signed string is
`${timestamp}.${rawBody}` where rawBody is the exact bytes of the request body.
Verify by recomputing the HMAC over the raw body you received (do not re-serialize the parsed JSON — key order
or number formatting could drift), comparing in constant time, and rejecting timestamps that drift more than
300 seconds from now.
Avoiding echo loops
Because deliveries carrymailr_lead_id, a lead you receive here and then write into your CRM can be recognized
if your automation fires it back at POST /api/v1/events. Always copy mailr_lead_id into
record.properties.mailr_lead_id on any event derived from a Mailr lead — Mailr will skip campaign matching
(reason: "loop_guard") rather than mailing an address it just sourced. See
Send events → Loop guard.
Poll instead
GET /api/v1/integrations/leads?since=<ISO> returns the 50 most recent landing-page leads, newest first, filtered
to created_at strictly after since. De-dupe by id.
200 OK
A brand-new org with no real leads receives exactly one clearly flagged sample lead (
test: true, fixed
all-zero id) so a Zap’s “Test trigger” step can map fields. It disappears once a real lead is captured.