BrightshoalDocsOpen console
MenuChat analytics

API reference

Chat analytics

Web chat health, quality, the visitor funnel, trends and incidents, with notification subscriptions.

List failed chat requests

GET/api/chat-analytics/failures

Each public chat request (config, events, start, send) that failed or was refused, with its status, outcome code, site, browser and request id, for chasing a broken embed. A malformed cursor is a 400.

Secret API key. How it works

Query parameters

  • project_idstring

    Only this project. An org-wide key covers every project by default.

  • assistant_idarray of string

    Only these assistants. Repeat the parameter or comma-separate the values; a row matching any of them is kept.

  • fromstringISO 8601 timestamp

    Default: 24 hours before to.

  • tostringISO 8601 timestamp

    Default: now. Ranges over 90 days are rejected.

  • cursorstring

    next_cursor from the previous page.

  • limitinteger1 to 100

    Page size.

    Default 50

Returns

200 OK

Response attributes
  • dataarray of ChatFailurerequired

    14 attributes
    • idstringrequired

    • occurred_atstringrequiredISO 8601 timestamp

    • routestringrequired

      One of config events create message

    • outcomestringrequired

    • status_codeintegerrequired

    • sitestring | null

    • sampledboolean

    • request_idstring | null

    • conversation_idstring | null

    • client_message_idstring | null

    • devicestring | null

    • browserstring | null

    • widget_versionstring | null

    • assistant_idstring | null

  • next_cursorstring | nullrequired

  • has_morebooleanrequired

  • 400 Validation error: usually invalid_request, with the failing fields in issues when there are any
  • 401 Missing, malformed, unknown or revoked API key (unauthorized)
  • 429 Rate limit exceeded (rate_limited). Wait Retry-After seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).
GET /api/chat-analytics/failures
curl "https://api.myleadsfactory.tech/api/chat-analytics/failures" \
  -H "Authorization: Bearer $MLF_API_KEY"
Response 200
{
  "data": [
    {
      "id": "id_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
      "occurred_at": "2026-10-28T09:30:00.000Z",
      "route": "config",
      "outcome": "string",
      "status_code": 1,
      "site": "string",
      "sampled": true,
      "request_id": "id_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
      "conversation_id": "conv_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
      "client_message_id": "msg_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
      "device": "string",
      "browser": "string",
      "widget_version": "string",
      "assistant_id": "asst_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f"
    }
  ],
  "next_cursor": "eyJ0IjoiMjAyNi0xMC0yOCJ9",
  "has_more": true
}

Get the visitor funnel

GET/api/chat-analytics/funnel

Page views at each stage (loaded, shown, opened, typed, sent), then sends, conversations, leads, handoffs and bookings, optionally split by a dimension and compared with the window before.

Secret API key. How it works

Query parameters

  • project_idstring

    Only this project. An org-wide key covers every project by default.

  • assistant_idarray of string

    Only these assistants. Repeat the parameter or comma-separate the values; a row matching any of them is kept.

  • fromstringISO 8601 timestamp

    Default: 24 hours before to.

  • tostringISO 8601 timestamp

    Default: now. Ranges over 90 days are rejected.

  • group_bystring

    Split the funnel into one group per value of this dimension.

    One of none site page device browser source hour open_reason visitor_type

    Default "none"

  • comparestring

    previous adds each group's counts for the window of the same length just before.

    One of previous

Returns

200 OK

