Leads

Base address: https://zqawbbylldymdfzineqq.supabase.co/functions/v1/pro-api

Method and pathScopeDoes
GET /v1/leadsleads:readList, sync or look up leads
GET /v1/leads/{id}leads:readOne lead
POST /v1/leads/{id}/acceptleads:writeAccept (charges like the Accept button)
POST /v1/leads/{id}/declineleads:writeDecline any lead you have not accepted, free
PATCH /v1/leads/{id}leads:writeStatus, job total, paid amount, external ids, note, follow-up date
PUT /v1/leads/{id}/statusleads:writeSame as PATCH (for code written for other platforms)
GET /v1/leads/{id}/timelineleads:readEvery event on the lead, in order
GET /v1/leads/{id}/messagesmessages:readSee Messages
POST /v1/leads/{id}/messagesmessages:writeSee Messages
POST /v1/leads/{id}/credit-requestbilling:writeSee Charges and credits

List

GET /v1/leads
QueryWhat it does
(none)Your most recently changed leads, newest first. Good for Zapier polling.
since (or updated_since)Leads created or changed after this ISO time, oldest change first, with paging. Use for sync.
cursorThe next_cursor from the last page.
statusOnly this status: delivered, contacted, appointment, hired, job_done, not_hired, unreachable.
limit1 to 100. Default 100.
phone, emailLeads whose contact you can see with this phone (any format) or email.
external_idsystem:value, for example servicetitan:12345.
customer_idAll leads from one customer.
{"leads":[...],"has_more":true,"next_cursor":"MjAyNi0xMC0yMlQxNjoyMDo0NS4wMDIrMDA6MDB8NmIxYy4uLg","next_since":"2026-10-22T16:20:45.002Z"}
  • With since or cursor: when has_more is true, call again with cursor=next_cursor until it is false. Then save next_since for your next sync. Declined leads are included (declined: true, no contact) so your system can close them.
  • Without since: next_since is the time of the call, so you can switch to since syncing from there.
  • Lookups return up to 100 matches, newest first, with has_more: false. Phone and email only match leads whose contact details you can already see.

Accept

POST /v1/leads/{id}/accept

Returns the full lead with contact details. Accepting twice is safe. It charges the lead price exactly as the app does, so only accept leads you mean to take. A lead you declined cannot be accepted.

Decline

POST /v1/leads/{id}/decline
Content-Type: application/json

{"reason":"too_far"}

reason (optional): too_far, not_my_service, too_busy, job_too_small, job_too_large, other. Only direct and related leads you have not accepted can be declined (409 otherwise). Declining is free.

Update

PATCH /v1/leads/{id}
Content-Type: application/json

{
  "status": "hired",
  "job_total_cents": 185000,
  "paid_cents": 0,
  "reason": "booked for Tuesday",
  "note": "Gate code 1234. Dog in yard.",
  "external_ids": {"servicetitan": "88213", "jobber": null},
  "follow_up_at": "2026-10-28T16:00:00Z"
}

Send any of these fields; leave out what you are not changing.

FieldRules
statusOne of our statuses, or a name other software uses (scheduled, completed, invoice_paid, customer_cancel...) which we map for you. Forward moves only (409 otherwise). The lead must be accepted.
job_total_cents, paid_centsWhole cents, 0 to 100,000,000. The lead must be accepted. invoice_total_cents is accepted as another name for job_total_cents.
reasonUp to 60 characters, saved with the status change.
noteYour internal note, up to 2,000 characters. null or "" clears it. Customers never see it.
external_idsYour own ids: keys are lowercase letters, digits or _ (up to 30), values are text up to 100. Merged with what is there; null removes a key. Up to 10.
follow_up_atA future time within 400 days, or null to clear. Same as the follow-up date in the app.

Returns the full lead. A status update never charges or refunds you. duplicate or spam is not a status: ask for a credit instead.

Timeline

GET /v1/leads/{id}/timeline
{"events":[
  {"type":"lead.received","at":"2026-10-22T16:04:11Z"},
  {"type":"charge.created","at":"2026-10-22T16:04:11Z","charge_id":"chg_311","amount_cents":4500,"status":"pending"},
  {"type":"lead.accepted","at":"2026-10-22T16:06:02Z"},
  {"type":"message.sent","at":"2026-10-22T16:07:40Z","message_id":"msg_902"},
  {"type":"lead.status","at":"2026-10-22T18:30:00Z","from":"delivered","to":"hired","source":"api","reason":"booked for Tuesday","job_total_cents":185000,"paid_cents":null,"note":null}
]}