LeadEnginesLeadEngines/API Reference

API Reference

Full programmatic access to your LeadEngines workspace β€” contacts, deals, tags, workflows, messaging, calls, and more.

Base URLhttps://app.leadengines.ai/api/v1
AI / LLM?GET /api/v1/docsβ€” plain-text version of this page, no auth required.

Authentication

All requests must include an Authorization header with your API key. Create API keys under Settings β†’ API Keys in your workspace.

Authorization: Bearer le_live_your_api_key_here
Keep keys secret. API keys grant full write access to the workspace they belong to. Never expose them in client-side code.

How to Connect

Use your API key to connect LeadEngines to AI assistants, external tools, or your own apps.

πŸ€–

Connect to AI Assistants (MCP)

Use with Claude Desktop, Cursor, Windsurf, or any MCP-compatible AI tool

LeadEngines supports the Model Context Protocol (MCP), so AI assistants can read your CRM data and take actions like sending SMS, making calls, managing deals, and generating workflows β€” all through natural language.

Claude Desktop

Open Settings β†’ MCP Servers and add:

{
  "mcpServers": {
    "leadengines": {
      "url": "https://app.leadengines.ai/api/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY"
      }
    }
  }
}

Cursor / Windsurf

Open MCP settings and add a new server with the URL https://app.leadengines.ai/api/mcp and your API key as a Bearer token header.

What AI assistants can do:

  • Search and manage contacts, deals, and tags
  • Send SMS messages and initiate phone calls (including AI voice calls)
  • Create and send email campaigns
  • Build and activate workflows from natural language
  • View pipeline analytics and dashboard stats

See the MCP reference below for the full list of tools and resources.

>_

REST API

Integrate with any language or platform via HTTP

All endpoints are at https://app.leadengines.ai/api/v1/ and require your API key as a Bearer token.

Example: List contacts

curl https://app.leadengines.ai/api/v1/contacts \
  -H "Authorization: Bearer YOUR_API_KEY"

Example: Send SMS

curl -X POST https://app.leadengines.ai/api/v1/sms \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"contactId": "CONTACT_ID", "body": "Hello!"}'

Example: Verify a lead’s phone number

# 1. Text a 6-digit code
curl -X POST https://app.leadengines.ai/api/v1/verify/start \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phone": "+14155551234"}'

# 2. Confirm it (this is the billable step)
curl -X POST https://app.leadengines.ai/api/v1/verify/check \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phone": "+14155551234", "code": "123456"}'

Pass formId, quizId or pageId to also receive a proof token you can submit to that asset’s public endpoint. Charged per successful verification only.

Available endpoints

GET /v1/contactsPOST /v1/contactsGET /v1/contacts/:id/tagsPOST /v1/contacts/:id/tagsGET /v1/tagsPOST /v1/tagsGET /v1/dealsPOST /v1/dealsGET /v1/pipelinesGET /v1/workflowsPOST /v1/smsPOST /v1/callsGET /v1/conversationsGET /v1/search?q=...GET /v1/dashboardPOST /v1/engines/:id/enrollPOST /v1/verify/startPOST /v1/verify/check
</>

TypeScript / JavaScript SDK

Type-safe client for Node.js, React Native, and browser apps

Install the SDK and use typed methods for all API operations.

npm install @leadengines/api-client

Usage

import { LeadEnginesClient } from "@leadengines/api-client";

const client = new LeadEnginesClient({
  baseUrl: "https://app.leadengines.ai",
  apiKey: "YOUR_API_KEY",
});

// List contacts
const { data, meta } = await client.contacts.list({ search: "alice" });

// Send SMS
await client.sms.send({ contactId: "c1", body: "Hello!" });

// Initiate AI call
await client.calls.initiate({ to: "+15551234567", aiCall: true });

// Assign a tag to a contact
await client.contacts.addTag("contact_id", { tagId: "tag_id" });

// Get dashboard stats
const { data: stats } = await client.dashboard.get();

Dashboard

Workspace-level summary stats in a single call.

GEThttps://app.leadengines.ai/api/v1/dashboard

Returns aggregate counts for contacts, deals, total deal value, active workflows, and conversations.

Response

{
  "data": {
    "contacts": 842,
    "deals": 134,
    "totalDealValue": 498500,
    "activeWorkflows": 6,
    "conversations": 310
  }
}

Example

curl https://app.leadengines.ai/api/v1/dashboard \
  -H "Authorization: Bearer le_live_..."

Contacts

Create, read, update, and delete contacts. All operations are scoped to the workspace associated with your API key.

GEThttps://app.leadengines.ai/api/v1/contacts

List contacts with optional filtering and pagination.

Query parameters

page    integer  Page number (default: 1)
limit   integer  Results per page, max 100 (default: 25)
search  string   Filter by name, email, phone, or company
status  string   Filter by status (e.g. ACTIVE, LEAD, CUSTOMER)

Response

{
  "data": [
    {
      "id": "clx...",
      "firstName": "Jane",
      "lastName": "Smith",
      "email": "jane@example.com",
      "phone": "+15550100",
      "company": "Acme Corp",
      "status": "ACTIVE",
      "createdAt": "2026-01-15T10:00:00.000Z"
    }
  ],
  "meta": { "total": 842, "page": 1, "limit": 25, "totalPages": 34 }
}

Example

curl "https://app.leadengines.ai/api/v1/contacts?page=1&limit=25&status=LEAD" \
  -H "Authorization: Bearer le_live_..."
POSThttps://app.leadengines.ai/api/v1/contacts

Create a single contact. Returns 409 if a contact with the same email already exists.

