Create a lead
POST /api/leads: create a lead with a per firm API key.
POST /api/leadsCreates a lead. With a per firm API key (scope leads:write) the payload is validated, the lead
lands in the firm's first active pipeline stage that isn't final (stages a firm retired are
skipped), and the created lead is returned in full. A
lead.created webhook fires.
Request body
JSON. first_name, last_name, and source are required, plus at least one of email /
phone. A lead has to be reachable.
| Field | Type | Notes |
|---|---|---|
first_name | string | Required. |
last_name | string | Required. |
source | string | Required. Where the lead came from. |
email | string | Required if no phone. |
phone | string | Required if no email. |
assignee_id | string | null | Optional firm member (UUID) to assign. |
data | object | Optional contact fields, by key (case type, etc.). Unknown keys are ignored. See Fields in data. |
Idempotency
Send an Idempotency-Key header to make a create safe to retry: a repeat with the same key
returns the original lead instead of creating a second. Use a fresh unique value (e.g. a UUID) per
logical create.
Idempotency-Key: 9d2b7c1e-5a4b-4c3d-8e2f-1a2b3c4d5e6fExample
curl https://app.lawfficient.com/api/leads \
-H "Authorization: Bearer $LAWFFICIENT_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 9d2b7c1e-5a4b-4c3d-8e2f-1a2b3c4d5e6f" \
-d '{
"first_name": "Ada",
"last_name": "Lovelace",
"email": "ada@example.com",
"source": "partner_referral",
"data": { "caseType": "Family" }
}'{
"id": "3f8c1e2a-1b2c-4d5e-8f90-abcdef012345",
"first_name": "Ada",
"last_name": "Lovelace",
"email": "ada@example.com",
"phone": "",
"source": "partner_referral",
"status": { "key": "new", "name": "New" },
"assignee_id": null,
"archived": false,
"created_at": "2026-06-24T10:00:00.000Z",
"last_activity_at": "2026-06-24T10:00:00.000Z",
"data": { "caseType": "Family" }
}Errors use the error envelope: 422 invalid_request (e.g. no contact method, or an
invalid data value), 403 insufficient_scope (the key lacks leads:write), 409 no_open_stage
(the firm has no active pipeline stage that isn't final), 409 stage_retired (the landing stage was
retired while the request ran; retry it).
Inbound ingestion shares this path. Authenticate with a per source ingestion key instead of
an API key and POST /api/leads is the inbound webhook: it takes the source's mapped fields,
dedupes on externalId, and returns { status, leadId }. See the
lead ingestion spec.