Expert AI Labs
Back to API overview

Leads

Live

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).

GET/api/v1/leadsreadLive

List leads

Cursor-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.

Example response
{
  "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"
}
GET/api/v1/leads/{id}readLive

Fetch a lead

Tenant-scoped: returns 404 if the lead belongs to a different organization. Returns the full record including AI-qualification metadata.

Example response
{
  "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"
}
POST/api/v1/leadswriteLive

Create a lead

Creates a new lead. Validates email format if provided. At least one of email, or (name + company), is required. Returns 201 with the created record.

Example request body
{
  "name": "Sarah Mitchell",
  "email": "sarah.mitchell@example.com",
  "source": "website",
  "industry": "residential",
  "pipeline_stage": "new"
}
Example response
{
  "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"
}
PATCH/api/v1/leads/{id}writeLive

Update a lead

Partial 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.

Example request body
{
  "pipeline_stage": "won",
  "lead_score": 92
}
Example response
{
  "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"
}
POST/api/v1/leads/{id}/scorewriteRoadmap

Re-score a lead

Force a re-score using the latest signals. Returns the new score and explanation.

Example response
{
  "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"
}
POST/api/v1/leads/{id}/push-to-crmwriteRoadmap

Push a lead to the configured CRM

Manually 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.

Example response
{
  "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"
  }
}