Response attributes
  • dataChatFunnelrequired

    11 attributes
    • fromstringrequiredISO 8601 timestamp

    • tostringrequiredISO 8601 timestamp

    • group_bystringrequired

    • visits_sincestring | nullISO 8601 timestamp

      When this scope's first page view tied to a visit arrived; visit counts start here. Null before any did.

    • page_views_sincestring | nullISO 8601 timestamp

      When this scope's first page view the widget reported arrived; page-view stages start here. Null before any did.

    • groupsarray of objectrequired

      15 attributes
      • page_viewsobjectrequired

        Distinct page views per stage. Events without a page_view_id are excluded.

        5 attributes
        • loadedintegerrequired

        • shownintegerrequired

        • openedintegerrequired

        • typedintegerrequired

        • sentintegerrequired

      • visitsobjectrequired

        Distinct visits (a run of page views by one browser) per stage, with the same filters as page_views. Only events that carry a visit_id count: visits exist only with analytics consent and a current widget.

        5 attributes
        • loadedintegerrequired

        • shownintegerrequired

        • openedintegerrequired

        • typedintegerrequired

        • sentintegerrequired

      • visitorsintegerrequired

        Distinct visitor ids among widget_loaded events.

      • returning_visitsintegerrequired

        Distinct visits among widget_loaded events whose visit_number is above 1.

      • identified_page_viewsintegerrequired

        Distinct page views among widget_loaded events that carry a visit_id. Divide by tracked_page_views for coverage.

      • tracked_page_viewsinteger

        Distinct page views since visit tracking started for this scope (the response's visits_since); 0 before it started. The coverage denominator, so earlier, untracked weeks do not count as untracked traffic.

      • sendsobject | nullrequired

        5 attributes
        • pressedinteger

        • acceptedinteger

        • answeredinteger

        • failedinteger

        • refusedinteger

      • conversationsinteger | nullrequired

        Chats with a visitor message started in the window, in every grouping: a chat takes the page, source and device of the page view its first message was sent from, or the group untracked when there is none. Test, preview-host and internal chats are left out (see excluded).

      • leadsinteger | nullrequired

        Leads saved in the window, grouped as their chat is.

      • valid_leadsinteger

        Leads with at least one email or phone that is not a placeholder or the business's own number.

      • handoffsinteger | nullrequired

        Handoffs requested in the window, grouped as their chat is.

      • bookedinteger | nullrequired

      • convertedobject

        The conversion cohort: page views loaded in the window from which a chat started, and those whose chat saved a lead or a valid lead in the window. Divide by page_views.loaded for conversion; unlike the activity counts above, it leaves out chats with no page view or from before page tracking.

        3 attributes
        • conversationsintegerrequired

        • leadsintegerrequired

        • valid_leadsintegerrequired

      • keystring | nullrequired

      • previousobject

        13 attributes
        • page_viewsobjectrequired

          Distinct page views per stage. Events without a page_view_id are excluded.

          5 attributes
          • loadedintegerrequired

          • shownintegerrequired

          • openedintegerrequired

          • typedintegerrequired

          • sentintegerrequired

        • visitsobjectrequired

          Distinct visits (a run of page views by one browser) per stage, with the same filters as page_views. Only events that carry a visit_id count: visits exist only with analytics consent and a current widget.

          5 attributes
          • loadedintegerrequired

          • shownintegerrequired

          • openedintegerrequired

          • typedintegerrequired

          • sentintegerrequired

        • visitorsintegerrequired

          Distinct visitor ids among widget_loaded events.

        • returning_visitsintegerrequired

          Distinct visits among widget_loaded events whose visit_number is above 1.

        • identified_page_viewsintegerrequired

          Distinct page views among widget_loaded events that carry a visit_id. Divide by tracked_page_views for coverage.

        • tracked_page_viewsinteger

          Distinct page views since visit tracking started for this scope (the response's visits_since); 0 before it started. The coverage denominator, so earlier, untracked weeks do not count as untracked traffic.

        • sendsobject | nullrequired

          5 attributes
          • pressedinteger

          • acceptedinteger

          • answeredinteger

          • failedinteger

          • refusedinteger

        • conversationsinteger | nullrequired

          Chats with a visitor message started in the window, in every grouping: a chat takes the page, source and device of the page view its first message was sent from, or the group untracked when there is none. Test, preview-host and internal chats are left out (see excluded).

        • leadsinteger | nullrequired

          Leads saved in the window, grouped as their chat is.

        • valid_leadsinteger

          Leads with at least one email or phone that is not a placeholder or the business's own number.

        • handoffsinteger | nullrequired

          Handoffs requested in the window, grouped as their chat is.

        • bookedinteger | nullrequired

        • convertedobject

          The conversion cohort: page views loaded in the window from which a chat started, and those whose chat saved a lead or a valid lead in the window. Divide by page_views.loaded for conversion; unlike the activity counts above, it leaves out chats with no page view or from before page tracking.

          3 attributes
          • conversationsintegerrequired

          • leadsintegerrequired

          • valid_leadsintegerrequired

    • groups_totalintegerrequired

      Groups found; only the first 50 are returned (hours are never cut).

    • truncatedbooleanrequired

    • coverageobjectrequired

      Counted chats in the window, and those tied to a page view the widget reported.

      2 attributes
      • conversationsintegerrequired

      • trackedintegerrequired

    • excludedobjectrequired

      Chats in the window left out of every number, each under the first reason that applies: a test marker or test key, a preview or staging host, or an internal lead email.

      3 attributes
      • testintegerrequired

      • previewintegerrequired

      • internalintegerrequired

    • partialbooleanrequired

      True when not every send in the window could be read, so the counts are a lower bound. Retry, or narrow the range.

  • 400 Validation error: usually invalid_request, with the failing fields in issues when there are any
  • 401 Missing, malformed, unknown or revoked API key (unauthorized)
  • 429 Rate limit exceeded (rate_limited). Wait Retry-After seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).
GET /api/chat-analytics/funnel
curl "https://api.myleadsfactory.tech/api/chat-analytics/funnel" \
  -H "Authorization: Bearer $MLF_API_KEY"
Response 200
{
  "data": {
    "from": "2026-10-28T09:30:00.000Z",
    "to": "2026-10-28T09:30:00.000Z",
    "group_by": "string",
    "visits_since": "2026-10-28T09:30:00.000Z",
    "page_views_since": "2026-10-28T09:30:00.000Z",
    "groups": [
      {
        "page_views": {
          "loaded": 1,
          "shown": 1,
          "opened": 1,
          "typed": 1,
          "sent": 1
        },
        "visits": {
          "loaded": 1,
          "shown": 1,
          "opened": 1,
          "typed": 1,
          "sent": 1
        },
        "visitors": 1,
        "returning_visits": 1,
        "identified_page_views": 1,
        "tracked_page_views": 1,
        "sends": {
          "pressed": 1,
          "accepted": 1,
          "answered": 1,
          "failed": 1,
          "refused": 1
        },
        "conversations": 1,
        "leads": 1,
        "valid_leads": 1,
        "handoffs": 1,
        "booked": 1,
        "converted": {
          "conversations": 1,
          "leads": 1,
          "valid_leads": 1
        },
        "key": "string",
        "previous": {
          "page_views": {
            "loaded": {},
            "shown": {},
            "opened": {},
            "typed": {},
            "sent": {}
          },
          "visits": {
            "loaded": {},
            "shown": {},
            "opened": {},
            "typed": {},
            "sent": {}
          },
          "visitors": 1,
          "returning_visits": 1,
          "identified_page_views": 1,
          "tracked_page_views": 1,
          "sends": {
            "pressed": {},
            "accepted": {},
            "answered": {},
            "failed": {},
            "refused": {}
          },
          "conversations": 1,
          "leads": 1,
          "valid_leads": 1,
          "handoffs": 1,
          "booked": 1,
          "converted": {
            "conversations": {},
            "leads": {},
            "valid_leads": {}
          }
        }
      }
    ],
    "groups_total": 1,
    "truncated": true,
    "coverage": {
      "conversations": 1,
      "tracked": 1
    },
    "excluded": {
      "test": 1,
      "preview": 1,
      "internal": 1
    },
    "partial": true
  }
}

Get chat health by site

GET/api/chat-analytics/health

For each site the chat runs on: page views, shown and opened panels, sends, failures and refusals, reply time percentiles, the last successful reply and open incidents, with a status (ok, warning, critical, idle). Defaults to the last 24 hours.

Secret API key. How it works

Query parameters

  • project_idstring

    Only this project. An org-wide key covers every project by default.

  • assistant_idarray of string

    Only these assistants. Repeat the parameter or comma-separate the values; a row matching any of them is kept.

  • fromstringISO 8601 timestamp

    Default: 24 hours before to.

  • tostringISO 8601 timestamp

    Default: now. Ranges over 90 days are rejected.

Returns

200 OK

Response attributes
  • dataChatHealthrequired

    3 attributes
    • checkerobjectrequired

      2 attributes
      • swept_atstring | nullrequiredISO 8601 timestamp

      • stalebooleanrequired

    • partialbooleanrequired

      True when not every send in the window could be read, so the counts are a lower bound. Retry, or narrow the range.

    • sitesarray of objectrequired

      14 attributes
      • sitestring | nullrequired

      • statusstringrequired

        One of ok warning critical idle

      • page_viewsintegerrequired

      • showninteger

      • openedinteger

      • sendsintegerrequired

      • acceptedinteger

      • failedinteger

      • refusedinteger

      • failure_ratenumber | null

      • reply_ms_p50integer | null

      • reply_ms_p90integer | null

      • last_success_atstring | nullISO 8601 timestamp

      • open_incidentsintegerrequired

  • 400 Validation error: usually invalid_request, with the failing fields in issues when there are any
  • 401 Missing, malformed, unknown or revoked API key (unauthorized)
  • 429 Rate limit exceeded (rate_limited). Wait Retry-After seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).
