Lead billing API
Validate leads for billing, mark them not billable, and read a buyer's billing statement from your own systems, for buyers billed after validation.
For buyers you bill after validation, your own systems can validate a delivered lead (it bills) or mark it not billable (it doesn't), and read a buyer's billing statement for a period. Use an organization key with the leads:bill scope.
Your buyer can validate its own leads too, with its reporting key and accepted on the lead outcomes API. Both land in the same place.
Authentication
Create the key in Settings → Developers → Create key and tick leads:bill (API keys). Send it as a bearer token:
| Endpoint | Method | Scope |
|---|---|---|
/api/v1/leads/{leadId}/billing | POST, GET | leads:bill |
/api/v1/buyers/{id}/billing | GET | leads:bill |
{leadId} is the lead reference, LD-XXXXXX. It is on the lead page and in every webhook your buyers receive. {id} is a buyer's ID or slug, as on the buyers API.
Deciding a lead
| Field | Required | What it is |
|---|---|---|
status | yes | validated (the lead bills) or not_billable (it doesn't). |
reason | no | Why, with not_billable, up to 200 characters. Shown on the lead and in the statement. |
buyer | only if the lead went to more than one buyer billed after validation | The buyer's ID or slug. Ignored otherwise. |
Sending the same decision again returns changed: false and writes nothing. The API never reopens a decision: asking for the opposite of what was decided returns billing_already_decided. To undo one, reopen the lead in the app (invoice buyers only).
On a prepaid buyer, not_billable refunds the lead's price to the balance, once.
Reading a lead
GET /api/v1/leads/{leadId}/billing lists the lead's deliveries that carry a billing status:
A lead delivered only to buyers billed on delivery answers "deliveries": [].
billing field | What it is |
|---|---|
status | pending (awaiting validation), validated or not_billable. |
dueAt | While pending: when the validation window ends. |
decidedAt | When it was decided. |
source | api (an organization key), buyer_api (the buyer's reporting key), admin or bulk (in the app), expiry (the window ended). |
reason | The reason given with not_billable, if any. |
A buyer's statement
The same statement as the buyer's Billing tab, for a period.
| Parameter | What it is |
|---|---|
from, to | Calendar days, YYYY-MM-DD, both included, in your organization's timezone. Up to 366 days. Omitted: this month. |
status | pending, validated or not_billable: filters the rows. The totals always cover the whole period. |
limit | Rows per page, 1 to 100. Default 50. |
cursor | The nextCursor of the previous page. |
- Rows are newest delivery first.
nextCursorisnullon the last page. validatedcounts what is billed: a validated lead refunded by an accepted dispute leaves the total, and its row saysdisputeAccepted: true.outcomeis what the buyer reported doing with the lead (in_progress,sold,lost), for reference. It never changes what is billed.- A buyer with nothing in the period answers empty
rows, never an error.
Errors
Errors come back as JSON. The code is the contract; messages are free to improve.
| HTTP | code | When |
|---|---|---|
| 400 | validation_error | The body is not valid JSON, or not an object |
| 401 | unauthorized | No Authorization header, wrong scheme, or a key we don't recognise |
| 401 | key_revoked | The key was revoked |
| 403 | insufficient_scope | The key doesn't hold leads:bill |
| 404 | lead_not_found | No lead with that reference in your account |
| 404 | buyer_not_found | No buyer with that ID or slug in your account |
| 405 | method_not_allowed | Wrong verb. The Allow header names the right one |
| 409 | billing_already_decided | The lead is already decided the other way. status and decidedAt in the error say how |
| 422 | billing_not_enabled | The lead has no delivery to a buyer billed after validation (or none to the buyer you named) |
| 422 | buyer_required | The lead went to more than one buyer billed after validation. buyers lists them; send one as buyer |
| 422 | validation_error | A field or parameter is wrong: field names it |
| 429 | rate_limited | Too many requests. Retry-After says how long to wait |
| 500 | internal_error | Our side. Safe to retry |
Debugging: every request is logged
Every request made with a recognised key is recorded, accepted or refused: the path, the body, the status and error code, and how long it took, along with the lead it was about. When a call does not behave as you expect, tell us when it was made and which lead it named: the answer is on record. Requests are kept 180 days.
Calls your buyer makes with its own reporting key show on the buyer's page, under Reporting API → Recent requests.
Common questions
Can a key validate leads for a buyer billed on delivery?
No. Those leads bill at once and there is nothing to validate: the answer is billing_not_enabled.
What if nobody decides?
When the buyer's validation window ends, the lead is validated or marked not billable automatically, as set on the buyer. The source then reads expiry.
Do the statement totals match the app? Yes. The Billing tab and this endpoint read the same computation, for the same period in the same timezone.
Lead outcomes API
Report what became of a lead you received, in progress, sold or lost, with your own stage name and the sale amount, from your CRM or dialer.
Connect Claude to your workspace
Pick where you'll use LeadMove, Claude, ChatGPT, Claude Code, Cursor or another tool, and choose what it may do.