Request body

{
  "firstName": "Jane",              // required
  "lastName":  "Smith",             // optional
  "email":     "jane@example.com",  // optional, unique per workspace
  "phone":     "+15550100",         // optional
  "company":   "Acme Corp",         // optional
  "title":     "CEO",               // optional
  "status":    "LEAD",              // optional, default: "ACTIVE"

  // Custom fields β€” auto-created as Text if they don't exist
  "customFields": {
    "LinkedIn URL": "https://linkedin.com/in/jane",
    "Lead Source":  "Webinar"
  }
}

Response

{ "data": { "id": "clx...", "firstName": "Jane", ... } }

Example

curl -X POST https://app.leadengines.ai/api/v1/contacts \
  -H "Authorization: Bearer le_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "Jane",
    "email": "jane@example.com",
    "status": "LEAD",
    "customFields": { "Lead Source": "Webinar" }
  }'
GEThttps://app.leadengines.ai/api/v1/contacts/:id

Fetch a single contact by ID, including custom field values and associated deals.

Response

{
  "data": {
    "id": "clx...",
    "firstName": "Jane",
    "email": "jane@example.com",
    "status": "ACTIVE",
    "fieldValues": [
      { "customField": { "name": "Lead Source" }, "value": "Webinar" }
    ],
    "deals": [
      { "deal": { "id": "clx...", "name": "Acme Corp", "value": 5000, "stage": { "name": "Qualified" } } }
    ]
  }
}

Example

curl https://app.leadengines.ai/api/v1/contacts/clx_contact_id \
  -H "Authorization: Bearer le_live_..."
PATCHhttps://app.leadengines.ai/api/v1/contacts/:id

Update a contact. Only include the fields you want to change.

Request body

{
  "firstName": "Jane",
  "status":    "CUSTOMER",
  "company":   "Acme Corp"
}

Response

{ "data": { "id": "clx...", "firstName": "Jane", "status": "CUSTOMER", ... } }

Example

curl -X PATCH https://app.leadengines.ai/api/v1/contacts/clx_contact_id \
  -H "Authorization: Bearer le_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "status": "CUSTOMER" }'
DELETEhttps://app.leadengines.ai/api/v1/contacts/:id

Permanently delete a contact and all associated data.

Response

{ "data": { "deleted": true } }

Example

curl -X DELETE https://app.leadengines.ai/api/v1/contacts/clx_contact_id \
  -H "Authorization: Bearer le_live_..."
POSThttps://app.leadengines.ai/api/v1/contacts/import

Bulk-create up to 500 contacts in a single request. Invalid rows are reported in errors but don't block the rest.

Request body

{
  "records": [
    {
      "firstName": "Alice",
      "email": "alice@example.com",
      "status": "ACTIVE",
      "customFields": { "Source": "CSV import" }
    },
    {
      "firstName": "Bob",
      "email": "bob@example.com"
    }
  ]
}

Response

{
  "data": {
    "created": 2,
    "skipped": 0,
    "errors":  [],
    "total":   2
  }
}

Example

curl -X POST https://app.leadengines.ai/api/v1/contacts/import \
  -H "Authorization: Bearer le_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "records": [...] }'

Tags

Tags let you categorize and filter contacts with colored labels. Create tags in your workspace under Contacts β†’ Tags, then assign them to contacts via the API. Tag IDs are stable cuid strings.

GEThttps://app.leadengines.ai/api/v1/tags

List all tags defined in the workspace, with the number of contacts assigned to each.

Response

{
  "data": [
    {
      "id": "clx...",
      "name": "VIP",
      "color": "#6366f1",
      "subAccountId": "clx...",
      "_count": { "contacts": 12 },
      "createdAt": "2026-04-09T10:00:00.000Z"
    }
  ]
}

Example

curl https://app.leadengines.ai/api/v1/tags \
  -H "Authorization: Bearer le_live_..."
POSThttps://app.leadengines.ai/api/v1/tags

Create a new tag. Tag names must be unique within the workspace. Returns 409 on duplicate name.

Request body

{
  "name":  "VIP",      // required, max 50 characters
  "color": "#6366f1"   // optional, hex color (default: #6366f1)
}

Response

{ "data": { "id": "clx...", "name": "VIP", "color": "#6366f1", ... } }

Example

curl -X POST https://app.leadengines.ai/api/v1/tags \
  -H "Authorization: Bearer le_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "name": "VIP", "color": "#f97316" }'
GEThttps://app.leadengines.ai/api/v1/contacts/:id/tags

Get all tags currently assigned to a contact.

Response

{
  "data": [
    {
      "tagId": "clx...",
      "contactId": "clx...",
      "assignedAt": "2026-04-09T12:00:00.000Z",
      "tag": { "id": "clx...", "name": "VIP", "color": "#6366f1" }
    }
  ]
}

Example

curl https://app.leadengines.ai/api/v1/contacts/clx_contact_id/tags \
  -H "Authorization: Bearer le_live_..."
POSThttps://app.leadengines.ai/api/v1/contacts/:id/tags

Assign a tag to a contact. If the tag is already assigned, it is silently skipped. This also triggers any workflows that use the TAG_ADDED trigger.

Request body

{
  "tagId": "clx..."  // required β€” tag ID from GET /tags
}

Response

{ "data": { "tagId": "clx...", "contactId": "clx...", "assignedAt": "..." } }

Example

curl -X POST https://app.leadengines.ai/api/v1/contacts/clx_contact_id/tags \
  -H "Authorization: Bearer le_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "tagId": "clx_tag_id" }'
DELETEhttps://app.leadengines.ai/api/v1/contacts/:id/tags

