Developers

Send leads in. Get events out.

A small REST API, signed webhooks for forms, messages and calls, and outgoing events for your own tools.

REST API

Versioned in the path. JSON in, JSON out.

Authentication

Make a key in Settings → Developers. It is shown once and stored only as a fingerprint. Send it on every request:

Authorization: Bearer lok_<business>_<secret>

120 requests a minute per key. Over that you get 429 with Retry-After.

Retries without doubles

Add an Idempotency-Key header. Sending the same key again returns the first answer (with Idempotent-Replayed: true) and changes nothing.

Create a lead — POST /api/v1/leads

curl -X POST https://<your-host>/api/v1/leads \
  -H "Authorization: Bearer lok_..." \
  -H "Idempotency-Key: form-2026-10-24-0042" \
  -H "Content-Type: application/json" \
  -d '{"name":"Jordan Lee","email":"jordan@example.com","service":"AC repair",
       "need":"AC stopped cooling","source":"website_form","consent":{"email":true}}'

Needs an email or a phone. 201 creates a lead; 200 means it matched someone you already have (same email or phone) and the inquiry was added to them — never a duplicate. Consent is only “granted” when you say so; otherwise replies go on the channel they used.

Update a lead — PATCH /api/v1/leads/:id

Fields: name, company, city, service, need, campaign. Stage, owner and consent change only inside Lead Growth OS.

List leads — GET /api/v1/leads

The 50 newest: id, name, state, source, createdAt.

Messages and calls — POST /api/v1/events

{"type":"message","channel":"sms","from":"+16175550101","body":"Is Thursday ok?"}
{"type":"call","from":"+16175550101","answered":false,"voicemail":"Call me back"}

A reply stops follow-ups that stop on reply. “STOP” opts the person out on every channel. A missed call becomes a recovery item.

Webhooks in

For tools that push events: forms, ad platforms, email, SMS, WhatsApp, phone systems.

POST JSON to /api/hooks/<kind>?ws=<business>, where kind is form, ads, email, sms, whatsapp or call. Bodies are the same as the API above (form/ads: a lead; email/sms/whatsapp: from + body; call: from + answered).

Signature

Make a signing secret in Settings → Developers, then sign each request:

t  = unix seconds now
v1 = hex( HMAC-SHA256( secret, t + "." + rawBody ) )
x-leados-signature: t=<t>,v1=<v1>
x-leados-delivery: <your unique id>   (optional — a repeat is ignored)

Unsigned, wrongly signed, or more than 5 minutes old (replay) is refused with 401. Every delivery — processed, rejected, duplicate or error — appears in the delivery log.

Webhooks out

Subscribe a URL to events; we POST them, signed the same way.

Events: lead.created, message.received, call.missed, lead.opted_out.

{"id":"evt_...","type":"lead.created","created":"2026-10-24T10:00:00.000Z",
 "business":"w-...","data":{"id":"l_...","name":"Jordan Lee","source":"website_form"}}

Answer with any 2xx. Anything else, or no answer in 8 seconds, is retried after 1, 5, 15, 60 and 240 minutes, then marked failed in Queue health, where it can be retried by hand. Delivery runs while background work is on. Receivers must use https.