GET /api/chat-analytics/health
curl "https://api.myleadsfactory.tech/api/chat-analytics/health" \
  -H "Authorization: Bearer $MLF_API_KEY"
Response 200
{
  "data": {
    "checker": {
      "swept_at": "2026-10-28T09:30:00.000Z",
      "stale": true
    },
    "partial": true,
    "sites": [
      {
        "site": "string",
        "status": "ok",
        "page_views": 1,
        "shown": 1,
        "opened": 1,
        "sends": 1,
        "accepted": 1,
        "failed": 1,
        "refused": 1,
        "failure_rate": 1,
        "reply_ms_p50": 640,
        "reply_ms_p90": 640,
        "last_success_at": "2026-10-28T09:30:00.000Z",
        "open_incidents": 1
      }
    ]
  }
}

List chat health incidents

GET/api/chat-analytics/incidents

Problems the health checker opened on its own (for example a site whose sends started failing), newest activity first. An incident resolves itself when the site recovers or goes quiet, or by hand.

Secret API key. How it works

Query parameters

  • project_idstring

    Only this project. An org-wide key covers every project by default.

  • assistant_idarray of string

    Only these assistants' incidents. Repeat the parameter or comma-separate the values; a row matching any of them is kept.

  • statusstring

    Which incidents to list.

    One of open resolved all

    Default "open"

  • cursorstring

    next_cursor from the previous page.

  • limitinteger1 to 100

    Page size.

    Default 50

Returns

200 OK

Response attributes
  • dataarray of ChatIncidentrequired

    23 attributes
    • idstringrequired

    • org_idstring

    • project_idstringrequired

    • assistant_idstring | null

    • widget_key_idstring | null

    • sitestring | null

    • rulestringrequired

    • severitystringrequired

      One of info warning critical

    • statusstringrequired

      One of open resolved

    • dedupe_keystring

    • first_seen_atstringISO 8601 timestamp

    • last_seen_atstringISO 8601 timestamp

    • occurrencesinteger

    • example_request_idstring | null

    • example_conversation_idstring | null

    • detailobject | null

    • muted_untilstring | nullISO 8601 timestamp

    • muted_bystring | null

    • resolved_atstring | nullISO 8601 timestamp

    • resolutionstring | null

      One of recovered quiet manual

    • resolved_bystring | null

    • created_atstringISO 8601 timestamp

    • updated_atstringISO 8601 timestamp

  • next_cursorstring | nullrequired

  • has_morebooleanrequired

  • 400 Validation error: usually invalid_request, with the failing fields in issues when there are any
  • 401 Missing, malformed, unknown or revoked API key (unauthorized)
  • 429 Rate limit exceeded (rate_limited). Wait Retry-After seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).
GET /api/chat-analytics/incidents
curl "https://api.myleadsfactory.tech/api/chat-analytics/incidents" \
  -H "Authorization: Bearer $MLF_API_KEY"