Remove a tag from a contact. Pass the tag ID as a query parameter.

Query parameters

tagId  string  (required) The tag ID to remove

Response

{ "success": true }

Example

curl -X DELETE "https://app.leadengines.ai/api/v1/contacts/clx_contact_id/tags?tagId=clx_tag_id" \
  -H "Authorization: Bearer le_live_..."

Deals

Create deals and link them to contacts. Fetch your pipeline stages from GET /pipelines to get the required IDs.

POSThttps://app.leadengines.ai/api/v1/deals

Create a single deal in a pipeline stage.

Request body

{
  "name":       "Acme Corp",      // required
  "stageId":    "clx...",         // required β€” from GET /pipelines
  "pipelineId": "clx...",         // required β€” from GET /pipelines
  "value":      5000,             // optional, numeric
  "currency":   "USD",            // optional, default: "USD"
  "closedAt":   "2026-06-30",     // optional, ISO date
  "notes":      "Met at conference",
  "contactIds": ["clx..."],       // optional, link existing contacts
  "customFields": {
    "Deal Source": "Inbound",
    "Priority":    "High"
  }
}

Response

{ "data": { "id": "clx...", "name": "Acme Corp", ... } }

Example

curl -X POST https://app.leadengines.ai/api/v1/deals \
  -H "Authorization: Bearer le_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Corp",
    "stageId": "clx_stage_id",
    "pipelineId": "clx_pipeline_id",
    "value": 5000
  }'
GEThttps://app.leadengines.ai/api/v1/deals/:id

Fetch a single deal by ID.

Response

{
  "data": {
    "id": "clx...",
    "name": "Acme Corp",
    "value": 5000,
    "currency": "USD",
    "stage": { "name": "Qualified", "color": "#22c55e" }
  }
}

Example

curl https://app.leadengines.ai/api/v1/deals/clx_deal_id \
  -H "Authorization: Bearer le_live_..."
PATCHhttps://app.leadengines.ai/api/v1/deals/:id

Update a deal. Only include the fields you want to change.

Request body

{
  "stageId": "clx...",
  "value":   7500
}

Response

{ "data": { "id": "clx...", "name": "Acme Corp", "value": 7500, ... } }

Example

curl -X PATCH https://app.leadengines.ai/api/v1/deals/clx_deal_id \
  -H "Authorization: Bearer le_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "stageId": "clx_stage_id", "value": 7500 }'
DELETEhttps://app.leadengines.ai/api/v1/deals/:id

Permanently delete a deal.

Response

{ "data": { "deleted": true } }

Example

curl -X DELETE https://app.leadengines.ai/api/v1/deals/clx_deal_id \
  -H "Authorization: Bearer le_live_..."
POSThttps://app.leadengines.ai/api/v1/deals/import

Bulk-create up to 100 deals in a single request.

Request body

{
  "records": [
    {
      "name": "Acme Corp",
      "stageId": "clx...",
      "pipelineId": "clx...",
      "value": 5000,
      "customFields": { "Source": "Referral" }
    }
  ]
}

Response

{
  "data": {
    "created": 1,
    "skipped": 0,
    "errors":  [],
    "total":   1
  }
}

Example

curl -X POST https://app.leadengines.ai/api/v1/deals/import \
  -H "Authorization: Bearer le_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "records": [...] }'

Pipelines

Fetch your pipelines and stages. The stageId and pipelineId values returned here are required when creating deals.

GEThttps://app.leadengines.ai/api/v1/pipelines

Returns all pipelines and their stages for the workspace.

Response

{
  "data": [
    {
      "id": "clx...",
      "name": "Sales Pipeline",
      "isDefault": true,
      "stages": [
        { "id": "clx...", "name": "New Lead",   "color": "#6366f1", "order": 0 },
        { "id": "clx...", "name": "Qualified",  "color": "#22c55e", "order": 1 },
        { "id": "clx...", "name": "Closed Won", "color": "#14b8a6", "order": 2 }
      ]
    }
  ]
}

Example

curl https://app.leadengines.ai/api/v1/pipelines \
  -H "Authorization: Bearer le_live_..."

Email Lists

Lists are the audiences campaigns send to. Create a list, add contacts to it, then reference its id in a campaign's listIds.

GEThttps://app.leadengines.ai/api/v1/lists

List every email list in the workspace with its member count.

Response

{
  "data": [
    { "id": "clx...", "name": "Newsletter", "description": null, "_count": { "members": 412 } }
  ]
}

Example

curl https://app.leadengines.ai/api/v1/lists \
  -H "Authorization: Bearer le_live_..."
POSThttps://app.leadengines.ai/api/v1/lists

Create an email list.

Request body

{
  "name": "Newsletter",
  "description": "Monthly product update"
}

Response

{ "data": { "id": "clx...", "name": "Newsletter", "description": "..." } }

Example

curl -X POST https://app.leadengines.ai/api/v1/lists \
  -H "Authorization: Bearer le_live_..." \
  -H "Content-Type: application/json" \
  -d '{"name":"Newsletter"}'
POSThttps://app.leadengines.ai/api/v1/lists/:id/contacts

Add contacts to a list. Idempotent β€” contacts already on the list are skipped, and ids from another workspace are ignored, so added can be lower than the number supplied.

Request body

{ "contactIds": ["clx...", "clx..."] }

Response

{ "data": { "added": 2 } }

Example

curl -X POST https://app.leadengines.ai/api/v1/lists/clx.../contacts \
  -H "Authorization: Bearer le_live_..." \
  -H "Content-Type: application/json" \
  -d '{"contactIds":["clx..."]}'

