BrightshoalDocsOpen console
MenuLeads and outcomes

After the conversation

Leads and outcomes

Have the assistant save each visitor's details as a lead, get every lead into your CRM the moment it is captured, and report back what became of it.

A lead is what a conversation produced: a name, an email or phone number, and any other details you asked the assistant to collect. Leads come from chat, on your website and on WhatsApp, and there is at most one per conversation; when the visitor adds a detail later, the same lead is updated.

Turn on lead capture

Lead capture is off until you turn it on in the assistant's web chat settings. When it is on, the assistant can save the visitor's name, email and phone, plus the extra fields you list:

config.channels.web_chat
{
  "enabled": true,
  "lead_capture": {
    "enabled": true,
    "fields": [
      { "key": "destination", "label": "Where they want to travel" },
      { "key": "travel_dates", "label": "When they want to travel", "required": true }
    ]
  }
}
SettingMeaning
fields[].keyThe name the value is saved under in the lead's fields.
fields[].labelWhat the assistant is told the field is. Write it the way you would brief a colleague.
fields[].typestring (the default), email or phone.
fields[].requiredThe assistant asks for it before it finishes.
require_completeWhen true, no lead is saved until every required field and an email or phone number are known. A visitor who leaves earlier leaves a conversation, not a lead.

By default a lead is saved as soon as there is an email or a phone number, and filled in as the visitor says more.

To change it on an existing assistant, read the assistant, edit config.channels.web_chat, and send the whole config back with PATCH/api/assistants/{id}: a config you send replaces the stored one, it is not merged. Values the API never returns (secrets, tool request specs) are kept for the objects and tools you send back.

Get each lead as it is captured

Subscribe a webhook to lead.captured. It fires when the assistant saves the lead, when a later analysis creates or refreshes it, when the visitor sends files after it exists, and when the chat is read again after a voice session and that creates or changes the lead. Each delivery carries the whole lead as it is now, so update your record in place rather than adding a new one.

lead.captured
{
  "event": "lead.captured",
  "event_id": "evt_9d1f...",
  "conversation_id": "conv_0c8e5f2a9b7d4c1e86f3a0d2b4c6e8f1",
  "data": {
    "lead": {
      "id": "lead_3a9c7e1f5b2d4c8e9f0a1b2c3d4e5f6a",
      "name": "Ana Silva",
      "email": "ana@example.com",
      "phone": null,
      "fields": { "destination": "Lisbon", "travel_dates": "May, one week" }
    },
    "conversation_id": "conv_0c8e5f2a9b7d4c1e86f3a0d2b4c6e8f1",
    "source": "tool",
    "summary": null
  }
}

source is tool when the assistant saved it during the chat, analysis when the background analysis did, attachments when files arrived, and voice when the conversation was read after a voice session in it ended. summary is the latest summary of the conversation, or null before the first analysis. All event fields are in webhook events.

Read leads

List leads newest first with GET/api/leads. It pages by cursor and filters by project_id, and by creation time with from and to (ISO 8601 with an offset).

curl "https://api.myleadsfactory.tech/api/leads?from=2026-10-01T00:00:00Z&limit=100" \
  -H "Authorization: Bearer $MLF_API_KEY"
Response
{
  "data": [
    {
      "id": "lead_3a9c7e1f5b2d4c8e9f0a1b2c3d4e5f6a",
      "project_id": "proj_...",
      "assistant_id": "asst_...",
      "conversation_id": "conv_0c8e5f2a9b7d4c1e86f3a0d2b4c6e8f1",
      "name": "Ana Silva",
      "email": "ana@example.com",
      "phone": null,
      "fields": { "destination": "Lisbon", "travel_dates": "May, one week" },
      "source": "web_chat",
      "outcome": null,
      "outcome_value": null,
      "outcome_currency": null,
      "outcome_at": null,
      "outcome_ref": null,
      "created_at": "2026-10-28T09:31:04.517Z",
      "updated_at": "2026-10-28T09:33:40.002Z"
    }
  ],
  "next_cursor": null,
  "has_more": false
}

Here source is the channel the lead came from, web_chat or whatsapp. GET/api/leads/{id} returns one lead with an attachments list: the files the visitor sent in the chat, each with a signed url. The conversation behind it, with the full transcript, is at GET/api/conversations/{id}.

Report what became of it

When your team books the trip, wins the deal or loses it, tell the API. Outcomes turn conversations into bookings in chat analytics, where the funnel counts booked leads.

curl -X PUT https://api.myleadsfactory.tech/api/leads/lead_3a9c7e1f5b2d4c8e9f0a1b2c3d4e5f6a/outcome \
  -H "Authorization: Bearer $MLF_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "outcome": "booked", "value": 2480, "currency": "EUR", "ref": "OPP-10442" }'
FieldMeaning
outcomeRequired. open, booked or lost.
valueWhat it was worth, zero or more. Needs currency. Stored to two decimals and returned as a string, "2480.00".
currencyThree uppercase letters, such as EUR or USD.
atWhen it happened, ISO 8601 with an offset. Defaults to now.
refYour own id for the booking or opportunity, up to 128 characters.

The call replaces the whole outcome: a field you leave out is cleared, not kept. Send the full outcome every time, including when you only change the value. customer_id, your CRM's id for the person, is the exception: leaving it out keeps the stored one. The response is the updated lead.

Stuck on leads and outcomes?