> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getomni.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Get Analytics

> Fetch aggregate totals and per-lead analytics for your workspace

Returns aggregate totals and a per-lead breakdown for leads in your workspace. Results are scoped to the API key's workspace and exclude deleted and test leads.

## Query Parameters

<ParamField query="start" type="string" optional>
  ISO 8601 datetime (e.g. `2026-03-14T00:00:00Z`). Filters leads by `createdAt`.
</ParamField>

<ParamField query="end" type="string" optional>
  ISO 8601 datetime. Must be on or after `start` when both are provided.
</ParamField>

<ParamField query="format" type="string" optional default="json">
  Response format. Either `json` or `csv`. When `csv`, the response is a JSON object with a signed `downloadUrl` pointing to a CSV file hosted on S3.
</ParamField>

<ParamField query="token" type="string" optional>
  Pagination token returned as `nextToken` on a prior request. Only used when `format=json`. Omit on the first request.
</ParamField>

If both `start` and `end` are omitted, all leads in the workspace are returned.

JSON responses are capped at 500 leads per page. Use `nextToken` to fetch the next page.

## Response

<ResponseField name="start" type="string" nullable>
  Echo of the start of the date range
</ResponseField>

<ResponseField name="end" type="string" nullable>
  Echo of the end of the date range
</ResponseField>

<ResponseField name="nextToken" type="string" nullable>
  Pagination token for the next page. `null` when no more leads remain. Pass back as the `token` query param to fetch the next page.
</ResponseField>

<ResponseField name="totals" type="object">
  Workspace-wide aggregates over all leads matching the filter.

  <Expandable title="totals properties">
    <ResponseField name="count" type="number">
      Number of leads in the result set
    </ResponseField>

    <ResponseField name="leadsCompleted" type="number">
      Leads with a submission timestamp
    </ResponseField>

    <ResponseField name="leadsOptedOut" type="number">
      Leads who opted out
    </ResponseField>

    <ResponseField name="totalMessages" type="number">
      Total messages across all leads
    </ResponseField>

    <ResponseField name="inboundMessages" type="number" />

    <ResponseField name="outboundMessages" type="number" />

    <ResponseField name="channels" type="object">
      Per-channel message counts, keyed by channel name (`sms`, `email`, `whatsapp`). Each value has `inbound` and `outbound` counts.
    </ResponseField>

    <ResponseField name="completion" type="object">
      Required-item completion aggregates.

      <Expandable title="completion properties">
        <ResponseField name="totalRequired" type="number">
          Sum of required + visible checklist items across all leads
        </ResponseField>

        <ResponseField name="totalCompleted" type="number">
          Sum of completed required items across all leads
        </ResponseField>

        <ResponseField name="bySource" type="object">
          Completion count by source actor: `agent`, `imported`, `manual`, `portal`
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="interventions" type="object">
      Human intervention counts across all leads.

      <Expandable title="interventions properties">
        <ResponseField name="messageEdits" type="number">
          AI drafts edited by a human before sending
        </ResponseField>

        <ResponseField name="messageRejects" type="number">
          AI drafts rejected or cancelled
        </ResponseField>

        <ResponseField name="manualUpdates" type="number">
          Human-initiated direct edits to lead or checklist fields
        </ResponseField>

        <ResponseField name="documentOverrides" type="number">
          Document validation overrides
        </ResponseField>

        <ResponseField name="escalations" type="number">
          Escalations resolved by a human
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="breakdown" type="array">
  Array of per-lead analytics objects.

  <Expandable title="breakdown[] properties">
    <ResponseField name="leadId" type="string">
      Unique identifier for the lead
    </ResponseField>

    <ResponseField name="firstName" type="string" nullable />

    <ResponseField name="lastName" type="string" nullable />

    <ResponseField name="email" type="string" nullable />

    <ResponseField name="phoneNumber" type="string" nullable />

    <ResponseField name="createdAt" type="string">
      ISO timestamp of lead creation
    </ResponseField>

    <ResponseField name="completedAt" type="string" nullable>
      ISO timestamp of lead submission, if completed
    </ResponseField>

    <ResponseField name="optedOutAt" type="string" nullable>
      ISO timestamp of opt-out, if applicable
    </ResponseField>

    <ResponseField name="totalMessages" type="number" />

    <ResponseField name="inboundMessages" type="number" />

    <ResponseField name="outboundMessages" type="number" />

    <ResponseField name="lastInboundAt" type="string" nullable>
      Most recent inbound message timestamp across all channels
    </ResponseField>

    <ResponseField name="lastOutboundAt" type="string" nullable>
      Most recent outbound message timestamp across all channels
    </ResponseField>

    <ResponseField name="channels" type="object">
      Per-channel stats for this lead. Keys are channel names (`sms`, `email`, `whatsapp`). Each value contains `inbound`, `outbound`, `lastInboundAt`, `lastOutboundAt`.
    </ResponseField>

    <ResponseField name="completion" type="object">
      Checklist completion for this lead. Same shape as `totals.completion`.
    </ResponseField>

    <ResponseField name="interventions" type="object">
      Human intervention counts for this lead. Same shape as `totals.interventions`.
    </ResponseField>

    <ResponseField name="checklistItems" type="array">
      One entry per checklist item associated with this lead.

      <Expandable title="checklistItems[] properties">
        <ResponseField name="code" type="string">
          `{GROUP_KEY}.{ITEM_CODE}` — uppercase identifier for the checklist item. Group key is the group's internal name uppercased with spaces replaced by `_`. Just `{ITEM_CODE}` if the item has no group.
        </ResponseField>

        <ResponseField name="required" type="boolean">
          `true` when the item is required AND visible to this lead (meets its conditions).
        </ResponseField>

        <ResponseField name="status" type="string">
          One of `TODO`, `COMPLETED`, `INVALID`, `SKIPPED`, `NEEDS_REVIEW`
        </ResponseField>

        <ResponseField name="value" type="any" nullable>
          Raw value entered for the item. Type varies by field. `null` if not filled.
        </ResponseField>

        <ResponseField name="updatedAt" type="string">
          ISO timestamp of the last update to this item
        </ResponseField>

        <ResponseField name="source" type="string" nullable>
          Source of the last update: `agent`, `imported`, `manual`, `portal`, or `null` if untouched
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