Email Campaigns

The full draft β†’ verify β†’ correct β†’ test β†’ send loop. Campaign status is one of DRAFT, SCHEDULED, SENDING, SENT, CANCELLED, PAUSED.

Sending anything β€” including a test β€” requires a verified sending domain, added under Email Settings. Without one these endpoints return 409 NO_VERIFIED_DOMAIN.
GEThttps://app.leadengines.ai/api/v1/campaigns

List campaigns, newest first. Returns the SIZE of each campaign's content rather than the bodies, so a listing stays small β€” use GET /campaigns/:id for the content itself.

Query parameters

status  string  DRAFT | SCHEDULED | SENDING | SENT | CANCELLED | PAUSED
search  string  Match against campaign name or subject
limit   number  Max results (default 25, max 100)

Response

{
  "data": [
    {
      "id": "clx...",
      "name": "August newsletter",
      "subject": "Your August update",
      "status": "DRAFT",
      "htmlContentLength": 7231,
      "jsonContentLength": 0,
      "lists": [{ "id": "clx...", "name": "Newsletter" }],
      "totalSent": 0
    }
  ]
}

Example

curl "https://app.leadengines.ai/api/v1/campaigns?status=DRAFT" \
  -H "Authorization: Bearer le_live_..."
POSThttps://app.leadengines.ai/api/v1/campaigns

Create a campaign draft. htmlContent alone is enough β€” the campaign builder parses it when a human opens the draft, so you never need to produce editor JSON. The campaign needs an audience: at least one of listIds, segmentIds, or sendToAllContacts.

Request body

{
  "name": "August newsletter",     // internal β€” recipients never see it
  "subject": "Your August update",
  "htmlContent": "<p>Hello</p>",
  "previewText": "Inbox preview line",
  "fromName": "Acme",              // falls back to the workspace default
  "replyTo": "hi@acme.com",        //   "
  "editorType": "visual",          // visual | text (default visual)
  "listIds": ["clx..."],
  "segmentIds": [],
  "excludeListIds": [],            // suppression β€” these recipients are skipped
  "sendToAllContacts": false,
  "jsonContent": null,             // only for real Maily/Tiptap editor JSON
  "templateId": null
}

Response

{ "data": { "id": "clx...", "status": "DRAFT", ... } }

Example

curl -X POST https://app.leadengines.ai/api/v1/campaigns \
  -H "Authorization: Bearer le_live_..." \
  -H "Content-Type: application/json" \
  -d '{"name":"August newsletter","subject":"Your August update","htmlContent":"<p>Hello</p>","listIds":["clx..."]}'
GEThttps://app.leadengines.ai/api/v1/campaigns/:id

Read one campaign in full β€” subject, preview text, the HTML body, its lists/segments/exclusions and its send stats.

Response

{ "data": { "id": "clx...", "htmlContent": "<p>Hello</p>", "lists": [...], "totalOpened": 0, ... } }

Example

curl https://app.leadengines.ai/api/v1/campaigns/clx... \
  -H "Authorization: Bearer le_live_..."
PATCHhttps://app.leadengines.ai/api/v1/campaigns/:id

Partially update a DRAFT. Any subset of the create body. Omitted fields keep their current value β€” including the audience β€” so fixing one bad link is a one-field PATCH, not a second campaign. Already scheduled? Unschedule it first.

Request body

{ "subject": "Corrected subject line" }

Response

{ "data": { campaign } }

Example

curl -X PATCH https://app.leadengines.ai/api/v1/campaigns/clx... \
  -H "Authorization: Bearer le_live_..." \
  -H "Content-Type: application/json" \
  -d '{"subject":"Corrected subject line"}'
DELETEhttps://app.leadengines.ai/api/v1/campaigns/:id

Delete a DRAFT campaign. Use POST /campaigns/:id/cancel for one already scheduled or sending.

Response

{ "data": { "deleted": true } }

Example

curl -X DELETE https://app.leadengines.ai/api/v1/campaigns/clx... \
  -H "Authorization: Bearer le_live_..."
POSThttps://app.leadengines.ai/api/v1/campaigns/:id/test

Send one [TEST]-prefixed copy to a single address, using the same sender the real send would. Does not change the campaign's status or audience. Rate limited to 10 per 5 minutes per workspace.

Request body

{ "email": "you@example.com" }

Response

{ "data": { "sent": true } }

Example

curl -X POST https://app.leadengines.ai/api/v1/campaigns/clx.../test \
  -H "Authorization: Bearer le_live_..." \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com"}'
POSThttps://app.leadengines.ai/api/v1/campaigns/:id/schedule

Schedule a DRAFT (or reschedule an already-scheduled campaign). scheduledAt must be ISO 8601 with a timezone.

Request body

{ "scheduledAt": "2026-08-15T15:00:00Z" }

Response

{ "data": { "scheduledAt": "2026-08-15T15:00:00Z" } }

Example

curl -X POST https://app.leadengines.ai/api/v1/campaigns/clx.../schedule \
  -H "Authorization: Bearer le_live_..." \
  -H "Content-Type: application/json" \
  -d '{"scheduledAt":"2026-08-15T15:00:00Z"}'
DELETEhttps://app.leadengines.ai/api/v1/campaigns/:id/schedule

Unschedule: SCHEDULED back to DRAFT, clearing the send time so the campaign can be edited and rescheduled.

Response

{ "data": { "status": "DRAFT" } }

Example

curl -X DELETE https://app.leadengines.ai/api/v1/campaigns/clx.../schedule \
  -H "Authorization: Bearer le_live_..."