Response 200
{
  "data": [
    {
      "id": "id_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
      "org_id": "org_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
      "project_id": "proj_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
      "assistant_id": "asst_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
      "widget_key_id": "id_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
      "site": "string",
      "rule": "string",
      "severity": "info",
      "status": "open",
      "dedupe_key": "string",
      "first_seen_at": "2026-10-28T09:30:00.000Z",
      "last_seen_at": "2026-10-28T09:30:00.000Z",
      "occurrences": 1,
      "example_request_id": "id_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
      "example_conversation_id": "conv_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
      "detail": {},
      "muted_until": "2026-10-28T09:30:00.000Z",
      "muted_by": "string",
      "resolved_at": "2026-10-28T09:30:00.000Z",
      "resolution": "recovered",
      "resolved_by": "string",
      "created_at": "2026-10-28T09:30:00.000Z",
      "updated_at": "2026-10-28T09:30:00.000Z"
    }
  ],
  "next_cursor": "eyJ0IjoiMjAyNi0xMC0yOCJ9",
  "has_more": true
}

Mute an incident

POST/api/chat-analytics/incidents/{id}/mute

Stops notifications for this incident for 15 minutes to 7 days. The incident stays open and keeps counting.

Secret API key, not read-only. How it works

Path parameters

  • idstringrequired

    Incident id.

Request body

  • minutesintegerrequired15 to 10080

Returns

200 OK

Response attributes
  • dataChatIncidentrequired

    23 attributes
    • idstringrequired

    • org_idstring

    • project_idstringrequired

    • assistant_idstring | null

    • widget_key_idstring | null

    • sitestring | null

    • rulestringrequired

    • severitystringrequired

      One of info warning critical

    • statusstringrequired

      One of open resolved

    • dedupe_keystring

    • first_seen_atstringISO 8601 timestamp

    • last_seen_atstringISO 8601 timestamp

    • occurrencesinteger

    • example_request_idstring | null

    • example_conversation_idstring | null

    • detailobject | null

    • muted_untilstring | nullISO 8601 timestamp

    • muted_bystring | null

    • resolved_atstring | nullISO 8601 timestamp

    • resolutionstring | null

      One of recovered quiet manual

    • resolved_bystring | null

    • created_atstringISO 8601 timestamp

    • updated_atstringISO 8601 timestamp

  • 400 Validation error: usually invalid_request, with the failing fields in issues when there are any
  • 401 Missing, malformed, unknown or revoked API key (unauthorized)
  • 403 Refused, for example a read-only key on a write (read_only_key), a role too low for the route (insufficient_role), or a route that needs a console session
  • 404 Not found, or outside what the key can see
  • 429 Rate limit exceeded (rate_limited). Wait Retry-After seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).
POST /api/chat-analytics/incidents/{id}/mute
curl -X POST "https://api.myleadsfactory.tech/api/chat-analytics/incidents/INCIDENT_ID/mute" \
  -H "Authorization: Bearer $MLF_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "minutes": 15
}'
Response 200
{
  "data": {
    "id": "id_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
    "org_id": "org_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
    "project_id": "proj_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
    "assistant_id": "asst_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
    "widget_key_id": "id_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
    "site": "string",
    "rule": "string",
    "severity": "info",
    "status": "open",
    "dedupe_key": "string",
    "first_seen_at": "2026-10-28T09:30:00.000Z",
    "last_seen_at": "2026-10-28T09:30:00.000Z",
    "occurrences": 1,
    "example_request_id": "id_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
    "example_conversation_id": "conv_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
    "detail": {},
    "muted_until": "2026-10-28T09:30:00.000Z",
    "muted_by": "string",
    "resolved_at": "2026-10-28T09:30:00.000Z",
    "resolution": "recovered",
    "resolved_by": "string",
    "created_at": "2026-10-28T09:30:00.000Z",
    "updated_at": "2026-10-28T09:30:00.000Z"
  }
}

Resolve an incident

POST/api/chat-analytics/incidents/{id}/resolve

Closes the incident as manual. No request body. 409 already_resolved if it is closed already; a new failure opens a new incident.

Secret API key, not read-only. How it works

Path parameters

  • idstringrequired

    Incident id.

Returns

200 OK

Response attributes
  • dataChatIncidentrequired

    23 attributes
    • idstringrequired

    • org_idstring

    • project_idstringrequired

    • assistant_idstring | null

    • widget_key_idstring | null

    • sitestring | null

    • rulestringrequired

    • severitystringrequired

      One of info warning critical

    • statusstringrequired

      One of open resolved

    • dedupe_keystring

    • first_seen_atstringISO 8601 timestamp

    • last_seen_atstringISO 8601 timestamp

    • occurrencesinteger

    • example_request_idstring | null

    • example_conversation_idstring | null

    • detailobject | null

    • muted_untilstring | nullISO 8601 timestamp

    • muted_bystring | null

    • resolved_atstring | nullISO 8601 timestamp

    • resolutionstring | null

      One of recovered quiet manual

    • resolved_bystring | null

    • created_atstringISO 8601 timestamp

    • updated_atstringISO 8601 timestamp

  • 401 Missing, malformed, unknown or revoked API key (unauthorized)
  • 403 Refused, for example a read-only key on a write (read_only_key), a role too low for the route (insufficient_role), or a route that needs a console session
  • 404 Not found, or outside what the key can see
  • 409 Conflict
  • 429 Rate limit exceeded (rate_limited). Wait Retry-After seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).
POST /api/chat-analytics/incidents/{id}/resolve
curl -X POST "https://api.myleadsfactory.tech/api/chat-analytics/incidents/INCIDENT_ID/resolve" \
  -H "Authorization: Bearer $MLF_API_KEY"