## Source values

| Source     | Origin                           |
| ---------- | -------------------------------- |
| `agent`    | AI agent via tool calls          |
| `imported` | Public REST API calls            |
| `manual`   | Human team member direct edits   |
| `portal`   | Customer self-service via portal |

## CSV format

When `format=csv`, the response is a JSON object with a signed URL to download the CSV file from S3. The URL expires after 1 hour.

**Response fields:**

<ResponseField name="downloadUrl" type="string">
  Signed S3 URL to download the CSV file. Expires in 1 hour.
</ResponseField>

<ResponseField name="expiresAt" type="string">
  ISO timestamp when the signed URL expires.
</ResponseField>

**CSV file columns** (in the downloaded file):

1. Base fields: `leadId`, `firstName`, `lastName`, `email`, `phoneNumber`, `createdAt`, `completedAt`, `optedOutAt`, message counts, last-touch timestamps
2. Per-channel: `channels.<name>.inbound`, `channels.<name>.outbound`, `channels.<name>.lastInboundAt`, `channels.<name>.lastOutboundAt`
3. Completion: `completion.totalRequired`, `completion.totalCompleted`, `completion.bySource.*`
4. Interventions: `interventions.*`
5. Checklist items (index-based, sorted by group order then item order, padded to the maximum count across all leads): `checklist.<i>.code`, `checklist.<i>.required`, `checklist.<i>.status`, `checklist.<i>.value`, `checklist.<i>.updatedAt`, `checklist.<i>.source`

Totals are not included in the CSV. Use `format=json` for aggregates.

**Example CSV response:**

```json 200 theme={null}
{
  "downloadUrl": "https://s3.amazonaws.com/your-bucket/analytics-exports/...?X-Amz-Signature=...",
  "expiresAt": "2026-04-14T18:00:00.000Z"
}
```

## Errors

| Status | Cause                                                                                             |
| ------ | ------------------------------------------------------------------------------------------------- |
| 400    | Missing or invalid query parameters (e.g., non-ISO date, `start` after `end`, unrecognized param) |
| 401    | Missing or invalid API key                                                                        |