POSThttps://app.leadengines.ai/api/v1/campaigns/:id/send

Send a DRAFT to its whole audience immediately. Irreversible once delivery starts β€” send a test first.

Response

{ "data": { "status": "SENDING", "recipientCount": 128 } }

Example

curl -X POST https://app.leadengines.ai/api/v1/campaigns/clx.../send \
  -H "Authorization: Bearer le_live_..."
POSThttps://app.leadengines.ai/api/v1/campaigns/:id/cancel

Cancel a SCHEDULED or SENDING campaign. Already-delivered emails cannot be recalled; this stops the rest.

Response

{ "data": { "status": "CANCELLED" } }

Example

curl -X POST https://app.leadengines.ai/api/v1/campaigns/clx.../cancel \
  -H "Authorization: Bearer le_live_..."

Campaign error codes

400  VALIDATION_ERROR      message names the offending field
404  NOT_FOUND             no such campaign here, or not a DRAFT for a DRAFT-only call
409  CAMPAIGN_INCOMPLETE   needs both a subject and HTML content
409  NO_RECIPIENTS         the audience resolved to zero contacts
409  NO_VERIFIED_DOMAIN    add a sending domain in Email Settings
409  SENDING_SUSPENDED     sending is paused for this workspace
429  RATE_LIMITED          test-send burst limit; see Retry-After

Workflows

Manage and enroll contacts in automated workflows (Engines). Workflows with API trigger type can be enrolled into via POST /engines/:id/enroll.

GEThttps://app.leadengines.ai/api/v1/workflows

List all workflows in the workspace.

Query parameters

status  string  Filter by status: DRAFT, ACTIVE, PAUSED, ARCHIVED

Response

{
  "data": [
    {
      "id": "clx...",
      "name": "New Lead Welcome",
      "status": "ACTIVE",
      "triggers": [{ "type": "CONTACT_CREATED" }],
      "_count": { "steps": 5, "enrollments": 128 }
    }
  ]
}

Example

curl "https://app.leadengines.ai/api/v1/workflows?status=ACTIVE" \
  -H "Authorization: Bearer le_live_..."
GEThttps://app.leadengines.ai/api/v1/workflows/:id

Fetch a single workflow with full triggers and steps.

Response

{
  "data": {
    "id": "clx...",
    "name": "New Lead Welcome",
    "status": "ACTIVE",
    "triggers": [{ "id": "clx...", "type": "API", "config": null }],
    "steps": [
      { "order": 0, "type": "SEND_SMS", "config": "{...}", "name": "Welcome text" },
      { "order": 1, "type": "WAIT", "config": "{"delayMinutes":1440}" }
    ],
    "_count": { "enrollments": 42 }
  }
}

Example

curl https://app.leadengines.ai/api/v1/workflows/clx_workflow_id \
  -H "Authorization: Bearer le_live_..."
PATCHhttps://app.leadengines.ai/api/v1/workflows/:id

Update the status of a workflow.

Request body

{
  "status": "ACTIVE"  // DRAFT | ACTIVE | PAUSED | ARCHIVED
}

Response

{ "data": { "updated": true } }

Example

curl -X PATCH https://app.leadengines.ai/api/v1/workflows/clx_workflow_id \
  -H "Authorization: Bearer le_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "status": "PAUSED" }'
DELETEhttps://app.leadengines.ai/api/v1/workflows/:id

Delete a workflow and all its enrollments.

Response

{ "data": { "deleted": true } }

Example

curl -X DELETE https://app.leadengines.ai/api/v1/workflows/clx_workflow_id \
  -H "Authorization: Bearer le_live_..."
POSThttps://app.leadengines.ai/api/v1/engines/:id/enroll

Enroll one or more contacts in an active workflow. Contacts already enrolled are silently skipped. The workflow must have at least one step.

Request body

{
  "contactIds": ["clx...", "clx..."],  // required
  "dealId": "clx..."                   // optional β€” associate a deal with the enrollment
}

Response

{ "data": { "enrolledCount": 2 } }

Example

curl -X POST https://app.leadengines.ai/api/v1/engines/clx_workflow_id/enroll \
  -H "Authorization: Bearer le_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "contactIds": ["clx_contact_id"] }'

Webhooks (Inbound)

Send contacts from Zapier, Whop, Make, or any third-party tool directly into an Engine β€” no API key required. LeadEngines creates or finds the contact from the payload and enrolls them automatically.

Webhook URLs are generated per-engine inside the canvas. Open an Engine β†’ add the Webhook / API trigger β†’ click Generate Webhook URL.
POSThttps://app.leadengines.ai/api/webhooks/engines/:secret

Public endpoint β€” no Authorization header needed. The secret in the URL authenticates the request. The engine must be ACTIVE and have the Webhook / API trigger enabled.

Request body

{
  "email":      "john@example.com",   // required if no phone
  "phone":      "+15551234567",       // required if no email
  "firstName":  "John",              // optional
  "lastName":   "Doe",               // optional
  "company":    "Acme Inc"           // optional
}

Smart field detection

LeadEngines automatically tries common key variants for each field, so you don't need to rename your payload:

Contact fieldKeys tried (in order)
emailemail, email_address, emailAddress, contact_email
phonephone, phone_number, phoneNumber, mobile, cell
firstNamefirstName, first_name, firstname, fname, given_name
lastNamelastName, last_name, lastname, lname, surname
companycompany, company_name, companyName, organization, org

You can also configure custom field mapping per-engine in the canvas trigger config (e.g. map customer_email β†’ email).

Response

{ "ok": true }

Example β€” curl

