LeadMove Docs
Delivery

Webhook deliveries

Posting leads to a buyer's API — URL, headers, the standard payload, custom body templates with merge codes, reading the buyer's response, and what a failure looks like.

A webhook delivery method POSTs one JSON body per lead to the buyer's URL. You set the URL, optional headers, and — when the buyer has their own format — the exact body to send.

The URL must be https:// and publicly reachable. Requests always use POST, and redirects are not followed.

A webhook delivery method: the buyer's https URL, custom headers carrying their API key as JSON, and Custom body template switched on, listing every merge code available plus the codes still waiting to be mapped to a lead field.

Custom headers

Optional, entered as JSON, and merged into the request on top of Content-Type: application/json and the lead reference headers:

{
  "Authorization": "Bearer your-token"
}

The standard payload

With Custom body template off, the buyer receives this envelope. lead contains the delivery method's expected fields, already transformed:

{
  "leadId": "LD-7QK4M2",
  "internalLeadId": "…",
  "buyer": { "id": "…", "name": "Acme Leads" },
  "lead": { "email": "…", "phone": "…", "zip": "90001" },
  "timestamp": "2026-07-29T14:02:11.000Z"
}

Custom body template

Turn the switch on when the buyer documents its own contract. You then write the exact JSON to send, with {merge_code} placeholders — a buyer's sample body can usually be pasted verbatim and mapped field by field.

Quoting decides the type: "year": "{vehicle_year}" sends text, "year": {vehicle_year} sends a number. Static values (an API key, a constant) are typed directly. The editor validates the JSON as you type and flags unknown merge codes.

Available merge codes: every field in this delivery method's schema and in the pipelines wired to the buyer, plus the system codes {timestamp}, {email}, {phone}, {lead_id}, {pipeline}, {buyer}, {source}, {quality_score} and {quality_grade}. Templates saved with the old {campaign} code keep working.

A code can carry a modifier: {sold_at|date:MM/DD/YYYY}, {phone|phone:e164}, {state|upper} (also |lower, |title). For repeated formatting, use Field transforms below the template instead — set it once and {field} sends the transformed value.

Keep {lead_id} in the template, in a field the buyer stores: a template sends exactly what it contains, and the lead reference leaves with the body only if you wrote it in. See The lead reference.

The lead reference

Every webhook POST carries the lead's reference, LD-XXXXXX, in three HTTP headers, whatever the body contains:

HeaderValue
X-Lead-IdThe lead reference, LD-XXXXXX
X-Delivery-IdThe id of this delivery
X-Buyer-IdThe buyer's id in LeadMove

A server ignores headers it does not read, so nothing changes for a buyer who never asked for them. The names are neutral on purpose: nothing in a delivery names LeadMove, the headers included. A custom header of yours with the same name wins.

Most lead-buying systems store body fields, not headers. With the standard payload the reference is the top-level leadId. With a custom body template the body is exactly what you wrote, so the reference goes out only if the template contains {lead_id}, in a field the buyer keeps (a sub-id field, a notes field, whatever they designate). The editor says so while you type when a template lacks it, the starter template includes it, and the buyer's Reporting API card lists any webhook method that does not send it.

A buyer whose system cannot store an extra field can still name the lead: set Buyer reference path (below) to read their own id from their response. The lead outcomes API accepts that id as well as the LD- reference.

Reading the buyer's response

By default any 2xx response counts as delivered. Turn on Buyer response when the buyer's system answers 2xx but says in the body whether it took the lead, for example {"Status":"Errors","Errors":["Duplicate Lead"]}. A lead the buyer turns down is then marked Rejected by buyer and is not billed: the buyer's wallet or credit is given back, the lead does not count toward their caps, and it stays out of their revenue.

Three settings, all read from the response body:

SettingWhat it doesExample
Field path and Accepted valuesWhere the verdict lives, and which values mean the lead was accepted. Paths use dots and [0] for nested values; key and value matching ignore case.Status / Success
Rejection reason path (optional)Where the buyer explains a refusal. Shown on the delivery and in the lead's timeline. If it points to a list, the first item is used.Errors[0]
Buyer reference path (optional)The buyer's own id for the lead. Saved on the delivery as Buyer ref, and accepted as the lead id by the outcomes API.LeadID

Response contains text is the alternative for buyers that answer in plain text: the lead is accepted when the body contains the text you type, wherever it sits.

A 2xx that does not match the rule is a rejection. That includes a response where the field is missing or the body is not JSON: if the buyer answers OK in plain text, use Response contains text. A non-2xx response is still a failure, with retries, as before.

A rejected delivery is final. It is never retried, and the lead is marked failed with a Rejected by buyer - fix and re-run card showing the buyer's reason. Fix the lead there and re-run distribution, or assign it to another buyer. The buyer's page shows an Acceptance rate as soon as one of their deliveries has been rejected.

Check before going live

Preview payload builds the exact JSON this method would send, with your formats and fixed values, using clearly-marked test values — nothing leaves LeadMove. Send a real test to the endpoint actually posts it to the buyer's URL, so their system may create a record; a 2xx response means they accept your leads. With Buyer response on, the test also shows the verdict LeadMove would give that response.

To check the rule itself without posting anything, paste a sample response under Buyer response, or click Use last response to load the last real response this method received. The verdict updates as you type.

When a delivery fails

The lead's delivery history stores the error and the exact body that was posted — open What was sent on the delivery — so you can hand the buyer something concrete:

What you seeWhat happened
HTTP 404: Not Found, HTTP 401: UnauthorizedThe buyer's endpoint answered with a non-2xx status
Timed out after 10sNo response within 10 seconds
URL must use https / URL resolves to a private or reserved addressThe URL isn't a public HTTPS address

Anything that isn't a 2xx counts as a failure and is retried — see Retries and deferrals.

Common questions

Can I use GET or PUT? No. Lead delivery is always POST with a JSON body.

The buyer returns 200 but says nothing arrived. Their endpoint accepted the request, so LeadMove considers it delivered. Open the delivery in the lead's history, copy the posted body, and check it against their spec — usually a field name or a type (quoted string where they expect a number). If their 200 carries a verdict in the body, turn on Buyer response so a refused lead is marked rejected instead of delivered.

The buyer says they rejected the lead, but it shows as delivered. Their endpoint answered 2xx and LeadMove did not read the body. Turn on Buyer response on that delivery method and point it at the field their answer uses. Leads delivered before the change keep their status.

How do I send the buyer's API key? Either as a custom header, or as a static value inside a custom body template. Both are stored with the delivery method.

Do my transforms apply inside a custom body template? Yes, through the Field transforms list on the same panel. The per-field transforms set in the schema apply to the standard payload.

On this page