Get started
Quickstart
Create a test key, send your first lead, and receive your first signed webhook.
You need an organization on Pro or Enterprise (or a Pro trial), and you must be its owner (or an admin the owner allowed). Test keys never touch real customers, send no e-mails and spend no credits.
1. Create a test key#
In Light AI open Settings → Developers → API keys and create a key with the Integration partner preset. Choose Test. The key (lai_test_…) is shown once: store it in your secret manager.
export LIGHT_AI_API_KEY="lai_test_…"2. Check the key#
GET /v1/me returns the organization, the key's effective scopes and its limits.
curl "https://api.light-ai.app/v1/me" \
-H "Authorization: Bearer $LIGHT_AI_API_KEY"const response = await fetch('https://api.light-ai.app/v1/me', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.LIGHT_AI_API_KEY}`,
},
});
const data = await response.json();import os, requests
response = requests.get(
"https://api.light-ai.app/v1/me",
headers={
"Authorization": f"Bearer {os.environ['LIGHT_AI_API_KEY']}",
},
)
data = response.json()3. Send a lead#
A lead needs a name and an e-mail or phone. Add an address so Light AI can create the project. Send an Idempotency-Key so a retry never creates a second lead.
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": "api",
"contact": {
"first_name": "Dana",
"last_name": "Reyes",
"email": "dana@example.com",
"phone": "+1 512 555 0134"
},
"address": {
"line1": "4102 Oak Hollow Dr",
"city": "Austin",
"region": "TX",
"postal_code": "78731",
"country": "US"
},
"service_interest": "christmas_lights",
"message": "Roofline and the two front trees, warm white.",
"external_ref": {
"system": "mycrm",
"id": "lead-1001"
}
}'const response = await fetch('https://api.light-ai.app/v1/leads', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LIGHT_AI_API_KEY}`,
'Idempotency-Key': crypto.randomUUID(),
'Content-Type': 'application/json',
},
body: JSON.stringify({
"source": "api",
"contact": {
"first_name": "Dana",
"last_name": "Reyes",
"email": "dana@example.com",
"phone": "+1 512 555 0134"
},
"address": {
"line1": "4102 Oak Hollow Dr",
"city": "Austin",
"region": "TX",
"postal_code": "78731",
"country": "US"
},
"service_interest": "christmas_lights",
"message": "Roofline and the two front trees, warm white.",
"external_ref": {
"system": "mycrm",
"id": "lead-1001"
}
}),
});
const data = await response.json();import os, uuid, requests
response = requests.post(
"https://api.light-ai.app/v1/leads",
headers={
"Authorization": f"Bearer {os.environ['LIGHT_AI_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"source": "api",
"contact": {
"first_name": "Dana",
"last_name": "Reyes",
"email": "dana@example.com",
"phone": "+1 512 555 0134"
},
"address": {
"line1": "4102 Oak Hollow Dr",
"city": "Austin",
"region": "TX",
"postal_code": "78731",
"country": "US"
},
"service_interest": "christmas_lights",
"message": "Roofline and the two front trees, warm white.",
"external_ref": {
"system": "mycrm",
"id": "lead-1001"
}
},
)
data = response.json()The answer is 201 with the lead (lead_…) and its project (prj_…). The same address and service again becomes a touch on the open lead (200), and the same external_ref replays the original lead.
4. Receive a webhook#
Point an endpoint at a public HTTPS URL (for a quick look, a request-bin service works). Create it in Settings → Developers → Webhooks, or with a key that holds the opt-in webhooks:manage scope:
curl -X POST "https://api.light-ai.app/v1/webhook_endpoints" \
-H "Authorization: Bearer $LIGHT_AI_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"url": "https://hooks.example.com/light-ai",
"events": [
"lead.created",
"quote.published",
"quote.approved",
"quote.declined"
]
}'const response = await fetch('https://api.light-ai.app/v1/webhook_endpoints', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LIGHT_AI_API_KEY}`,
'Idempotency-Key': crypto.randomUUID(),
'Content-Type': 'application/json',
},
body: JSON.stringify({
"url": "https://hooks.example.com/light-ai",
"events": [
"lead.created",
"quote.published",
"quote.approved",
"quote.declined"
]
}),
});
const data = await response.json();import os, uuid, requests
response = requests.post(
"https://api.light-ai.app/v1/webhook_endpoints",
headers={
"Authorization": f"Bearer {os.environ['LIGHT_AI_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"url": "https://hooks.example.com/light-ai",
"events": [
"lead.created",
"quote.published",
"quote.approved",
"quote.declined"
]
},
)
data = response.json()Save the secret (whsec_…): it is returned once. Send a ping to check delivery, then verify the signature on every request you receive.
curl -X POST "https://api.light-ai.app/v1/webhook_endpoints/we_…/test" \
-H "Authorization: Bearer $LIGHT_AI_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'const response = await fetch('https://api.light-ai.app/v1/webhook_endpoints/we_…/test', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LIGHT_AI_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
});
const data = await response.json();import os, requests
response = requests.post(
"https://api.light-ai.app/v1/webhook_endpoints/we_…/test",
headers={
"Authorization": f"Bearer {os.environ['LIGHT_AI_API_KEY']}",
},
json={},
)
data = response.json()5. Simulate a quote#
Test mode can play the homeowner. Publish a test quote for your test lead, then approve it: your endpoint receives quote.published and quote.approved exactly as in live mode.
curl -X POST "https://api.light-ai.app/v1/test_helpers/leads/lead_…/publish_quote" \
-H "Authorization: Bearer $LIGHT_AI_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'const response = await fetch('https://api.light-ai.app/v1/test_helpers/leads/lead_…/publish_quote', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LIGHT_AI_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
});
const data = await response.json();import os, requests
response = requests.post(
"https://api.light-ai.app/v1/test_helpers/leads/lead_…/publish_quote",
headers={
"Authorization": f"Bearer {os.environ['LIGHT_AI_API_KEY']}",
},
json={},
)
data = response.json()curl -X POST "https://api.light-ai.app/v1/test_helpers/leads/lead_…/approve_quote" \
-H "Authorization: Bearer $LIGHT_AI_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'const response = await fetch('https://api.light-ai.app/v1/test_helpers/leads/lead_…/approve_quote', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LIGHT_AI_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
});
const data = await response.json();import os, requests
response = requests.post(
"https://api.light-ai.app/v1/test_helpers/leads/lead_…/approve_quote",
headers={
"Authorization": f"Bearer {os.environ['LIGHT_AI_API_KEY']}",
},
json={},
)
data = response.json()6. Go live#
Create a Live key (lai_live_…) with only the scopes you need, and a live endpoint. Live and test objects never mix: a test key cannot read live data and the other way around.