curl -X POST https://app.leadengines.ai/api/webhooks/engines/YOUR_SECRET \
  -H "Content-Type: application/json" \
  -d '{
    "email": "john@example.com",
    "first_name": "John",
    "last_name": "Doe",
    "phone": "+15551234567",
    "company": "Acme Inc"
  }'

Zapier setup

  1. In your Engine canvas, add the Webhook / API trigger and click Generate Webhook URL
  2. In Zapier, add a Webhooks by Zapier β†’ POST action
  3. Paste the webhook URL and set Payload Type to json
  4. Map your Zap fields to email, phone, firstName, etc.
Error responses
400Payload must contain at least one of: email or phone
400Invalid JSON body
404Wrong secret or engine is not ACTIVE
500Internal error

Conversations

Access SMS conversations with contacts.

GEThttps://app.leadengines.ai/api/v1/conversations

List all conversations, ordered by most recent message.

Query parameters

page   integer  Page number (default: 1)
limit  integer  Results per page, max 100 (default: 25)

Response

{
  "data": [
    {
      "id": "clx...",
      "lastMessageAt": "2026-04-09T11:30:00.000Z",
      "contact": {
        "id": "clx...",
        "firstName": "Jane",
        "lastName": "Smith",
        "email": "jane@example.com"
      }
    }
  ],
  "meta": { "total": 310, "page": 1, "limit": 25, "totalPages": 13 }
}

Example

curl "https://app.leadengines.ai/api/v1/conversations?limit=10" \
  -H "Authorization: Bearer le_live_..."

SMS

Send outbound SMS messages to contacts. Your workspace must have a provisioned phone number configured.

POSThttps://app.leadengines.ai/api/v1/sms

Send an SMS message to a contact by their ID.

Request body

{
  "contactId": "clx...",           // required
  "body":      "Hi {{firstName}}, just checking in!" // required, max 1600 chars
}

Response

{ "data": { "id": "clx...", "body": "Hi Jane, just checking in!", ... } }

Example

curl -X POST https://app.leadengines.ai/api/v1/sms \
  -H "Authorization: Bearer le_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "contactId": "clx_contact_id", "body": "Hey, are you still interested?" }'
SMS error codes
422CONTACT_NO_PHONEContact has no phone number
422OPT_OUTContact has opted out of SMS
422UNVERIFIEDContact phone is not verified
422PHONE_NOT_CONFIGUREDNo phone number provisioned for workspace
429RATE_LIMITToo many messages β€” slow down
403SUSPENDEDWorkspace is suspended

Calls

Initiate outbound calls β€” standard or AI-powered voice agent calls. Requires a provisioned phone number.

POSThttps://app.leadengines.ai/api/v1/calls

Initiate an outbound phone call to any number. Set aiCall to true to use the AI voice agent.

Request body

{
  "to":        "+15550100",  // required, E.164 format
  "contactId": "clx...",    // optional β€” link call to a contact record
  "aiCall":    false,        // optional, default: false

  // Only used when aiCall is true:
  "aiConfig": {
    "systemPrompt": "You are a friendly sales rep calling to schedule a demo...",
    "assistantId":  "clx..."  // optional β€” use a saved AI assistant config
  }
}

Response

{ "data": { "callId": "telnyx-call-id", ... } }

Example

curl -X POST https://app.leadengines.ai/api/v1/calls \
  -H "Authorization: Bearer le_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+15550100",
    "contactId": "clx_contact_id",
    "aiCall": true,
    "aiConfig": { "systemPrompt": "You are scheduling a product demo..." }
  }'

Custom Fields

You don't need to pre-configure custom fields before using them. Simply pass a customFields object with key-value pairs and LeadEngines will automatically create the fields (as Text type) the first time it sees them.

How it works

  1. Pass "customFields": { "Lead Source": "Webinar" } in your request
  2. LeadEngines checks if a field named Lead Source exists in your workspace
  3. If not, it creates a new Text field called Lead Source
  4. The value Webinar is saved against that field for the contact or deal
  5. The field now appears in your workspace under Settings β†’ Custom Fields
Auto-created fields default to Text type. You can change the type, add select options, or mark them as required from the Custom Fields settings page.

MCP Server (AI Integration)

LeadEngines ships a built-in Model Context Protocol (MCP) server. Connect it to Claude, Cursor, or any MCP-compatible AI client to let AI agents read and write your CRM data directly.

Connection

MCP endpoint: https://app.leadengines.ai/api/mcp
Auth header:  Authorization: Bearer le_live_your_api_key_here

The same API key used for the REST API authenticates MCP. The server uses the Streamable HTTP transport.

Available Resources (read-only context)

URIDescription
leadengines://contactsLast 100 contacts with tags
leadengines://dealsLast 100 deals with stage info
leadengines://pipelinesAll pipelines with stages and deal counts
leadengines://conversationsLast 50 SMS conversations
leadengines://workflowsAll workflows with status and trigger info
leadengines://tagsAll tags with contact counts
leadengines://dashboardAggregate workspace stats

Available Tools (read + write actions)

The email and list tools also take an optional subAccountId β€” omit it with a workspace-scoped key, supply it with an admin-scoped key to choose which workspace to act on.