Response 200
{
  "data": {
    "id": "id_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
    "org_id": "org_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
    "project_id": "proj_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
    "assistant_id": "asst_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
    "widget_key_id": "id_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
    "site": "string",
    "rule": "string",
    "severity": "info",
    "status": "open",
    "dedupe_key": "string",
    "first_seen_at": "2026-10-28T09:30:00.000Z",
    "last_seen_at": "2026-10-28T09:30:00.000Z",
    "occurrences": 1,
    "example_request_id": "id_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
    "example_conversation_id": "conv_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
    "detail": {},
    "muted_until": "2026-10-28T09:30:00.000Z",
    "muted_by": "string",
    "resolved_at": "2026-10-28T09:30:00.000Z",
    "resolution": "recovered",
    "resolved_by": "string",
    "created_at": "2026-10-28T09:30:00.000Z",
    "updated_at": "2026-10-28T09:30:00.000Z"
  }
}

Unmute an incident

POST/api/chat-analytics/incidents/{id}/unmute

Resumes notifications for a muted incident. No request body.

Secret API key, not read-only. How it works

Path parameters

  • idstringrequired

    Incident id.

Returns

200 OK

Response attributes
  • dataChatIncidentrequired

    23 attributes
    • idstringrequired

    • org_idstring

    • project_idstringrequired

    • assistant_idstring | null

    • widget_key_idstring | null

    • sitestring | null

    • rulestringrequired

    • severitystringrequired

      One of info warning critical

    • statusstringrequired

      One of open resolved

    • dedupe_keystring

    • first_seen_atstringISO 8601 timestamp

    • last_seen_atstringISO 8601 timestamp

    • occurrencesinteger

    • example_request_idstring | null

    • example_conversation_idstring | null

    • detailobject | null

    • muted_untilstring | nullISO 8601 timestamp

    • muted_bystring | null

    • resolved_atstring | nullISO 8601 timestamp

    • resolutionstring | null

      One of recovered quiet manual

    • resolved_bystring | null

    • created_atstringISO 8601 timestamp

    • updated_atstringISO 8601 timestamp

  • 401 Missing, malformed, unknown or revoked API key (unauthorized)
  • 403 Refused, for example a read-only key on a write (read_only_key), a role too low for the route (insufficient_role), or a route that needs a console session
  • 404 Not found, or outside what the key can see
  • 429 Rate limit exceeded (rate_limited). Wait Retry-After seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).
POST /api/chat-analytics/incidents/{id}/unmute
curl -X POST "https://api.myleadsfactory.tech/api/chat-analytics/incidents/INCIDENT_ID/unmute" \
  -H "Authorization: Bearer $MLF_API_KEY"
Response 200
{
  "data": {
    "id": "id_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
    "org_id": "org_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
    "project_id": "proj_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
    "assistant_id": "asst_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
    "widget_key_id": "id_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
    "site": "string",
    "rule": "string",
    "severity": "info",
    "status": "open",
    "dedupe_key": "string",
    "first_seen_at": "2026-10-28T09:30:00.000Z",
    "last_seen_at": "2026-10-28T09:30:00.000Z",
    "occurrences": 1,
    "example_request_id": "id_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
    "example_conversation_id": "conv_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
    "detail": {},
    "muted_until": "2026-10-28T09:30:00.000Z",
    "muted_by": "string",
    "resolved_at": "2026-10-28T09:30:00.000Z",
    "resolution": "recovered",
    "resolved_by": "string",
    "created_at": "2026-10-28T09:30:00.000Z",
    "updated_at": "2026-10-28T09:30:00.000Z"
  }
}

Label how already analysed chats ended (staff only)

POST/api/chat-analytics/outcomes/backfill

Queues ended, counted web chats in the window whose analysis has no outcome label for an outcome-only run. It sends nothing to any CRM or webhook. At most 500 per call; call again while remaining is true.

Secret API key, not read-only. How it works

Query parameters

  • project_idstring

    Only this project. An org-wide key covers every project by default.

  • assistant_idarray of string

    Only these assistants. Repeat the parameter or comma-separate the values; a row matching any of them is kept.

  • fromstringISO 8601 timestamp

    Default: 24 hours before to.

  • tostringISO 8601 timestamp

    Default: now. Ranges over 90 days are rejected.

Returns

200 OK

Response attributes
  • dataChatOutcomesBackfillrequired

    3 attributes
    • queuedintegerrequired

    • candidatesintegerrequired

    • remainingbooleanrequired

      More chats in the window still need a label: call again.

  • 400 Validation error: usually invalid_request, with the failing fields in issues when there are any
  • 401 Missing, malformed, unknown or revoked API key (unauthorized)
  • 403 Refused, for example a read-only key on a write (read_only_key), a role too low for the route (insufficient_role), or a route that needs a console session
  • 429 Rate limit exceeded (rate_limited). Wait Retry-After seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).
POST /api/chat-analytics/outcomes/backfill
curl -X POST "https://api.myleadsfactory.tech/api/chat-analytics/outcomes/backfill" \
  -H "Authorization: Bearer $MLF_API_KEY"
Response 200
{
  "data": {
    "queued": 1,
    "candidates": 1,
    "remaining": true
  }
}

Get chat quality

GET/api/chat-analytics/quality

How well the chat is doing for visitors: reply and first-token time, missing replies, handoffs answered, how complete leads are, cost per lead, the assistant lines visitors most often stop after, and improvement (reply feedback, conversation ratings, containment, unanswered knowledge questions, unhelpful answers and comments, with emails, phone numbers and common name patterns masked).

Secret API key. How it works

Query parameters

  • project_idstring

    Only this project. An org-wide key covers every project by default.

  • assistant_idarray of string

    Only these assistants. Repeat the parameter or comma-separate the values; a row matching any of them is kept.

  • fromstringISO 8601 timestamp

    Default: 24 hours before to.

  • tostringISO 8601 timestamp

    Default: now. Ranges over 90 days are rejected.

Returns

200 OK