<RequestExample>
  ```javascript Node.js theme={null}
  const options = {
    method: 'GET',
    headers: {
      'x-api-key': '<your-api-key>',
    },
  };

  fetch(
    'https://api-v2.getomni.ai/api/v0/analytics?start=2026-03-14T00:00:00Z&end=2026-04-14T00:00:00Z',
    options,
  )
    .then((response) => response.json())
    .then((response) => console.log(response))
    .catch((err) => console.error(err));
  ```

  ```python Python theme={null}
  import requests

  url = "https://api-v2.getomni.ai/api/v0/analytics"
  params = {
      "start": "2026-03-14T00:00:00Z",
      "end": "2026-04-14T00:00:00Z"
  }
  headers = {
      "x-api-key": "<your-api-key>"
  }

  response = requests.request("GET", url, headers=headers, params=params)
  print(response.json())
  ```

  ```bash cURL theme={null}
  curl --request GET \
    --url 'https://api-v2.getomni.ai/api/v0/analytics?start=2026-03-14T00:00:00Z&end=2026-04-14T00:00:00Z' \
    --header 'x-api-key: <your-api-key>'
  ```

  ```bash cURL (CSV — get download URL) theme={null}
  curl --request GET \
    --url 'https://api-v2.getomni.ai/api/v0/analytics?format=csv&start=2026-03-14T00:00:00Z&end=2026-04-14T00:00:00Z' \
    --header 'x-api-key: <your-api-key>'
  ```

  Then download the CSV from the returned `downloadUrl`:

  ```bash cURL (download CSV from signed URL) theme={null}
  curl -o analytics.csv '<downloadUrl-from-response>'
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "start": "2026-03-14T00:00:00Z",
    "end": "2026-04-14T00:00:00Z",
    "totals": {
      "count": 950,
      "leadsCompleted": 320,
      "leadsOptedOut": 45,
      "totalMessages": 12400,
      "inboundMessages": 4800,
      "outboundMessages": 7600,
      "channels": {
        "sms": { "inbound": 3200, "outbound": 4800 },
        "email": { "inbound": 1400, "outbound": 2800 }
      },
      "completion": {
        "totalRequired": 7600,
        "totalCompleted": 2060,
        "bySource": {
          "agent": 520,
          "imported": 80,
          "manual": 140,
          "portal": 1320
        }
      },
      "interventions": {
        "documentOverrides": 0,
        "escalations": 12,
        "manualUpdates": 51,
        "messageEdits": 109,
        "messageRejects": 14
      }
    },
    "breakdown": [
      {
        "leadId": "4ef30123-90c2-4dc0-bd28-b43eee8664ea",
        "firstName": "Jane",
        "lastName": "Doe",
        "email": "jane@acme.com",
        "phoneNumber": "+15551234567",
        "createdAt": "2026-03-15T10:22:00Z",
        "completedAt": "2026-03-17T04:45:00Z",
        "optedOutAt": null,
        "totalMessages": 18,
        "inboundMessages": 7,
        "outboundMessages": 11,
        "lastInboundAt": "2026-03-17T04:30:00Z",
        "lastOutboundAt": "2026-03-17T04:40:00Z",
        "channels": {
          "sms": {
            "inbound": 6,
            "outbound": 9,
            "lastInboundAt": "2026-03-17T04:30:00Z",
            "lastOutboundAt": "2026-03-17T04:40:00Z"
          },
          "email": {
            "inbound": 1,
            "outbound": 2,
            "lastInboundAt": "2026-03-15T11:12:00Z",
            "lastOutboundAt": "2026-03-15T10:30:00Z"
          }
        },
        "completion": {
          "totalRequired": 8,
          "totalCompleted": 8,
          "bySource": {
            "agent": 2,
            "imported": 0,
            "manual": 1,
            "portal": 5
          }
        },
        "interventions": {
          "documentOverrides": 0,
          "escalations": 0,
          "manualUpdates": 1,
          "messageEdits": 1,
          "messageRejects": 0
        },
        "checklistItems": [
          {
            "code": "PERSONAL_DETAILS.FIRST_NAME",
            "required": true,
            "status": "COMPLETED",
            "value": "Jane",
            "updatedAt": "2026-03-15T10:22:00Z",
            "source": "portal"
          },
          {
            "code": "DOCUMENTS.INCOME_VERIFICATION",
            "required": true,
            "status": "COMPLETED",
            "value": "s3://...",
            "updatedAt": "2026-03-15T11:02:00Z",
            "source": "portal"
          }
        ]
      }
    ]
  }
  ```
</ResponseExample>