ToolParametersDescription
create_contactfirstName, lastName?, email?, phone?, company?, title?, status?, tagIds?, smsOptIn?Create a new contact
delete_contactcontactIdDelete a contact by ID
bulk_update_contact_statuscontactIds[], statusUpdate status for multiple contacts
searchquerySearch contacts and deals by keyword
list_tagsβ€”List all workspace tags
create_tagname, color?Create a tag
add_tags_to_contactcontactId, tagIds[]Assign tags to a contact
remove_tags_from_contactcontactId, tagIds[]Remove tags from a contact
list_custom_fieldsentity? (CONTACT | DEAL)List custom fields
create_custom_fieldname, type, entity, required?, options?Create a custom field on CONTACT or DEAL
create_dealname, stageId, value?, contactIds?, notes?, currency?Create a new deal
move_dealdealId, stageIdMove a deal to a different stage
delete_dealdealIdDelete a deal
create_pipelinename, isDefault?Create a pipeline with default stages
create_pipeline_stagepipelineId, name, color?, orderAdd a stage to a pipeline
list_email_listsβ€”List email lists (audiences)
create_listname, description?Create an email list
add_contacts_to_listlistId, contactIds[]Add contacts to a list
create_email_templatename, subject?, htmlContent?, jsonContent?, editorType?Create an email template
list_email_campaignsstatus?, search?, limit?List campaigns (content sizes, not bodies)
get_email_campaigncampaignIdRead one campaign in full
create_email_campaignname, subject, htmlContent?, previewText?, fromName?, replyTo?, templateId?, editorType?, jsonContent?, listIds[]?, segmentIds[]?, excludeListIds[]?, sendToAllContacts?Create a campaign draft
update_email_campaigncampaignId + any create field (all optional)Correct a DRAFT in place
send_test_emailcampaignId, emailSend one [TEST] copy (rate limited)
delete_email_campaigncampaignIdDelete a DRAFT campaign
send_campaign_nowcampaignIdSend a campaign immediately
schedule_campaigncampaignId, scheduledAt (ISO 8601)Schedule a campaign for later
unschedule_campaigncampaignIdSCHEDULED β†’ DRAFT so it can be edited
cancel_campaigncampaignIdCancel a scheduled or sending campaign
create_formname, slug?, fields[], createContact?, tagOnSubmission?, completionWorkflowId?, publish?Create a lead-capture form
create_quizname, questions?[], resultHeadline?, resultBody?, publish?Create a lead-capture quiz
send_smscontactId, messageSend an SMS to a contact
generate_ai_sms_replyconversationIdGenerate an AI reply suggestion
initiate_callto, contactId?, aiCall?, systemPrompt?Initiate an outbound call
initiate_ai_call_to_contactcontactId, systemPrompt?AI voice call to a contact
activate_workflowworkflowIdActivate a workflow
pause_workflowworkflowIdPause a workflow
enroll_contacts_in_workflowworkflowId, contactIds[]Enroll contacts in a workflow
generate_workflowpromptGenerate workflow steps from natural language using AI
The generate_workflow tool uses Claude AI to generate workflow steps from a natural language description. This deducts from your workspace AI balance. The generated steps are returned as JSON β€” you can review and save them via the Engines UI.

MCP Builder β€” AI Design Sites via Claude Subscription

Enterprise plan onlyWebsites Β· Pages Β· Funnels

The MCP Builder lets you build complete AI Design websites, standalone landing pages, and multi-step funnels inside any sub-account you have access to, powered by your own Claude.ai subscription (Pro, Max, or higher) instead of platform AI credits or sub-account wallet billing. Every LLM token bills against your subscription's 5-hour message window β€” the LeadEngines side never makes an Anthropic API call.

This feature is gated to Enterprise plans only. Plans without allowMcpBuilder = true reject the website-builder tools at call time with "The MCP builder is not enabled on this workspace's plan". Super-admins bypass the check, but the underlying CRM MCP tools (contacts, deals, etc.) remain available on every plan.

Two API key scopes

ScopeWho issuesTargetsUse
SUB_ACCOUNTSub-account admin (Settings β†’ API Keys)One workspaceCRM MCP tools + (on enterprise plans) builder tools scoped to that workspace
USER_ADMINSuper-admin only (/admin/mcp-keys)Any sub-account the user can accessCross-workspace builder via a per-call subAccountId argument

Both key types use the same le_live_… format and the same https://app.leadengines.ai/api/mcp endpoint β€” Claude doesn't need to know which scope it's holding.

Connecting from Claude Desktop

// ~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "leadengines": {
      "url": "https://app.leadengines.ai/api/mcp",
      "headers": {
        "Authorization": "Bearer le_live_your_admin_key_here"
      }
    }
  }
}

For Claude Code: claude mcp add leadengines https://app.leadengines.ai/api/mcp --header "Authorization: Bearer le_live_...".

Builder tools

None of these tools call Anthropic. They accept HTML produced by Claude β€” the LLM work happens inside Claude's own chat using your subscription quota.