Response attributes
  • dataChatQualityrequired

    13 attributes
    • reply_msobject

      7 attributes
      • bucketsarray of object

        2 attributes
        • le_msinteger | nullrequired

        • countintegerrequired

      • p50integer | null

      • p90integer | null

      • p95integer | null

      • p99integer | null

      • samplesinteger

        Replies measured.

      • slowinteger

        Replies slower than 15 seconds.

    • first_token_msobject

      2 attributes
      • p50integer | null

      • p90integer | null

    • repliesobject

      3 attributes
      • answeredinteger

      • missinginteger

      • rescuedinteger

    • sendsobject

      3 attributes
      • totalinteger

      • failedinteger

      • quick_reply_sharenumber | null

    • handoffobject

      4 attributes
      • requestedinteger

      • answeredinteger

        Taken over by an advisor.

      • repliedinteger

        An advisor wrote after the request.

      • median_answer_secondsinteger | null

    • leadsobject

      10 attributes
      • totalinteger

      • with_emailinteger

      • with_phoneinteger

      • with_nameinteger

      • validinteger

        With one email or phone that is not a placeholder or the business's own number.

      • flagsobject

        3 attributes
        • placeholder_emailinteger

        • placeholder_phoneinteger

        • business_phoneinteger

      • required_completeinteger | null

        Leads holding every required field; null when no assistant marks any.

      • bookedinteger

      • lostinteger

      • revenuearray of object

        2 attributes
        • currencystringrequired

        • amountnumberrequired

    • costobject

      2 attributes
      • totalarray of object

        2 attributes
        • currencystringrequired

        • amountnumberrequired

      • per_leadarray of object

        2 attributes
        • currencystringrequired

        • amountnumberrequired

    • stop_pointsarray of object

      Where ended chats stopped while the assistant waited on the visitor, by what it had asked (from the analysis labels).

      3 attributes
      • keystringrequired

      • textstringrequired

      • conversationsintegerrequired

    • outcomesobject

      How ended chats ended, from the analysis labels. Counts by label; labelled of ended were labelled.

      7 attributes
      • endedinteger

      • labelledinteger

      • intentsobject

      • goalobject

      • waiting_onobject

      • contact_declinedinteger

      • frictionobject

    • toolsarray of object

      Tool calls in the window's chats. expected failures are the tool asking for what is missing; the rest are the platform failing.

      5 attributes
      • namestring

      • callsinteger

      • failedinteger

      • expectedinteger

      • errorsobject

    • knowledgeobject

      Knowledge searches by result: unavailable is the search failing, empty is missing content.

      5 attributes
      • searchesinteger

      • okinteger

      • emptyinteger

      • unavailableinteger

      • otherinteger

    • improvementChatImprovementrequired

      What visitors said about the chat, and where it could do better. Quoted text is one line, at most 200 characters, with emails and phone numbers masked, and names masked where they follow common patterns (an introduction such as "my name is" or a greeting such as "Thanks Jane"). Other personal details can remain.

      6 attributes
      • repliesobjectrequired

        Thumbs up and down on assistant replies.

        4 attributes
        • ratedintegerrequired

        • upintegerrequired

        • downintegerrequired

        • reasonsobjectrequired

          Thumbs-down count per reason.

      • chats_ratedobjectrequired

        3 attributes
        • countintegerrequired

        • averagenumber | nullrequired

        • distributionarray of integerrequired

          Count of 1 to 5 star ratings, in order.

      • containmentobjectrequired

        5 attributes
        • chatsintegerrequired

        • without_personintegerrequired

          Never asked for a person.

        • with_leadintegerrequired

        • asked_for_personintegerrequired

        • one_messageintegerrequired

          Conversations, open or ended, with exactly one visitor message and no lead.

      • knowledge_gapsobjectrequired

        Replies whose knowledge searches all came back empty.

        2 attributes
        • totalintegerrequired

        • recentarray of objectrequired

          3 attributes
          • questionstringrequired

          • conversation_idstringrequired

          • atstringrequiredISO 8601 timestamp

      • unhelpfularray of objectrequired

        Recent replies rated down.

        4 attributes
        • answerstringrequired

        • reasonstring | nullrequired

        • conversation_idstringrequired

        • atstringrequiredISO 8601 timestamp

      • commentsarray of objectrequired

        Recent conversation ratings that came with a comment.

        4 attributes
        • ratingintegerrequired

        • commentstringrequired

        • conversation_idstringrequired

        • atstringrequiredISO 8601 timestamp

    • partialbooleanrequired

      True when not every send in the window could be read, so the counts are a lower bound. Retry, or narrow the range.

  • 400 Validation error: usually invalid_request, with the failing fields in issues when there are any
  • 401 Missing, malformed, unknown or revoked API key (unauthorized)
  • 429 Rate limit exceeded (rate_limited). Wait Retry-After seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).
GET /api/chat-analytics/quality
curl "https://api.myleadsfactory.tech/api/chat-analytics/quality" \
  -H "Authorization: Bearer $MLF_API_KEY"
