REST API and instant triggers
Keys, leads, appointments, calls, messages, and event subscriptions for Zapier, Make, and n8n.
The LeadBind REST API is plain JSON over HTTPS. It is what Zapier, Make, n8n and your own code use to read your leads, appointments, calls and texts, to create and update leads, to book and cancel, and to hear about events the moment they happen.
Base URL: https://leadbind.org/api/v1
Keys
Every request carries an API key in a header:
Authorization: Bearer lb_live_...
X-API-Key: lb_live_... works too. Create a key under Menu, then Lead sources, then AI access (leadbind.org/agent/ai-access). See Keys and permissions.
A key belongs to one account, and every request acts on that account only. There is no account id to send, and no way to reach another account with any id you send. An id from another account answers 404, the same as an id that does not exist.
| Scope | What it allows |
|---|---|
read |
Every GET, and subscribing a URL to events (a subscription only receives what a read key could already read) |
write |
Creating and updating leads, notes, booking and cancelling |
There is no way to send a text or place a call through the REST API. That is on purpose: an automation built in a hurry could reach people outside their consent or outside calling hours. Use the app, or MCP with the dial scope, which carries those checks.
A revoked or expired key answers 401 straight away.
Responses and errors
A success is { "data": ... } with a 2xx status. A list also has next_offset: pass it back as offset for the next page, and stop when it is null.
A failure is always this shape, with a real HTTP status:
{ "error": { "code": "not_found", "message": "Lead not found." } }
| Status | code |
Means |
|---|---|---|
| 400 | invalid_request |
A field is missing, the wrong type, too long, or not one we take. The message names it. Unknown fields are refused, never ignored. |
| 401 | unauthorized |
No key, or the key is not valid (revoked, expired, or copied incompletely) |
| 402 | payment_required |
Event subscriptions need an active plan |
| 403 | forbidden |
The key lacks the scope this route needs |
| 404 | not_found |
No such record on your account |
| 409 | conflict |
That time was just taken. Read availability again. |
| 413 | payload_too_large |
The body is over 32 KB |
| 422 | rejected |
LeadBind understood the request and said no, for example a booking without consent. The message says why. |
| 429 | rate_limited |
Slow down. Wait the Retry-After seconds. |
| 502 | outcome_unknown |
The action may or may not have happened, for example a call that got no confirmation back. Do not retry. Read back first. |
| 502 | unavailable |
A provider refused or failed the request. Nothing is known to have happened. |
| 503 | unavailable |
A read or save could not be completed just now. It is never an empty list pretending to be an answer. Try again. |
Rate limits
Limits are per key, not per address, so many accounts sending through Zapier never share a budget: 240 reads and 60 writes a minute per key.
Every answer says where you stand: RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset (seconds until the window resets). A 429 carries Retry-After with the real seconds left, so you can wait exactly that long.
The whole API is described in OpenAPI 3.1 at https://leadbind.org/api/v1/openapi.json, which needs no key. Import it into Zapier, Make, n8n or any agent framework.
Safe retries
Send an Idempotency-Key header on any write. A retry with the same key returns the first answer instead of creating a second lead or a second booking.
Account
GET /me (read)
The connection test. Returns your account's name and the key's scopes.
curl https://leadbind.org/api/v1/me -H "Authorization: Bearer $LEADBIND_KEY"
{ "data": { "account": { "id": "...", "name": "Lopez Roofing", "timezone": "America/Chicago" }, "key": { "id": "...", "scopes": ["read", "write"] } } }
Leads
GET /leads (read)
Your leads, newest first. Optional stage, limit (1 to 100, default 25) and offset.
To find one person, send phone (matches the last ten digits exactly) or email (matches the address exactly, case does not matter).
GET /leads/{id} (read)
One lead in full, with its recent calls and appointments. Calls carry an outcome and a short written summary, never a recording or a transcript.
POST /leads (write)
Create a lead, or update the one you already have with the same phone or email. Phone is checked first, then email.
| Field | Notes |
|---|---|
name |
Required to create. Up to 120 characters. |
phone |
Required to create. A US number. |
email |
Optional |
notes |
On a new lead, its notes. On an existing lead, added as a new note, never a replacement. |
stage |
One of the base pipeline stages: new, contacted, callback, hot, booked, sold, lost. A custom stage you added to your board is set in the app. |
address, city, state, zip |
An address puts the lead on the door-mode map |
source |
Where it came from, for your reports. Defaults to api. |
curl -X POST https://leadbind.org/api/v1/leads \
-H "Authorization: Bearer $LEADBIND_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: form-4471" \
-d '{"name":"Maria Lopez","phone":"8325550134","email":"maria@example.com","source":"facebook-roofing"}'
201 with {"data":{"id":"...","created":true}} for a new lead, or 200 with "created": false and the fields that changed for an existing one.
A new lead alerts you and fires lead.created, like a lead from any other door. It does not start a speed-to-lead call. To have LeadBind call a new lead within seconds, send it to your lead webhook instead, which checks consent and calling hours first.
PATCH /leads/{id} (write)
Change only the fields you send. notes here replaces the notes; to add one, use the next route. A stage change fires lead.status_changed.
POST /leads/{id}/notes (write)
{"note": "..."}, up to 1000 characters. Added with a timestamp; every earlier note is kept.
Appointments
GET /availability (read)
Open slots for the next two weeks, grouped by date. Optional timezone (for example America/Chicago) and calendar (a calendar's short name).
GET /appointments (read)
Upcoming appointments from now, in time order. Optional from and to (dates such as 2026-11-01; a bare to date includes that whole day), lead_id, limit, offset.
POST /appointments (write)
Book a slot that GET /availability returned.
| Field | Notes |
|---|---|
start_time |
Required. The slot's start, exactly as availability returned it. |
name, email, phone |
Required. The confirmation goes to this email. |
consent |
Required. true only when the customer really agreed to be contacted. LeadBind refuses false. |
timezone |
Optional. The zone start_time is in. |
The same checks as your public booking page run: open hours, double booking, minimum notice. A time that was just taken answers 409.
POST /appointments/{id}/cancel (write)
The booking is kept as canceled, its slot opens again, and its calendar event is removed. Cancelling one that is already canceled succeeds with already_canceled: true.
Calls and texts
GET /calls (read)
Recent calls, newest first: direction, outcome, duration, who called, the linked lead and appointment, and a short summary. Optional direction (inbound or outbound), limit, offset. Never a recording or a transcript.
GET /messages (read)
Your text messages, newest first. Optional phone (the other person), direction, limit, offset.
Instant triggers
Subscribe a URL to an event and LeadBind posts to it the moment the event happens. This is what powers instant triggers in Zapier, Make and n8n.
POST /hooks (read)
curl -X POST https://leadbind.org/api/v1/hooks \
-H "Authorization: Bearer $LEADBIND_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://hooks.zapier.com/hooks/standard/123/abc/","event":"call.missed"}'
Send url (or target_url) and event, or events as a list. The URL must be public https. Answers 201 with the subscription's id and its signing secret. The secret is shown once, on this answer only.
Subscriptions made here appear on your Developer page, where you can see deliveries, send a test, or pause one.
DELETE /hooks/{id} (read)
Removes a subscription made through the API. A subscription you set up by hand in the app is not removable here.
GET /hooks/samples?event=... (read)
A list with one example delivery for that event, in exactly the shape a real delivery has. It is your latest real one when you have had that event, otherwise a written example marked "sample": true.
Events
| Event | When |
|---|---|
lead.created |
A new lead arrives from any door: the lead webhook, a website form, a chat, a landing page, a first-time booker, an inbound caller, the app, or this API |
lead.status_changed |
A lead changes stage |
call.completed |
A call finishes |
call.missed |
An inbound call ends with nobody talking to the caller |
message.received |
A text comes in. A STOP reply is included with optOut: true. |
appointment.booked |
An appointment is booked |
appointment.updated |
An appointment is moved, corrected, confirmed, done, or a no-show |
appointment.cancelled |
An appointment is cancelled, from any place |
payment.received |
A customer payment goes through |
deal.won / deal.lost |
A deal is marked won or lost |
What a delivery looks like
{
"id": "6f1c2a9e-0d3b-4c55-9a8e-2f7d1b0c4e11",
"event": "call.missed",
"data": { "id": "...", "direction": "inbound", "callerPhone": "+18325550134", "durationSeconds": 4, "outcome": "hung_up_early", "leadId": "..." },
"sent_at": "2026-11-10T15:04:05.000Z"
}
id is the same for every URL that hears this event and for every retry of it, so dedupe on it. Each request also carries:
| Header | Value |
|---|---|
X-LeadBind-Event |
The event name |
X-LeadBind-Delivery |
One id per URL per event, the same on every retry |
X-LeadBind-Signature |
sha256= and the HMAC SHA-256 of the raw body, keyed with the subscription's secret |
Check the signature against the raw body before you trust a delivery:
const crypto = require("crypto");
const expected = "sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.headers["x-leadbind-signature"]));
Retries and stopping
Answer with any 2xx within five seconds. Anything else is retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours, then given up.
Answer 410 Gone and LeadBind stops at once and switches that subscription off. That is how Zapier tells a sender a Zap was turned off.
Recordings, transcripts and call summaries are never in a delivery.