ToolParametersDescription
list_my_sub_accountsβ€”Discover sub-accounts you can target. Returns mcpBuilderEnabled per row.
list_websitessubAccountId?List AI Design websites in a sub-account.
get_websitesubAccountId?, websiteIdFetch an existing site's metadata + pages + a chrome-only reference HTML. Call this BEFORE adding pages to an existing site so new pages match the visual language.
create_websitesubAccountId?, name, slug?Create a NEW AI Design website. Returns websiteId + final slug.
add_pagesubAccountId?, websiteId, websitePath (null for home), name, htmlAdd a page to an existing website. Pass the FULL HTML5 document.
publish_websitesubAccountId?, websiteIdFlip status from DRAFT to PUBLISHED.
delete_websitesubAccountId?, websiteIdDelete a website. Its pages are detached (kept), not deleted.
list_pagessubAccountId?, standaloneOnly?List pages (standalone by default; pass false for website/funnel pages too).
create_pagesubAccountId?, name, slug?, html, publish?Create a standalone AI Design landing page (served at /p/<sub>/<slug>).
publish_pagesubAccountId?, pageIdPublish a standalone page.
list_funnelssubAccountId?List funnels in a sub-account.
create_funnelsubAccountId?, name, slug?, description?Create an AI Design funnel (ordered step pages).
add_funnel_stepsubAccountId?, funnelId, name, stepSlug?, htmlAppend a step page to a funnel. Link steps with data-le-link="next".
publish_funnelsubAccountId?, funnelIdPublish a funnel + all its step pages (served at /f/<sub>/<slug>).
get_page_htmlsubAccountId?, pageIdFetch one page's full HTML β€” works for website, standalone, and funnel pages.
set_page_htmlsubAccountId?, pageId, htmlReplace any page's HTML in-place (website/standalone/funnel).
list_media_assetssubAccountId?, type? (all | image | document), limit?List the workspace media library β€” images and documents (PDF, Office, CSV). Reuse these URLs instead of inventing stock ones.
upload_media_assetsubAccountId?, sourceUrl OR contentBase64, name?Upload/rehost a file into the media library and get a permanent public URL. Images, PDF, Word/Excel/PowerPoint, .txt, .csv. Max 20 MB.
register_media_assetsubAccountId?, url, name?, mimeType?, sizeBytes?Bookmark an already-hosted URL in the library without rehosting it.

MCP prompts

PromptArgsReturns
v2_design_systembrand?The full LeadEngines v2 design-system seed: Tailwind Play CDN + framer-motion + page-shell SEO contract + component library + anti-slop directives + per-page token budget. Invoke this before generating any HTML.

Sample prompt to give Claude

Using LeadEngines MCP, list my sub-accounts. Then load the
v2_design_system prompt and build a 6-page website for Acme
Bookkeeping β€” a small-business bookkeeper with a warm but
professional voice. Put it in the "Praxis Marketing" sub-account.
Pages: home, services, three service detail pages, contact.
Publish when done.

Claude will pick the right sub-account, call create_website, generate each page's HTML following the v2 design system, call add_page per page, and call publish_website at the end. The site appears in the chosen sub-account's Websites list immediately.

Adding pages to a site that already exists

Using LeadEngines MCP, find the "Busy Business Solutions" website
in the same sub-account. Use get_website on it to see the existing
chrome and the page paths that are already taken. Then add three
new service pages β€” tax prep, payroll, and CFO services β€” that
match the existing design verbatim.

Claude calls list_websites β†’ get_website β†’ reads the returned referenceHtml (the home's head + header + footer, stripped of body content) β†’ writes new pages that reuse the exact same Tailwind config, fonts, header nav, and footer β†’ calls add_page per new page. The existing pages are untouched.

Same idea for iterating on a single page: get_page_html β†’ rewrite β†’set_page_html with the same pageId.

Lead-capture forms β€” automatically wired to LeadEngines

Any form on an MCP-built page is automatically captured into the same sub-account's Contacts β€” no extra configuration needed. The pipeline:

  1. The v2_design_system prompt tells Claude to emit <form data-le-form="lead"> with named inputs (name, email, phone,company, message).
  2. If Claude forgets the attribute, a render-time safety net auto-tags any plain<form> at the time the page is served (forms with an externalaction=... URL are left alone β€” they're clearly pointed elsewhere).
  3. The AI Design runtime intercepts the submit, POSTs to /api/ai-design-form/[pageId], and creates a Contact in the website's sub-account. The Activity log shows which page/form the lead came from via le_source.

Where the lead lands: in the sub-account that owns the website. If you want BBS leads to land in the BBS workspace, build the BBS site inside that sub-account (create_website with that sub-account's ID).

Test it: after publish_website the public URL captures live submissions. For preview testing, use the auth-gated editor preview β€” the runtime fires there too.

Best practice when prompting: don't over-specify the form. The design-system seed already covers everything. *"Add a 5-page site for Acme with a lead-capture form on the contact and home pages"* is enough β€” Claude handles the rest.

Subscription rate-limit reality. Each add_page turn is one chat message against your Claude.ai 5-hour window. Max Plus fits a full ~6-page site comfortably; Pro will start hitting limits on bigger sites. If Claude tells you it's out of usage, anything already saved is permanent β€”add_page commits page-by-page, not all-or-nothing.

What this is not. The MCP Builder is purely additive β€” it doesn't replace the in-app "AI Design β€” Website" button. End users on Pro / Starter plans still use that button, which generates via the platform's Anthropic API key (or their BYO key) and bills against wallet credits as always. The MCP Builder is a separate admin/enterprise surface, layered on top, gated by allowMcpBuilder.

Errors

All errors return a JSON body with an error object.

StatusCodeMeaning
401UNAUTHORIZEDMissing, invalid, or expired API key
400VALIDATION_ERRORRequired field missing or invalid value
400MISSING_TAG_IDtagId query parameter required (DELETE contact tag)
404NOT_FOUNDResource does not exist in this workspace
409DUPLICATEA record with this value already exists (e.g. duplicate email or tag name)
422NO_STEPSWorkflow has no steps β€” cannot enroll or activate
422CONTACT_NO_PHONEContact has no phone number on file
422OPT_OUTContact has opted out of SMS
422PHONE_NOT_CONFIGUREDWorkspace has no provisioned phone number
429RATE_LIMITToo many requests β€” slow down
403SUSPENDEDWorkspace account is suspended
500INTERNAL_ERRORUnexpected server error β€” try again
// Error response shape
{ "error": { "code": "UNAUTHORIZED", "message": "Optional detail" } }