Response 200
{
  "data": {
    "reply_ms": {
      "buckets": [
        {
          "le_ms": 640,
          "count": 1
        }
      ],
      "p50": 1,
      "p90": 1,
      "p95": 1,
      "p99": 1,
      "samples": 1,
      "slow": 1
    },
    "first_token_ms": {
      "p50": 1,
      "p90": 1
    },
    "replies": {
      "answered": 1,
      "missing": 1,
      "rescued": 1
    },
    "sends": {
      "total": 1,
      "failed": 1,
      "quick_reply_share": 1
    },
    "handoff": {
      "requested": 1,
      "answered": 1,
      "replied": 1,
      "median_answer_seconds": 1
    },
    "leads": {
      "total": 1,
      "with_email": 1,
      "with_phone": 1,
      "with_name": 1,
      "valid": 1,
      "flags": {
        "placeholder_email": 1,
        "placeholder_phone": 1,
        "business_phone": 1
      },
      "required_complete": 1,
      "booked": 1,
      "lost": 1,
      "revenue": [
        {
          "currency": "string",
          "amount": 1
        }
      ]
    },
    "cost": {
      "total": [
        {
          "currency": "string",
          "amount": 1
        }
      ],
      "per_lead": [
        {
          "currency": "string",
          "amount": 1
        }
      ]
    },
    "stop_points": [
      {
        "key": "string",
        "text": "Hi, can I book a trip to Lisbon in May?",
        "conversations": 1
      }
    ],
    "outcomes": {
      "ended": 1,
      "labelled": 1,
      "intents": {
        "key": 1
      },
      "goal": {
        "key": 1
      },
      "waiting_on": {
        "key": 1
      },
      "contact_declined": 1,
      "friction": {
        "key": 1
      }
    },
    "tools": [
      {
        "name": "Front desk",
        "calls": 1,
        "failed": 1,
        "expected": 1,
        "errors": {
          "key": 1
        }
      }
    ],
    "knowledge": {
      "searches": 1,
      "ok": 1,
      "empty": 1,
      "unavailable": 1,
      "other": 1
    },
    "improvement": {
      "replies": {
        "rated": 1,
        "up": 1,
        "down": 1,
        "reasons": {
          "key": 1
        }
      },
      "chats_rated": {
        "count": 1,
        "average": 1,
        "distribution": [
          1
        ]
      },
      "containment": {
        "chats": 1,
        "without_person": 1,
        "with_lead": 1,
        "asked_for_person": 1,
        "one_message": 1
      },
      "knowledge_gaps": {
        "total": 1,
        "recent": [
          {
            "question": {},
            "conversation_id": {},
            "at": {}
          }
        ]
      },
      "unhelpful": [
        {
          "answer": "string",
          "reason": "string",
          "conversation_id": "conv_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
          "at": "2026-10-28T09:30:00.000Z"
        }
      ],
      "comments": [
        {
          "rating": 1,
          "comment": "string",
          "conversation_id": "conv_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
          "at": "2026-10-28T09:30:00.000Z"
        }
      ]
    },
    "partial": true
  }
}

List notification subscriptions

GET/api/chat-analytics/subscriptions

Where health alerts and the digest are sent: Slack incoming webhooks or your own HTTPS endpoint. Needs the admin role, which API keys have.

Secret API key. How it works

Query parameters

  • project_idstring

    Only this project. An org-wide key covers every project by default.

Returns

200 OK

Response attributes
  • dataarray of ChatSubscriptionrequired

    9 attributes
    • idstringrequired

    • org_idstring

    • project_idstringrequired

    • kindstringrequired

      One of health digest

    • channelChatNotificationChannelrequired

      2 attributes
      • typestringrequired

        One of slack webhook

      • urlstringrequireduri

    • enabledbooleanrequired

    • timezonestringrequired

    • created_atstringISO 8601 timestamp

    • updated_atstringISO 8601 timestamp

  • 401 Missing, malformed, unknown or revoked API key (unauthorized)
  • 403 Refused, for example a read-only key on a write (read_only_key), a role too low for the route (insufficient_role), or a route that needs a console session
  • 429 Rate limit exceeded (rate_limited). Wait Retry-After seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).
GET /api/chat-analytics/subscriptions
curl "https://api.myleadsfactory.tech/api/chat-analytics/subscriptions" \
  -H "Authorization: Bearer $MLF_API_KEY"
Response 200
{
  "data": [
    {
      "id": "whsub_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
      "org_id": "org_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
      "project_id": "proj_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
      "kind": "health",
      "channel": {
        "type": "slack",
        "url": "https://example.com/hook"
      },
      "enabled": true,
      "timezone": "string",
      "created_at": "2026-10-28T09:30:00.000Z",
      "updated_at": "2026-10-28T09:30:00.000Z"
    }
  ]
}

Create a notification subscription

POST/api/chat-analytics/subscriptions

Sends health alerts (incidents opening and resolving) or a digest summary to a Slack incoming webhook or an HTTPS URL. An org-wide key must name project_id.

Secret API key, not read-only. How it works

Request body

  • project_idstring

  • kindstringrequired

    One of health digest

  • channelChatNotificationChannelrequired

    2 attributes
    • typestringrequired

      One of slack webhook

    • urlstringrequireduri

  • timezonestring

    IANA zone; default UTC.

Returns

201 OK

Response attributes
  • dataChatSubscriptionrequired

    9 attributes
    • idstringrequired

    • org_idstring

    • project_idstringrequired

    • kindstringrequired

      One of health digest

    • channelChatNotificationChannelrequired

      2 attributes
      • typestringrequired

        One of slack webhook

      • urlstringrequireduri

    • enabledbooleanrequired

    • timezonestringrequired

    • created_atstringISO 8601 timestamp

    • updated_atstringISO 8601 timestamp

  • 400 Validation error: usually invalid_request, with the failing fields in issues when there are any
  • 401 Missing, malformed, unknown or revoked API key (unauthorized)
  • 403 Refused, for example a read-only key on a write (read_only_key), a role too low for the route (insufficient_role), or a route that needs a console session
  • 404 Not found, or outside what the key can see
  • 429 Rate limit exceeded (rate_limited). Wait Retry-After seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).
