Inbound and outbound lead records. Each lead is bound to the calling organization (RLS enforced). The shape below matches the live Postgres schema; additional fields available on the single-record GET include AI-qualification metadata (pain_points, timeline, budget_range, ai_reasoning, qualification_completed_at).
/api/v1/leadsreadLiveCursor-paginated. Default limit 50, max 200. Filter by pipeline_stage, source, and created_at range (?start_at=ISO8601&end_at=ISO8601). Cursors are opaque base64url-encoded JSON.
{
"data": [
{
"id": "9f1c8a32-4d65-4e2b-8a91-3c0e7d4b2f18",
"organization_id": "8c2f0a14-0c8a-4a3a-9a4d-3e0f1d6c2a91",
"name": "Sarah Mitchell",
"company": null,
"email": "sarah.mitchell@example.com",
"source": "google_ads",
"lead_score": 87,
"pipeline_stage": "qualified",
"industry": null,
"company_size": null,
"created_at": "2026-04-27T13:22:05.114Z",
"updated_at": "2026-04-27T13:22:09.218Z"
}
],
"next_cursor": "eyJjcmVhdGVkX2F0IjoiMjAyNi0wNC0yN1QxMzoyMjowNS4xMTRaIiwiaWQiOiI5ZjFjOGEzMi00ZDY1LTRlMmItOGE5MS0zYzBlN2Q0YjJmMTgifQ"
}/api/v1/leads/{id}readLiveTenant-scoped: returns 404 if the lead belongs to a different organization. Returns the full record including AI-qualification metadata.
{
"id": "9f1c8a32-4d65-4e2b-8a91-3c0e7d4b2f18",
"organization_id": "8c2f0a14-0c8a-4a3a-9a4d-3e0f1d6c2a91",
"name": "Sarah Mitchell",
"company": null,
"email": "sarah.mitchell@example.com",
"source": "google_ads",
"lead_score": 87,
"pipeline_stage": "qualified",
"industry": "residential",
"company_size": null,
"pain_points": "Recurring basement moisture after spring rain",
"timeline": "this_week",
"budget_range": null,
"ai_reasoning": "High urgency: water damage signal, recent storm event, in-radius",
"qualification_completed_at": "2026-04-27T13:22:09.218Z",
"created_at": "2026-04-27T13:22:05.114Z",
"updated_at": "2026-04-27T13:22:09.218Z"
}/api/v1/leadswriteLiveCreates a new lead. Validates email format if provided. At least one of email, or (name + company), is required. Returns 201 with the created record.
{
"name": "Sarah Mitchell",
"email": "sarah.mitchell@example.com",
"source": "website",
"industry": "residential",
"pipeline_stage": "new"
}{
"id": "9f1c8a32-4d65-4e2b-8a91-3c0e7d4b2f18",
"organization_id": "8c2f0a14-0c8a-4a3a-9a4d-3e0f1d6c2a91",
"name": "Sarah Mitchell",
"company": null,
"email": "sarah.mitchell@example.com",
"source": "website",
"lead_score": 0,
"pipeline_stage": "new",
"industry": "residential",
"company_size": null,
"created_at": "2026-04-27T13:22:05.114Z",
"updated_at": "2026-04-27T13:22:05.114Z"
}/api/v1/leads/{id}writeLivePartial update. Whitelisted fields only: name, company, email, source, lead_score (0–100), pipeline_stage, industry, company_size, pain_points, timeline, budget_range, ai_reasoning. Every change is recorded in /audit-log as a `lead.updated` event with a per-field diff.
{
"pipeline_stage": "won",
"lead_score": 92
}{
"id": "9f1c8a32-4d65-4e2b-8a91-3c0e7d4b2f18",
"organization_id": "8c2f0a14-0c8a-4a3a-9a4d-3e0f1d6c2a91",
"name": "Sarah Mitchell",
"company": null,
"email": "sarah.mitchell@example.com",
"source": "google_ads",
"lead_score": 92,
"pipeline_stage": "won",
"updated_at": "2026-04-27T18:55:42.001Z"
}/api/v1/leads/{id}/scorewriteRoadmapForce a re-score using the latest signals. Returns the new score and explanation.
{
"id": "9f1c8a32-4d65-4e2b-8a91-3c0e7d4b2f18",
"lead_score": 91,
"ai_reasoning": "Recent storm event in service radius (+0.18); phone validated (+0.10); fast form completion (+0.08)",
"scored_at": "2026-04-27T19:01:15.214Z"
}/api/v1/leads/{id}/push-to-crmwriteRoadmapManually trigger a CRM push if the automatic push failed or was skipped. Idempotent: re-pushing returns the existing external_id without creating a duplicate record.
{
"id": "9f1c8a32-4d65-4e2b-8a91-3c0e7d4b2f18",
"crm_sync": {
"vendor": "jobnimbus",
"external_id": "jn_117422",
"status": "ok",
"last_synced_at": "2026-04-27T19:03:42.118Z"
}
}