# Insights

Funnel, per campaign and daily numbers.

Source: https://docs.leadarc.io/reference-insights.html

- [GET /v1/workspaces/{ws}/insights/summary](#get-v1-workspaces-ws-insights-summary)
- [GET /v1/workspaces/{ws}/insights/campaigns](#get-v1-workspaces-ws-insights-campaigns)
- [GET /v1/workspaces/{ws}/insights/daily](#get-v1-workspaces-ws-insights-daily)

## GET /v1/workspaces/{ws}/insights/summary

`insights:read` · read bucket · returns insights_summary

Funnel, rates, speed to lead, per-rep

```bash
curl -sS https://api.leadarc.io/v1/workspaces/airpay/insights/summary?limit=25 \
  -H "Authorization: Bearer $LEADARC_API_KEY"
```

**Query parameters.** Anything else is [`400 unsupported_filter`](errors.html#status-400).

`from` `member_id` `to`

**Errors from this endpoint**, on top of the [common ones](errors.html): [`invalid_request`](errors.html#status-400).

```json
{
  "object": "insights_summary",
  "workspace": "airpay",
  "window": { "from": "2026-07-01T00:00:00Z", "to": "2026-07-27T00:00:00Z" },
  "funnel": {
    "all_responses": 412,
    "positive_responses": 96,
    "booked_meetings": 31,
    "showed_meetings": 22
  },
  "headline": {
    "total_meetings_booked": 31,
    "meetings_with_outcome": 27,
    "show_rate": 0.815,
    "total_interested_leads": 96,
    "booked_interested_leads": 31,
    "booking_rate": 0.323
  },
  "speed_to_lead": {
    "first_response_avg_sec": 2040,
    "first_response_count": 388,
    "followup_avg_sec": 5220,
    "followup_count": 611
  },
  "per_rep": [{
    "member_id": "usr_zaDRRpdu8EPZSU3i89lvZgKPkVQo4uBFA_KOhm",
    "name": "Can Ersöz",
    "meetings_booked": 18,
    "showed_meetings": 14,
    "meetings_with_outcome": 16,
    "show_rate": 0.875,
    "interested_leads": 54,
    "booked_from_interested": 18,
    "booking_rate": 0.333,
    "first_response_avg_sec": 1680,
    "first_response_count": 210,
    "followup_avg_sec": 4980,
    "followup_count": 330
  }],
  "speed_buckets": [
    { "label": "<2m", "n": 44, "booked": 21, "showed": 18, "booking_rate": 0.477, "show_rate": 0.857 }
  ],
  "generated_at": "2026-07-27T09:50:00Z"
}
```
Every field name maps 1:1 to the `Insights`, `RepStat`, and `SpeedBucket` types in the app, snake-cased. **All rates are ratios in `[0,1]`, never percentages.** A caller multiplying by 100 twice is a documentation failure we avoid by never shipping a percent.

Two accuracy notes carried over from the source, because they are already in the code comments and a report built on them should say so:

- `per_rep` booking-rate inputs are approximate. Interested is attributed through `action_log`, booked through `scheduled_meetings.booked_by_id`, and those two can disagree for the same rep.
- `speed_to_lead` is deploy-forward only. `lead_response_times` rows exist from the day the feature shipped, so a window that reaches earlier will under-count.

The 366-day cap exists because these are aggregate scans over `responses_index` with no materialized rollup. Over the cap is `400 invalid_request`.

## GET /v1/workspaces/{ws}/insights/campaigns

`insights:read` · upstream bucket · returns list of campaign_insight

Per-campaign and per-step metrics

```bash
curl -sS https://api.leadarc.io/v1/workspaces/airpay/insights/campaigns?limit=25 \
  -H "Authorization: Bearer $LEADARC_API_KEY"
```

**Query parameters.** Anything else is `400 unsupported_filter`.

`campaign_id` `cursor` `from` `limit` `to`

**Errors from this endpoint**, on top of the [common ones](errors.html): `invalid_request`, [`not_found`](errors.html#status-404), [`upstream_error`](errors.html#status-502).

```json
{
  "object": "list",
  "data": [{
    "object": "campaign_insight",
    "campaign_id": "cmp_HWO-6oxZflAfh5iwoKTzqjgycG_C8wMff6Galz",
    "campaign_name": "Senior Living / Ops Directors / v3",
    "status": "active",
    "emails_sent": 5152,
    "contacts": 4980,
    "replies": 188,
    "interested": 41,
    "bounced": 334,
    "booked": 12,
    "showed": 9,
    "tags": [{ "id": "ctg_7yHIVjtbLY11QWHJ5fpU3NpBjDkdBmTjy4q10v", "name": "senior-living" }],
    "steps": [
      { "object": "campaign_step", "step_id": 8814, "label": "Variant A",
        "subject": "quick question about your billing flow",
        "sent": 2610, "contacts": 2540, "replies": 101, "interested": 24 },
      { "object": "campaign_step", "step_id": 8815, "label": "Variant B",
        "subject": "your intake paperwork",
        "sent": 2542, "contacts": 2440, "replies": 87, "interested": 17 }
    ]
  }],
  "has_more": false,
  "next_cursor": null
}
```
**The `steps` array is named `steps`, not `variants`, on purpose.** In the code they come from `stats.steps.map(...)` over EmailBison sequence steps, and `step_id` is `EbSequenceStepStat.sequence_step_id`, a cadence step id. The UI labels them "Variant A", "Variant B" and that label is preserved, but calling the array `variants` would invite every consumer to read this as A/B test output. The source file's own comment says why that reading is wrong:

So there is no per-step `booked` or `showed`, and these pages say per-step booking is not attributable rather than shipping a zero.

Two more warnings for these pages. Removing a variant in EmailBison **resets per-variant ids and breaks historical metrics**, which is a documented production hazard in the vault's `CLAUDE.md`, so `step_id` is not a durable key. And `bounced` here is a reporting number, not the deliverability gate the daily diagnosis uses.

There is no `unique_opens` field. Every campaign ships `open_tracking: false`, so the number does not exist. Adding a null field would invite someone to chart it.

## GET /v1/workspaces/{ws}/insights/daily

`insights:read` · upstream bucket · returns list of daily_point

Dense daily series, max 120 days

```bash
curl -sS https://api.leadarc.io/v1/workspaces/airpay/insights/daily?limit=25 \
  -H "Authorization: Bearer $LEADARC_API_KEY"
```

**Query parameters.** Anything else is `400 unsupported_filter`.

`cursor` `from` `limit` `metric` `to`

**Errors from this endpoint**, on top of the [common ones](errors.html): `invalid_request`, `unsupported_filter`, `upstream_error`.

```json
{
  "object": "list",
  "data": [{
    "object": "daily_point",
    "date": "2026-07-25",
    "emails_sent": 1240,
    "responses": 18,
    "positive_responses": 5,
    "booked": 2
  }],
  "has_more": false,
  "next_cursor": null
}
```
**The cap is 120 days, not 366.** `MAX_DAYS = 120` in the app, and `resolveWindow` silently truncates the start when a caller asks for more. A silent truncation through an API is worse than an error, because the caller charts 120 points and believes it has a year. So the API rejects with `400 invalid_request` instead.

Field names map 1:1 to `DailyPoint`: `emailsSent`, `responses`, `positiveResponses`, `booked`. Dense series: days with no activity come back as zeros, not omitted, so a caller can chart without gap-filling. `date` is a UTC calendar day.

---

---

Base URL `https://api.leadarc.io/v1`. Generated from the API source.
