Light AIDocs Beta Open Light AI

Leads in

Inbound leads

Send estimate requests into Light AI from any form, ad platform or automation tool, with or without code.

Every request becomes a lead. A lead with a street address also becomes a project in the contractor's pipeline. Repeat requests never create duplicates: the same address and service on an open lead becomes a touch (lead.touched).

PathUse it forCredential
POST /v1/leadsYour own code, or any tool that can set an API key.API key with leads:write
POST /v1/inbound/{source_id}HighLevel workflows, Zapier, Make, n8n, form tools. Any JSON shape (mapped).Source secret in a header
POST /v1/inbound/{source_id}/{token}Senders that cannot set headers (Sweep&Go, some form tools).Secret token in the URL
POST /v1/inbound/google-ads/{source_id}Google Ads lead form extensions. Setup.Google's google_key
n8n templatesMeta Lead Ads and Google Local Services Ads. Setup.API key

Required fields#

A name plus an e-mail or a phone. Everything else is optional. A lead without a street address waits for one (needs_address: true) and converts to a project when you add it with PATCH /v1/leads/{id}. (The contractor's own website request form always asks the homeowner for an e-mail, a phone and the address; that does not change what the API requires.)

Inbound sources#

An inbound source is a receive-only credential: it can create leads and nothing else (it can never read data or change existing clients). The owner creates one in Settings → Developers → Inbound (at most 10 per organization; revoked sources do not count) and picks a mode.

Send the secret as Authorization: Bearer lai_whin_… or X-Light-AI-Source-Secret: lai_whin_….

curl
curl -X POST "https://api.light-ai.app/v1/inbound/src_…" \
  -H "Authorization: Bearer $INBOUND_SOURCE_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
  "contact": { "first_name": "Dana", "last_name": "Reyes", "email": "dana@example.com", "phone": "512-555-0134" },
  "address": { "line1": "4102 Oak Hollow Dr", "postal_code": "78731" },
  "message": "Roofline, warm white"
}'

This is the POST /v1/leads body shape, which every source accepts as is. A sender with its own field names (name, street, zip, …) needs the source's field map first: see Field mapping.

URL mode#

For senders that cannot set headers, the secret token is part of the URL: https://api.light-ai.app/v1/inbound/src_…/lai_whu_…. The URL itself is the credential and shows up in the sender's logs, so keep it private and rotate it if it leaks.

Responses#

Inbound sources always get a receipt ({"object":"receipt","status":"received"}), never the lead, so a sender cannot probe for existing customers. Validation errors name the field pointer without echoing values.

Capture and map#

A source accepts either the POST /v1/leads body or any JSON your tool already sends. To map a custom shape:

  1. Open the source in Settings → Developers → Inbound and click Capture a sample.
  2. Send one real request from your tool. Light AI shows the received body (kept 24 hours).
  3. Click each value onto a Light AI field. Optional transforms: trim, lowercase, phone to +1 format, first name(s) or last word of a full name, join a list.
  4. Save. Every later request uses the map. Unmapped values can go to custom_fields.
Light AI fieldNotes
contact.full_name, contact.first_name, contact.last_nameMap a full name to contact.full_name, or split it with the first-word and last-word transforms.
contact.email, contact.phoneAt least one. Phones are normalized to E.164.
address.formatted or address.line1, line2, city, region, postal_code, countryA country name is normalized (United States → US).
service_interestchristmas_lights, permanent_lighting, landscape_lighting or free text (kept raw).
message, source_detail, submitted_at
external_ref.id, external_ref.namespaceYour record id: a repeat with the same id replays the original lead.
consent.email, consent.sms, consent.marketing, consent.textStored verbatim for your compliance records.
attribution.utm_source, utm_medium, utm_campaign, gclid, campaign_id
custom_fields.<key>Anything else (letters, digits, _).

Only the contact can refuse a lead. An unusable address or optional value is dropped (its raw value kept in custom_fields.inbound_*) rather than losing the request.

Limits#

Each source takes 60 requests a minute by default and a daily limit (default 500; at most 2,000 in header mode and 500 in URL mode). Above a limit the answer is 429 with Retry-After. Organizations also have a daily cap on created leads across all channels.

With an API key#

curl -X POST "https://api.light-ai.app/v1/leads" \
  -H "Authorization: Bearer $LIGHT_AI_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "source": "zapier",
  "contact": {
    "first_name": "Dana",
    "last_name": "Reyes",
    "phone": "+1 512 555 0134"
  },
  "address": {
    "line1": "4102 Oak Hollow Dr",
    "postal_code": "78731",
    "country": "US"
  },
  "service_interest": "permanent_lighting",
  "external_ref": {
    "system": "zapier",
    "id": "form-entry-88"
  }
}'

A key without leads:read also gets a receipt (202). Keep its rcpt_… id: you can add the address later with PATCH /v1/leads/rcpt_….

Paused organizations#

While an organization is paused its leads are held (held: true) and convert automatically when it resumes. Nothing is lost.