POST /api/chat-analytics/subscriptions
curl -X POST "https://api.myleadsfactory.tech/api/chat-analytics/subscriptions" \
  -H "Authorization: Bearer $MLF_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "kind": "health",
  "channel": {
    "type": "slack",
    "url": "https://example.com/hook"
  }
}'
Response 201
{
  "data": {
    "id": "whsub_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
    "org_id": "org_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
    "project_id": "proj_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
    "kind": "health",
    "channel": {
      "type": "slack",
      "url": "https://example.com/hook"
    },
    "enabled": true,
    "timezone": "string",
    "created_at": "2026-10-28T09:30:00.000Z",
    "updated_at": "2026-10-28T09:30:00.000Z"
  }
}

Update a notification subscription

PATCH/api/chat-analytics/subscriptions/{id}

Turn a subscription on or off, or change where it sends or its time zone. Send at least one field.

Secret API key, not read-only. How it works

Path parameters

  • idstringrequired

    Subscription id.

Request body

  • enabledboolean

  • channelChatNotificationChannel

    2 attributes
    • typestringrequired

      One of slack webhook

    • urlstringrequireduri

  • timezonestring

Returns

200 OK

Response attributes
  • dataChatSubscriptionrequired

    9 attributes
    • idstringrequired

    • org_idstring

    • project_idstringrequired

    • kindstringrequired

      One of health digest

    • channelChatNotificationChannelrequired

      2 attributes
      • typestringrequired

        One of slack webhook

      • urlstringrequireduri

    • enabledbooleanrequired

    • timezonestringrequired

    • created_atstringISO 8601 timestamp

    • updated_atstringISO 8601 timestamp

  • 400 Validation error: usually invalid_request, with the failing fields in issues when there are any
  • 401 Missing, malformed, unknown or revoked API key (unauthorized)
  • 403 Refused, for example a read-only key on a write (read_only_key), a role too low for the route (insufficient_role), or a route that needs a console session
  • 404 Not found, or outside what the key can see
  • 429 Rate limit exceeded (rate_limited). Wait Retry-After seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).
PATCH /api/chat-analytics/subscriptions/{id}
curl -X PATCH "https://api.myleadsfactory.tech/api/chat-analytics/subscriptions/SUBSCRIPTION_ID" \
  -H "Authorization: Bearer $MLF_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "enabled": true,
  "channel": {
    "type": "slack",
    "url": "https://example.com/hook"
  }
}'
Response 200
{
  "data": {
    "id": "whsub_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
    "org_id": "org_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
    "project_id": "proj_5f0c2a9e7b1d4c3e8a6f0b2d4c6e8a1f",
    "kind": "health",
    "channel": {
      "type": "slack",
      "url": "https://example.com/hook"
    },
    "enabled": true,
    "timezone": "string",
    "created_at": "2026-10-28T09:30:00.000Z",
    "updated_at": "2026-10-28T09:30:00.000Z"
  }
}

Delete a notification subscription

DELETE/api/chat-analytics/subscriptions/{id}

Stops the subscription for good. Answers 204 with no body.

Secret API key, not read-only. How it works

Path parameters

  • idstringrequired

    Subscription id.

Returns

204 Deleted

  • 401 Missing, malformed, unknown or revoked API key (unauthorized)
  • 403 Refused, for example a read-only key on a write (read_only_key), a role too low for the route (insufficient_role), or a route that needs a console session
  • 404 Not found, or outside what the key can see
  • 429 Rate limit exceeded (rate_limited). Wait Retry-After seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).
DELETE /api/chat-analytics/subscriptions/{id}
curl -X DELETE "https://api.myleadsfactory.tech/api/chat-analytics/subscriptions/SUBSCRIPTION_ID" \
  -H "Authorization: Bearer $MLF_API_KEY"

Get daily or weekly chat totals

GET/api/chat-analytics/trend

Funnel totals per day or per ISO week (weeks start on Monday), for charts over a longer range than the live views.

Secret API key. How it works

Query parameters

  • project_idstring

    Only this project. An org-wide key covers every project by default.

  • assistant_idarray of string

    Only these assistants. Repeat the parameter or comma-separate the values; a row matching any of them is kept.

  • fromstringISO 8601 timestamp

    Default: 90 days before to.

  • tostringISO 8601 timestamp

    Default: now. Up to 400 days.

  • intervalstring

    One point per day or per week.

    One of day week

    Default "day"

Returns

200 OK

Response attributes
  • dataChatTrendrequired

    2 attributes
    • intervalstringrequired

      One of day week

    • pointsarray of objectrequired

      10 attributes
      • periodstringrequired

        YYYY-MM-DD; a Monday for weekly points.

      • page_viewsinteger

      • showninteger

      • openedinteger

      • sendsinteger

      • acceptedinteger

      • failedinteger

      • conversationsinteger

      • leadsinteger

      • handoffsinteger

  • 400 Validation error: usually invalid_request, with the failing fields in issues when there are any
  • 401 Missing, malformed, unknown or revoked API key (unauthorized)
  • 429 Rate limit exceeded (rate_limited). Wait Retry-After seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).
GET /api/chat-analytics/trend
curl "https://api.myleadsfactory.tech/api/chat-analytics/trend" \
  -H "Authorization: Bearer $MLF_API_KEY"
Response 200
{
  "data": {
    "interval": "day",
    "points": [
      {
        "period": "string",
        "page_views": 1,
        "shown": 1,
        "opened": 1,
        "sends": 1,
        "accepted": 1,
        "failed": 1,
        "conversations": 1,
        "leads": 1,
        "handoffs": 1
      }
    ]
  }
}