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

# Analysis API

> Query intents, violations, conversations, events, and trace data programmatically

Use the Analysis API to query the same production data available in Agnost AI: **Intents**, **Violations**, **Conversations**, and **Events**. It is useful for internal tools, scheduled reports, and investigations driven by an AI agent.

<Note>
  This reference is intentionally not indexed by search engines. Endpoint names containing
  `sentiment` and `sop` are legacy API names; the corresponding Agnost AI concepts are
  **Intents** and **Violations**.
</Note>

## Before you begin

Create an API key in Agnost AI, then set it as an environment variable using your runtime, deployment platform, or preferred `.env` loader:

```dotenv theme={null}
AGNOST_API_KEY=your_api_key
```

Every request uses:

* Base URL: `https://api.agnost.ai`
* Header: `x-api-key: $AGNOST_API_KEY`
* Content type: `application/json` for request bodies

Most endpoints accept `time_range`. Supported values are `5min`, `1h`, `1d`, `1w`, `1m`, `all`, and custom durations such as `custom:2h30m`. Most endpoints default to `30d` when the value is omitted or invalid.

## Recommended workflow

| Goal                       | Start with                    | Then drill into                              |
| -------------------------- | ----------------------------- | -------------------------------------------- |
| Understand user demand     | `sentiment-stats`             | `sentiment-messages` → `conversation-detail` |
| Find broken agent behavior | `sop-stats`                   | `sop-messages` → `conversation-detail`       |
| Debug one execution        | `conversation-spans`          | `event-io`                                   |
| Build a conversation feed  | `user-stories`                | `conversation-detail`                        |
| Ask an open-ended question | Deterministic endpoints above | `run-spotlight-query` as a fallback          |

Use exact intent or violation titles returned by the list and stats endpoints. Do not invent normalized tags. Intents and violations are classified proactively, so there is no manual classification step.

## Intents

### POST `/dashboard/api/sentiment-stats`

Lists top intent clusters and counts in a time window.
**Best for:** Ranking the intents that matter across recent conversations.

Request:

| Field              | Required | Default | Description                                                  |
| ------------------ | -------- | ------- | ------------------------------------------------------------ |
| `time_range`       | No       | `30d`   | Window to aggregate.                                         |
| `metadata_filters` | No       | `[]`    | Filters by event, conversation, user metadata, or `user_id`. |

Response schema:

| Field                      | Type       | Description                                                       |
| -------------------------- | ---------- | ----------------------------------------------------------------- |
| `total_analyzed`           | `integer`  | Count of unique events matched to active intents.                 |
| `total_messages`           | `integer`  | Count of events in the same filtered window.                      |
| `top_tags`                 | `object[]` | Up to 10 ranked intent clusters.                                  |
| `top_tags[].tag`           | `string`   | Intent title.                                                     |
| `top_tags[].count`         | `integer`  | Number of matched unique events.                                  |
| `tag_distribution`         | `object[]` | Same ranked cluster list used by the dashboard distribution view. |
| `tag_distribution[].tag`   | `string`   | Intent title.                                                     |
| `tag_distribution[].count` | `integer`  | Number of matched unique events.                                  |

Call:

```bash theme={null}
curl -sS "https://api.agnost.ai/dashboard/api/sentiment-stats" \
  -H "x-api-key: $AGNOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"time_range":"1w"}'
```

### POST `/dashboard/api/sentiment-messages`

Returns paginated user messages that matched one intent cluster.
**Best for:** Inspecting evidence after selecting an exact intent title from `sentiment-stats` or `sentiments`.

Request:

| Field              | Required | Default         | Description                                          |
| ------------------ | -------- | --------------- | ---------------------------------------------------- |
| `tag`              | Yes      | None            | Intent title from `sentiment-stats` or `sentiments`. |
| `time_range`       | No       | `30d`           | Window to search.                                    |
| `page`             | No       | `1`             | Page number.                                         |
| `limit`            | No       | `50`, max `100` | Messages per page.                                   |
| `metadata_filters` | No       | `[]`            | Metadata filters.                                    |

Response schema:

| Field                        | Type       | Description                                                                  |
| ---------------------------- | ---------- | ---------------------------------------------------------------------------- |
| `messages`                   | `object[]` | User-side matched messages for the requested intent tag.                     |
| `messages[].message_id`      | `string`   | Matched event id. Intent message rows use the input-side id when applicable. |
| `messages[].conversation_id` | `string`   | Conversation UUID.                                                           |
| `messages[].timestamp`       | `string`   | Event timestamp.                                                             |
| `messages[].role`            | `string`   | `user`, `assistant`, or `system`.                                            |
| `messages[].message`         | `string`   | Readable matched message text.                                               |
| `messages[].input`           | `string`   | Readable input text when loaded.                                             |
| `messages[].output`          | `string`   | Readable output text when loaded.                                            |
| `messages[].matched_tags`    | `string[]` | Matching intent titles.                                                      |
| `messages[].user_id`         | `string`   | Customer-provided user id when present on the conversation.                  |
| `total_count`                | `integer`  | Total matching unique events for the filter.                                 |
| `page`                       | `integer`  | Applied page number.                                                         |
| `limit`                      | `integer`  | Applied page size.                                                           |

Call:

```bash theme={null}
curl -sS "https://api.agnost.ai/dashboard/api/sentiment-messages" \
  -H "x-api-key: $AGNOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tag":"Draft an email","time_range":"1w","page":1,"limit":25}'
```

### POST `/dashboard/api/sentiment-timeline`

Returns a bucketed count timeline for one intent cluster.
**Best for:** Seeing whether one intent is rising, falling, or spiking over time.

Request:

| Field              | Required | Default | Description       |
| ------------------ | -------- | ------- | ----------------- |
| `tag`              | Yes      | None    | Intent title.     |
| `time_range`       | No       | `30d`   | Window to chart.  |
| `metadata_filters` | No       | `[]`    | Metadata filters. |

Response schema:

| Field                  | Type       | Description                                   |
| ---------------------- | ---------- | --------------------------------------------- |
| `timeline`             | `object[]` | Bucketed counts for the requested intent tag. |
| `timeline[].timestamp` | `string`   | Bucket start timestamp in RFC3339 format.     |
| `timeline[].value`     | `number`   | Count for the bucket.                         |
| `bucket_seconds`       | `integer`  | Bucket width used for the timeline.           |

Call:

```bash theme={null}
curl -sS "https://api.agnost.ai/dashboard/api/sentiment-timeline" \
  -H "x-api-key: $AGNOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tag":"Draft an email","time_range":"1w"}'
```

### POST `/dashboard/api/sentiment-timelines`

Returns bucketed timelines for multiple intent clusters in one call.
**Best for:** Comparing several known intents over the same time window.

Request:

| Field              | Required | Default | Description       |
| ------------------ | -------- | ------- | ----------------- |
| `tags`             | Yes      | None    | Intent titles.    |
| `time_range`       | No       | `30d`   | Window to chart.  |
| `metadata_filters` | No       | `[]`    | Metadata filters. |

Response schema:

| Field                         | Type                      | Description                                                                  |
| ----------------------------- | ------------------------- | ---------------------------------------------------------------------------- |
| `timelines`                   | `object<string,object[]>` | Object keyed by intent title. Each value is that intent's bucketed timeline. |
| `timelines.<tag>[].timestamp` | `string`                  | Bucket start timestamp in RFC3339 format.                                    |
| `timelines.<tag>[].value`     | `number`                  | Count for the bucket.                                                        |
| `bucket_seconds`              | `integer`                 | Bucket width used for all timelines.                                         |

Call:

```bash theme={null}
curl -sS "https://api.agnost.ai/dashboard/api/sentiment-timelines" \
  -H "x-api-key: $AGNOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tags":["Draft an email","Schedule a meeting"],"time_range":"1w"}'
```

### POST `/dashboard/api/summarize-intent-distribution`

Summarizes why one intent cluster is appearing in the selected time window.
**Best for:** Producing a short explanation after reviewing `sentiment-stats` or `sentiment-messages`.

Request:

| Field              | Required | Default | Description                                          |
| ------------------ | -------- | ------- | ---------------------------------------------------- |
| `tag`              | Yes      | None    | Intent title from `sentiment-stats` or `sentiments`. |
| `time_range`       | No       | `30d`   | Window to summarize.                                 |
| `metadata_filters` | No       | `[]`    | Metadata filters.                                    |

Response schema:

| Field                   | Type       | Description                                                                         |
| ----------------------- | ---------- | ----------------------------------------------------------------------------------- |
| `summary`               | `string`   | Natural-language explanation of the main patterns behind the matched intent events. |
| `buckets`               | `object[]` | Summarizer-produced buckets. Empty array when no matching input messages exist.     |
| `buckets[].label`       | `string`   | Natural-language bucket label from the summarizer.                                  |
| `buckets[].count`       | `integer`  | Number of sampled matched messages in the bucket.                                   |
| `buckets[].message_ids` | `string[]` | Matched message ids supporting the bucket.                                          |

Call:

```bash theme={null}
curl -sS "https://api.agnost.ai/dashboard/api/summarize-intent-distribution" \
  -H "x-api-key: $AGNOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tag":"Draft an email","time_range":"1w"}'
```

### GET `/dashboard/api/sentiments`

Lists active and suggested intent definitions.
**Best for:** Finding an existing intent before querying or creating one.

Request:

| Field | Required | Default | Description      |
| ----- | -------- | ------- | ---------------- |
| None  | No       | None    | No request body. |

Response schema:

Output format: `object[]` at the JSON response root. Decode directly as an array/list, not as `{ "sentiments": [...] }`.

| Field                                  | Type       | Description                                                           |
| -------------------------------------- | ---------- | --------------------------------------------------------------------- |
| `(root)`                               | `object[]` | Active and suggested intent definitions for the API-key organization. |
| `(root)[].id`                          | `string`   | Stable intent id.                                                     |
| `(root)[].title`                       | `string`   | Intent title used as the query `tag`.                                 |
| `(root)[].description`                 | `string`   | Matching guidance.                                                    |
| `(root)[].active`                      | `boolean`  | True for active or suggested definitions accepted by readers.         |
| `(root)[].status`                      | `string`   | `active`, `inactive`, or `suggested`.                                 |
| `(root)[].evidence_message_count`      | `integer`  | Suggested-intent evidence message count.                              |
| `(root)[].evidence_conversation_count` | `integer`  | Suggested-intent evidence conversation count.                         |

Call:

```bash theme={null}
curl -sS "https://api.agnost.ai/dashboard/api/sentiments" \
  -H "x-api-key: $AGNOST_API_KEY"
```

### POST `/dashboard/api/sentiments`

Creates a new intent definition for future proactive classification.
**Best for:** Creating an intent only when no existing definition covers the need.

Request:

| Field         | Required | Default | Description                                                                                                                                |
| ------------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `title`       | Yes      | None    | Stable intent name. Duplicate active/suggested titles are skipped.                                                                         |
| `description` | No       | `""`    | Intent matching guidance.                                                                                                                  |
| `active`      | Yes      | None    | Must be `true` for agent-created intents. If omitted or false, the intent is inactive and will not be useful for proactive classification. |

Response schema:

Created response:

| Field         | Type      | Description                                                                                 |
| ------------- | --------- | ------------------------------------------------------------------------------------------- |
| `id`          | `string`  | Stable intent id.                                                                           |
| `title`       | `string`  | Created intent title.                                                                       |
| `description` | `string`  | Created intent description.                                                                 |
| `active`      | `boolean` | Final active state. Send `active:true` in the request for agent-created production intents. |
| `status`      | `string`  | Final lifecycle status.                                                                     |

Duplicate skip response:

| Field     | Type      | Description                                                                   |
| --------- | --------- | ----------------------------------------------------------------------------- |
| `skipped` | `boolean` | `true` when an active or suggested intent with the same title already exists. |
| `message` | `string`  | Duplicate explanation.                                                        |

Call:

```bash theme={null}
curl -sS "https://api.agnost.ai/dashboard/api/sentiments" \
  -H "x-api-key: $AGNOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"Draft an email","description":"User wants the agent to draft, edit, or send an email.","active":true}'
```

### PUT `/dashboard/api/sentiments/{id}`

Updates an existing intent definition or lifecycle status.
**Best for:** Correcting, enabling, or disabling an existing intent. Use `active`, not `status`.

Request:

| Field           | Required | Default        | Description                                               |
| --------------- | -------- | -------------- | --------------------------------------------------------- |
| `id` path param | Yes      | None           | Intent id from `GET /sentiments` or create response.      |
| `title`         | No       | Existing value | New title; cannot be empty.                               |
| `description`   | No       | Existing value | New matching guidance.                                    |
| `active`        | No       | Existing value | Set `true` to enable the intent or `false` to disable it. |

Response schema:

| Field     | Type      | Description                            |
| --------- | --------- | -------------------------------------- |
| `id`      | `string`  | Updated intent id.                     |
| `success` | `boolean` | `true` when the mutation was accepted. |
| `active`  | `boolean` | Final active state.                    |
| `status`  | `string`  | Final lifecycle status.                |

Call:

```bash theme={null}
curl -sS -X PUT "https://api.agnost.ai/dashboard/api/sentiments/$INTENT_ID" \
  -H "x-api-key: $AGNOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"description":"User wants the agent to draft, edit, or send an email.","active":true}'
```

## Violations

### POST `/dashboard/api/sop-stats`

Lists the most common violations and their counts in a time window.
**Best for:** Ranking the agent rules violated most often.

Request:

| Field              | Required | Default | Description          |
| ------------------ | -------- | ------- | -------------------- |
| `time_range`       | No       | `30d`   | Window to aggregate. |
| `metadata_filters` | No       | `[]`    | Metadata filters.    |

Response schema:

| Field                      | Type       | Description                                                       |
| -------------------------- | ---------- | ----------------------------------------------------------------- |
| `total_analyzed`           | `integer`  | Count of unique events matched to active violations.              |
| `total_messages`           | `integer`  | Count of events in the same filtered window.                      |
| `top_tags`                 | `object[]` | Up to 10 ranked violation rule clusters.                          |
| `top_tags[].tag`           | `string`   | Violation title.                                                  |
| `top_tags[].count`         | `integer`  | Number of matched unique events.                                  |
| `tag_distribution`         | `object[]` | Same ranked cluster list used by the dashboard distribution view. |
| `tag_distribution[].tag`   | `string`   | Violation title.                                                  |
| `tag_distribution[].count` | `integer`  | Number of matched unique events.                                  |

Call:

```bash theme={null}
curl -sS "https://api.agnost.ai/dashboard/api/sop-stats" \
  -H "x-api-key: $AGNOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"time_range":"1w"}'
```

### POST `/dashboard/api/sop-messages`

Returns paginated assistant messages that violated one rule.
**Best for:** Inspecting evidence after selecting an exact violation title. Violations match assistant output, while intents match user input.

Request:

| Field              | Required | Default         | Description                                 |
| ------------------ | -------- | --------------- | ------------------------------------------- |
| `tag`              | Yes      | None            | Violation title from `sop-stats` or `sops`. |
| `time_range`       | No       | `30d`           | Window to search.                           |
| `page`             | No       | `1`             | Page number.                                |
| `limit`            | No       | `50`, max `100` | Messages per page.                          |
| `metadata_filters` | No       | `[]`            | Metadata filters.                           |

Response schema:

| Field                        | Type       | Description                                                           |
| ---------------------------- | ---------- | --------------------------------------------------------------------- |
| `messages`                   | `object[]` | Assistant-side matched messages for the requested violation rule tag. |
| `messages[].message_id`      | `string`   | Matched assistant event id.                                           |
| `messages[].conversation_id` | `string`   | Conversation UUID.                                                    |
| `messages[].timestamp`       | `string`   | Event timestamp.                                                      |
| `messages[].role`            | `string`   | `user`, `assistant`, or `system`.                                     |
| `messages[].message`         | `string`   | Readable matched message text.                                        |
| `messages[].input`           | `string`   | Readable input text when loaded.                                      |
| `messages[].output`          | `string`   | Readable output text when loaded.                                     |
| `messages[].matched_tags`    | `string[]` | Matching violation rule titles.                                       |
| `messages[].user_id`         | `string`   | Customer-provided user id when present on the conversation.           |
| `total_count`                | `integer`  | Total matching unique events for the filter.                          |
| `page`                       | `integer`  | Applied page number.                                                  |
| `limit`                      | `integer`  | Applied page size.                                                    |

Call:

```bash theme={null}
curl -sS "https://api.agnost.ai/dashboard/api/sop-messages" \
  -H "x-api-key: $AGNOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tag":"Confirm scheduling conflicts proactively","time_range":"1w","page":1,"limit":25}'
```

### POST `/dashboard/api/sop-violations`

Returns a paginated feed of violation events across all rules.
**Best for:** Exploring violations before choosing a single rule to investigate.

Request:

| Field              | Required | Default         | Description                                                                                                                             |
| ------------------ | -------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `time_range`       | No       | `30d`           | Window to search.                                                                                                                       |
| `page`             | No       | `1`             | Page number.                                                                                                                            |
| `limit`            | No       | `20`, max `100` | Violating events per page.                                                                                                              |
| `metadata_filters` | No       | `[]`            | Parsed but currently not applied by this endpoint. Use `sop-messages` when metadata filtering by a specific violation rule is required. |

Response schema:

| Field                                | Type       | Description                                                       |
| ------------------------------------ | ---------- | ----------------------------------------------------------------- |
| `violations`                         | `object[]` | Paginated violating events across all violation rule definitions. |
| `violations[].message_id`            | `string`   | Violating assistant event id.                                     |
| `violations[].conversation_id`       | `string`   | Conversation UUID.                                                |
| `violations[].timestamp`             | `string`   | Event timestamp.                                                  |
| `violations[].violations`            | `string[]` | Violation titles matched by the event.                            |
| `violations[].message_content`       | `string`   | Readable assistant output when available.                         |
| `violations[].sop_title`             | `string`   | Matched violation title when resolved.                            |
| `violations[].violation_description` | `string`   | Violation description when populated.                             |
| `violations[].user_id`               | `string`   | Customer-provided user id when present.                           |
| `violations[].event_id`              | `string`   | Underlying event id when populated.                               |
| `total_count`                        | `integer`  | Total unique violating events in the time window.                 |
| `page`                               | `integer`  | Applied page number.                                              |
| `limit`                              | `integer`  | Applied page size.                                                |

Call:

```bash theme={null}
curl -sS "https://api.agnost.ai/dashboard/api/sop-violations" \
  -H "x-api-key: $AGNOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"time_range":"1w","page":1,"limit":20}'
```

### POST `/dashboard/api/sop-timeline`

Returns a bucketed count timeline for one violation rule.
**Best for:** Seeing whether one violation is rising, falling, or spiking over time.

Request:

| Field              | Required | Default | Description       |
| ------------------ | -------- | ------- | ----------------- |
| `tag`              | Yes      | None    | Violation title.  |
| `time_range`       | No       | `30d`   | Window to chart.  |
| `metadata_filters` | No       | `[]`    | Metadata filters. |

Response schema:

| Field                  | Type       | Description                                           |
| ---------------------- | ---------- | ----------------------------------------------------- |
| `timeline`             | `object[]` | Bucketed counts for the requested violation rule tag. |
| `timeline[].timestamp` | `string`   | Bucket start timestamp in RFC3339 format.             |
| `timeline[].value`     | `number`   | Count for the bucket.                                 |
| `bucket_seconds`       | `integer`  | Bucket width used for the timeline.                   |

Call:

```bash theme={null}
curl -sS "https://api.agnost.ai/dashboard/api/sop-timeline" \
  -H "x-api-key: $AGNOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tag":"Confirm scheduling conflicts proactively","time_range":"1w"}'
```

### POST `/dashboard/api/sop-timelines`

Returns bucketed timelines for multiple violation rule clusters in one call.
**Best for:** Comparing several known violations over the same time window.

Request:

| Field              | Required | Default | Description       |
| ------------------ | -------- | ------- | ----------------- |
| `tags`             | Yes      | None    | Violation titles. |
| `time_range`       | No       | `30d`   | Window to chart.  |
| `metadata_filters` | No       | `[]`    | Metadata filters. |

Response schema:

| Field                         | Type                      | Description                                                                        |
| ----------------------------- | ------------------------- | ---------------------------------------------------------------------------------- |
| `timelines`                   | `object<string,object[]>` | Object keyed by violation rule title. Each value is that rule's bucketed timeline. |
| `timelines.<tag>[].timestamp` | `string`                  | Bucket start timestamp in RFC3339 format.                                          |
| `timelines.<tag>[].value`     | `number`                  | Count for the bucket.                                                              |
| `bucket_seconds`              | `integer`                 | Bucket width used for all timelines.                                               |

Call:

```bash theme={null}
curl -sS "https://api.agnost.ai/dashboard/api/sop-timelines" \
  -H "x-api-key: $AGNOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tags":["Confirm scheduling conflicts proactively","No unsupported guarantees"],"time_range":"1w"}'
```

### POST `/dashboard/api/summarize-sop-distribution`

Summarizes why one violation rule is being violated in the selected time window.
**Best for:** Producing a short explanation after reviewing `sop-stats` or `sop-messages`.

Request:

| Field              | Required | Default | Description                                 |
| ------------------ | -------- | ------- | ------------------------------------------- |
| `tag`              | Yes      | None    | Violation title from `sop-stats` or `sops`. |
| `time_range`       | No       | `30d`   | Window to summarize.                        |
| `metadata_filters` | No       | `[]`    | Metadata filters.                           |

Response schema:

| Field     | Type     | Description                                                  |
| --------- | -------- | ------------------------------------------------------------ |
| `summary` | `string` | Natural-language explanation of the main violation patterns. |

Call:

```bash theme={null}
curl -sS "https://api.agnost.ai/dashboard/api/summarize-sop-distribution" \
  -H "x-api-key: $AGNOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tag":"Confirm scheduling conflicts proactively","time_range":"1w"}'
```

### GET `/dashboard/api/sops`

Lists active and suggested violation rule definitions.
**Best for:** Finding an existing violation rule before querying or creating one.

Request:

| Field | Required | Default | Description      |
| ----- | -------- | ------- | ---------------- |
| None  | No       | None    | No request body. |

Response schema:

Output format: `object[]` at the JSON response root. Decode directly as an array/list, not as `{ "sops": [...] }`.

| Field                  | Type       | Description                                                                   |
| ---------------------- | ---------- | ----------------------------------------------------------------------------- |
| `(root)`               | `object[]` | Active and suggested violation rule definitions for the API-key organization. |
| `(root)[].id`          | `string`   | Stable violation rule id.                                                     |
| `(root)[].title`       | `string`   | Violation title used as the query `tag`.                                      |
| `(root)[].description` | `string`   | Rule text or matching guidance.                                               |
| `(root)[].active`      | `boolean`  | True for active or suggested definitions accepted by readers.                 |
| `(root)[].status`      | `string`   | `active`, `inactive`, or `suggested`.                                         |

Call:

```bash theme={null}
curl -sS "https://api.agnost.ai/dashboard/api/sops" \
  -H "x-api-key: $AGNOST_API_KEY"
```

### POST `/dashboard/api/sops`

Creates a new violation rule definition for proactive violation detection.
**Best for:** Creating a rule only when no existing violation definition covers it. Send `active:true` to classify future messages and asynchronously backfill up to 1,000 recent conversations.

Request:

| Field         | Required | Default | Description                                                                                                                                           |
| ------------- | -------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `title`       | Yes      | None    | Stable violation rule name. Duplicate active/suggested titles are skipped.                                                                            |
| `description` | No       | `""`    | Rule text or matching guidance.                                                                                                                       |
| `active`      | Yes      | None    | Must be `true` for agent-created violation rules. Active rules classify future messages and asynchronously backfill up to 1,000 recent conversations. |

Response schema:

Created response:

| Field         | Type      | Description                                                                               |
| ------------- | --------- | ----------------------------------------------------------------------------------------- |
| `id`          | `string`  | Stable violation rule id.                                                                 |
| `title`       | `string`  | Created violation rule title.                                                             |
| `description` | `string`  | Created violation rule description.                                                       |
| `active`      | `boolean` | Final active state. Send `active:true` in the request for agent-created production rules. |
| `status`      | `string`  | Final lifecycle status.                                                                   |

Duplicate skip response:

| Field     | Type      | Description                                                                           |
| --------- | --------- | ------------------------------------------------------------------------------------- |
| `skipped` | `boolean` | `true` when an active or suggested violation rule with the same title already exists. |
| `message` | `string`  | Duplicate explanation.                                                                |

Call:

```bash theme={null}
curl -sS "https://api.agnost.ai/dashboard/api/sops" \
  -H "x-api-key: $AGNOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"Confirm scheduling conflicts proactively","description":"Assistant must identify scheduling conflicts and ask for confirmation before booking or sending calendar changes.","active":true}'
```

### PUT `/dashboard/api/sops/{id}`

Updates an existing violation rule definition or lifecycle status.
**Best for:** Correcting, enabling, or disabling a violation rule. Activating a rule or changing its active description asynchronously backfills up to 1,000 recent conversations.

Request:

| Field           | Required | Default        | Description                                                       |
| --------------- | -------- | -------------- | ----------------------------------------------------------------- |
| `id` path param | Yes      | None           | Violation rule ID from `GET /sops` or the create response.        |
| `title`         | No       | Existing value | New title; cannot be empty.                                       |
| `description`   | No       | Existing value | New rule text or matching guidance.                               |
| `active`        | No       | Existing value | Set `true` to enable the violation rule or `false` to disable it. |

Response schema:

| Field     | Type      | Description                            |
| --------- | --------- | -------------------------------------- |
| `id`      | `string`  | Updated violation rule id.             |
| `success` | `boolean` | `true` when the mutation was accepted. |
| `active`  | `boolean` | Final active state.                    |
| `status`  | `string`  | Final lifecycle status.                |

Call:

```bash theme={null}
curl -sS -X PUT "https://api.agnost.ai/dashboard/api/sops/$VIOLATION_ID" \
  -H "x-api-key: $AGNOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"description":"Assistant must surface conflicts and get explicit confirmation before taking scheduling action.","active":true}'
```

## Conversations and events

### POST `/dashboard/api/user-stories`

Lists conversations with event rows, metadata, intent matches, and violations. It can also page conversation- or user-metadata groups, then page users within one selected group.
**Best for:** Building a paginated group → user → conversation inventory. Use `event-io` or `raw-logs` for exact input and output.

Request:

| Field                   | Required             | Default                             | Description                                                                                                                         |
| ----------------------- | -------------------- | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `time_range`            | No                   | `30d`                               | Window based on conversation `created_at`.                                                                                          |
| `page`                  | No                   | `1`                                 | Page number.                                                                                                                        |
| `page_size`             | No                   | `50`, max `100`                     | Conversations, groups, or users per page. The dashboard requests 15 distinct group values at a time.                                |
| `connection_type`       | No                   | `[]`                                | String or array. Filters `conversation.metadata.transport_type`.                                                                    |
| `agent_name`            | No                   | `[]`                                | String or array. Filters event `agent_name`.                                                                                        |
| `exclude_io`            | No                   | `false`                             | If `true`, omits `input_args` and `output_result`.                                                                                  |
| `metadata_filters`      | No                   | `[]`                                | Metadata filters.                                                                                                                   |
| `sentiment_ids`         | No                   | `[]`                                | Intent UUIDs; conversations must contain an event matching any selected intent.                                                     |
| `sop_ids`               | No                   | `[]`                                | Violation UUIDs; conversations must contain an event matching any selected violation.                                               |
| `allow_limited_preview` | No                   | `false`                             | Dashboard preview flag; agents should leave it false.                                                                               |
| `search_field`          | With `search_value`  | None                                | Exact selected identifier. Grouped requests accept only `group_value`.                                                              |
| `search_value`          | With `search_field`  | None                                | Exact identifier or grouped metadata value selected from search options.                                                            |
| `group_by`              | No                   | None                                | `{source,key}` where `source` is `conversation_metadata` or `user_metadata`. Omit for the original conversation response.           |
| `group_level`           | No                   | `groups` when `group_by` is present | `groups` pages metadata values; `users` pages users within `group_value`; `conversations` pages conversations for `group_user_key`. |
| `group_value`           | For grouped children | None                                | Exact metadata value whose users or conversations should be returned.                                                               |
| `group_user_key`        | For `conversations`  | None                                | Opaque `key` returned by the grouped-user response.                                                                                 |

Response schema:

| Field                                       | Type                      | Description                                                                                                                                                                                                                              |
| ------------------------------------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `conversations`                             | `object[]`                | Conversation rows with event children and metadata.                                                                                                                                                                                      |
| `conversations[].id`                        | `string`                  | Conversation UUID.                                                                                                                                                                                                                       |
| `conversations[].created_at`                | `string`                  | Conversation creation timestamp.                                                                                                                                                                                                         |
| `conversations[].metadata`                  | `object<string,string>`   | Conversation metadata.                                                                                                                                                                                                                   |
| `conversations[].user_id`                   | `string`                  | Customer-provided user id.                                                                                                                                                                                                               |
| `conversations[].user_metadata`             | `object<string,any>`      | Customer user metadata.                                                                                                                                                                                                                  |
| `conversations[].events`                    | `object[]`                | Events in this conversation, including child spans.                                                                                                                                                                                      |
| `conversations[].events[].event_id`         | `string`                  | Event UUID.                                                                                                                                                                                                                              |
| `conversations[].events[].event_type`       | `string`                  | Event primitive type.                                                                                                                                                                                                                    |
| `conversations[].events[].event_name`       | `string`                  | Agent/tool/model/event name.                                                                                                                                                                                                             |
| `conversations[].events[].io_id`            | `string`                  | Event I/O row id.                                                                                                                                                                                                                        |
| `conversations[].events[].parent_id`        | `string`                  | Parent event id for child spans.                                                                                                                                                                                                         |
| `conversations[].events[].timestamp`        | `string`                  | Event timestamp.                                                                                                                                                                                                                         |
| `conversations[].events[].latency`          | `integer`                 | Latency in milliseconds.                                                                                                                                                                                                                 |
| `conversations[].events[].success`          | `boolean`                 | True when the event succeeded.                                                                                                                                                                                                           |
| `conversations[].events[].conversation_id`  | `string`                  | Conversation UUID.                                                                                                                                                                                                                       |
| `conversations[].events[].metadata`         | `object<string,string>`   | Allowlisted pricing metadata: explicit cost, model/provider, and token fields. Nested SDK usage/response values are flattened to those pricing fields; the original blobs are not exposed.                                               |
| `conversations[].events[].sentiments_match` | `string[]`                | Intent titles matched on the event.                                                                                                                                                                                                      |
| `conversations[].events[].sops_violated`    | `string[]`                | Violation titles matched by the event.                                                                                                                                                                                                   |
| `conversations[].events[].input_args`       | `string`                  | Readable input text unless `exclude_io:true`.                                                                                                                                                                                            |
| `conversations[].events[].output_result`    | `string`                  | Readable output text unless `exclude_io:true`.                                                                                                                                                                                           |
| `user_summaries`                            | `object[]`                | Full filtered-history metrics for each user visible on the current ungrouped interactive page; omitted for grouped responses and empty for limited-preview responses. Optional aggregation failures degrade these fields to zero values. |
| `user_summaries[].key`                      | `string`                  | Stable `user:<user_id>` key, or `conversation:<conversation_id>` for an anonymous conversation.                                                                                                                                          |
| `user_summaries[].conversation_count`       | `integer`                 | Conversations for this user across the active timeline and filters.                                                                                                                                                                      |
| `user_summaries[].message_count`            | `integer`                 | Distinct captured events across those conversations, including tool events.                                                                                                                                                              |
| `user_summaries[].intent_count`             | `integer`                 | Distinct event/intent matches across those conversations.                                                                                                                                                                                |
| `user_summaries[].intent_labels`            | `string[]`                | Up to 20 matched intent titles.                                                                                                                                                                                                          |
| `user_summaries[].violation_count`          | `integer`                 | Distinct event/violation matches across those conversations.                                                                                                                                                                             |
| `user_summaries[].violation_labels`         | `string[]`                | Up to 20 matched violation titles.                                                                                                                                                                                                       |
| `user_summaries[].cost_metadata`            | `object<string,string>[]` | Aggregated explicit-cost or model/provider token buckets for client-side pricing.                                                                                                                                                        |
| `page`                                      | `integer`                 | Applied page number.                                                                                                                                                                                                                     |
| `page_size`                                 | `integer`                 | Applied page size.                                                                                                                                                                                                                       |
| `total_pages`                               | `integer`                 | Current page, plus one when a lookahead row proves a next page exists.                                                                                                                                                                   |
| `has_more`                                  | `boolean`                 | True when another page is available.                                                                                                                                                                                                     |
| `partial_data`                              | `boolean`                 | True when optional hydration or enrichment failed and the returned page is incomplete.                                                                                                                                                   |

With `group_by` and `group_level:"groups"` (the grouped default), the response contains `groups` instead of `conversations`. Each group includes `value`, `user_count`, `conversation_count`, `message_count`, intent and violation counts/labels, `last_active`, and `cost_metadata`. The legacy `message_count` field counts distinct captured events, including tool events, and equals the sum shown for its child conversations. With `group_level:"users"`, the response contains `users` with the same metrics plus an opaque `key`, `user_id`, `label`, and `representative_conversation_id`. Send that `key` as `group_user_key` with `group_level:"conversations"` to retrieve the user's conversations through the exact same group membership logic. Metadata arrays can place one conversation in more than one group.

Each `cost_metadata` entry is either an explicit USD cost bucket or a model/provider token bucket. Token buckets combine requests that share the same input-token count, expose that count as `tokens.request_input`, and use `tokens.aggregated:"true"`; clients should select any request-level pricing tier from `tokens.request_input`, then apply it to the aggregated token totals. `pricing.invalid:"true"` marks malformed numeric metadata. If an event has neither a valid explicit cost nor enough valid model/token data to price it, a computed total is incomplete.

Call:

```bash theme={null}
curl -sS "https://api.agnost.ai/dashboard/api/user-stories" \
  -H "x-api-key: $AGNOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"time_range":"1w","page":1,"page_size":15,"exclude_io":true}'
```

Grouped metadata call:

```bash theme={null}
curl -sS "https://api.agnost.ai/dashboard/api/user-stories" \
  -H "x-api-key: $AGNOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"time_range":"1w","page":1,"page_size":15,"group_by":{"source":"user_metadata","key":"organization_id"},"group_level":"groups"}'
```

### POST `/dashboard/api/conversation-detail`

Fetches one conversation's metadata, transcript messages, and full span tree.
**Best for:** Loading messages and trace spans when you already have a `conversation_id`.

Request:

| Field             | Required | Default | Description                                                                 |
| ----------------- | -------- | ------- | --------------------------------------------------------------------------- |
| `conversation_id` | Yes      | None    | Conversation UUID.                                                          |
| `event_id`        | No       | None    | Passed to `conversation-spans` to focus spans around an event root subtree. |
| `load_io`         | No       | `true`  | Passed to `conversation-messages`; if false, omits message text.            |

Response schema:

| Field                           | Type                    | Description                                                                 |
| ------------------------------- | ----------------------- | --------------------------------------------------------------------------- |
| `conversation`                  | `object` or `null`      | Conversation metadata, or `null` if metadata lookup fails.                  |
| `conversation.created_at`       | `string`                | Conversation creation timestamp when `conversation` is not null.            |
| `conversation.id`               | `string`                | Conversation UUID when `conversation` is not null.                          |
| `conversation.metadata`         | `object<string,string>` | Conversation metadata map when `conversation` is not null.                  |
| `conversation.user_id`          | `string`                | Customer-provided user id.                                                  |
| `conversation.user_metadata`    | `object<string,any>`    | Customer user metadata.                                                     |
| `messages`                      | `object` or `null`      | Transcript payload, or `null` if the messages subrequest fails.             |
| `messages.conversation_id`      | `string`                | Conversation UUID when `messages` is not null.                              |
| `messages.timestamp`            | `string`                | Conversation creation timestamp string.                                     |
| `messages.user_id`              | `string`                | Customer-provided user id, or empty string.                                 |
| `messages.messages`             | `object[]`              | User-facing top-level transcript rows.                                      |
| `messages.messages[].id`        | `string`                | Event id for assistant/system rows; user input rows use `{event_id}_input`. |
| `messages.messages[].role`      | `string`                | `user`, `assistant`, or `system`.                                           |
| `messages.messages[].timestamp` | `string`                | Event timestamp.                                                            |
| `messages.messages[].latency`   | `integer`               | Latency in milliseconds. User input rows use `0`.                           |
| `messages.messages[].success`   | `boolean`               | True when the event succeeded. User input rows use `true`.                  |
| `messages.messages[].message`   | `string`                | Readable message text when `load_io:true`.                                  |
| `messages.total_messages`       | `integer`               | Number of returned transcript rows.                                         |
| `spans`                         | `object` or `null`      | Span payload, or `null` if the spans subrequest fails.                      |
| `spans.conversation_id`         | `string`                | Conversation UUID when `spans` is not null.                                 |
| `spans.spans`                   | `object[]`              | Event/span rows for the full conversation or event-focused subtree.         |
| `spans.spans[].id`              | `string`                | Event/span id.                                                              |
| `spans.spans[].agent_name`      | `string`                | Agent/tool/model/span name.                                                 |
| `spans.spans[].parent_id`       | `string`                | Parent event id, or empty string for a root span.                           |
| `spans.spans[].timestamp`       | `string`                | Event timestamp.                                                            |
| `spans.spans[].latency`         | `integer`               | Latency in milliseconds.                                                    |
| `spans.spans[].success`         | `boolean`               | True when the span succeeded.                                               |
| `spans.spans[].event_metadata`  | `object<string,string>` | Event metadata map. Empty object when no metadata exists.                   |

Call:

```bash theme={null}
curl -sS "https://api.agnost.ai/dashboard/api/conversation-detail" \
  -H "x-api-key: $AGNOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"conversation_id":"'$CONVERSATION_ID'","load_io":true}'
```

### POST `/dashboard/api/conversation-messages`

Returns the user-facing transcript for one conversation.
**Best for:** Replaying the user-visible message flow without internal tool or model spans.

Request:

| Field             | Required | Default | Description                                                 |
| ----------------- | -------- | ------- | ----------------------------------------------------------- |
| `conversation_id` | Yes      | None    | Conversation UUID.                                          |
| `load_io`         | No       | `true`  | If true, resolves readable user input and assistant output. |

Response schema:

| Field                  | Type       | Description                                                                                                                                                                             |
| ---------------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `conversation_id`      | `string`   | Conversation UUID.                                                                                                                                                                      |
| `timestamp`            | `string`   | Conversation creation timestamp string.                                                                                                                                                 |
| `user_id`              | `string`   | Customer-provided user id, or empty string.                                                                                                                                             |
| `messages`             | `object[]` | User-facing top-level transcript rows. Child/tool/model spans are not included.                                                                                                         |
| `messages[].id`        | `string`   | Event id for assistant/system rows; user input rows use `{event_id}_input`.                                                                                                             |
| `messages[].role`      | `string`   | `user`, `assistant`, or `system`.                                                                                                                                                       |
| `messages[].timestamp` | `string`   | Event timestamp.                                                                                                                                                                        |
| `messages[].latency`   | `integer`  | Latency in milliseconds. User input rows use `0`.                                                                                                                                       |
| `messages[].success`   | `boolean`  | True when the event succeeded. User input rows use `true`.                                                                                                                              |
| `messages[].message`   | `string`   | Readable message when `load_io:true`. Text-only messages remain plain strings; multimodal messages use a versioned JSON string containing `text` and hosted `image_url` content blocks. |
| `total_messages`       | `integer`  | Number of returned transcript rows.                                                                                                                                                     |

Call:

```bash theme={null}
curl -sS "https://api.agnost.ai/dashboard/api/conversation-messages" \
  -H "x-api-key: $AGNOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"conversation_id":"'$CONVERSATION_ID'","load_io":true}'
```

### POST `/dashboard/api/conversation-spans`

Returns the event/span hierarchy for one conversation, including child spans.
**Best for:** Debugging tool calls, model execution, latency, errors, and parent-child spans.

Request:

| Field             | Required | Default | Description                                                                                                                         |
| ----------------- | -------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `conversation_id` | Yes      | None    | Conversation UUID.                                                                                                                  |
| `event_id`        | No       | None    | If omitted, returns up to 500 spans for the conversation. If provided, walks up to the root ancestor and returns that root subtree. |

Response schema:

| Field                    | Type                    | Description                                                         |
| ------------------------ | ----------------------- | ------------------------------------------------------------------- |
| `conversation_id`        | `string`                | Conversation UUID.                                                  |
| `spans`                  | `object[]`              | Event/span rows for the full conversation or event-focused subtree. |
| `spans[].id`             | `string`                | Event/span id.                                                      |
| `spans[].agent_name`     | `string`                | Agent/tool/model/span name.                                         |
| `spans[].parent_id`      | `string`                | Parent event id, or empty string for a root span.                   |
| `spans[].timestamp`      | `string`                | Event timestamp.                                                    |
| `spans[].latency`        | `integer`               | Latency in milliseconds.                                            |
| `spans[].success`        | `boolean`               | True when the span succeeded.                                       |
| `spans[].event_metadata` | `object<string,string>` | Event metadata map. Empty object when no metadata exists.           |

Call:

```bash theme={null}
curl -sS "https://api.agnost.ai/dashboard/api/conversation-spans" \
  -H "x-api-key: $AGNOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"conversation_id":"'$CONVERSATION_ID'","event_id":"'$EVENT_ID'"}'
```

### POST `/dashboard/api/event-io`

Fetches only the readable input and output for an exact event id.
**Best for:** Fetching readable input and output when you already have an `event_id`.

Request:

| Field      | Required | Default | Description |
| ---------- | -------- | ------- | ----------- |
| `event_id` | Yes      | None    | Event UUID. |

Response schema:

| Field    | Type               | Description                                                                  |
| -------- | ------------------ | ---------------------------------------------------------------------------- |
| `input`  | `string` or `null` | Readable user/input text, or `null` if the event has no IO row/input.        |
| `output` | `string` or `null` | Readable assistant/output text, or `null` if the event has no IO row/output. |

Call:

```bash theme={null}
curl -sS "https://api.agnost.ai/dashboard/api/event-io" \
  -H "x-api-key: $AGNOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"event_id":"'$EVENT_ID'"}'
```

### POST `/dashboard/api/raw-logs`

Fetches raw event rows with optional metadata, agent, connection, success, and IO filters.
**Best for:** Paginating raw events with metadata, status, latency, and optional input and output.

Request:

| Field              | Required | Default          | Description                                                                                                                                                                           |
| ------------------ | -------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `time_range`       | No       | `30d`            | Event timestamp window. Use `all` when looking up an event id with unknown timestamp.                                                                                                 |
| `page`             | No       | `1`              | Page number.                                                                                                                                                                          |
| `page_size`        | No       | `100`, max `500` | Event rows per page. Use `1` for exact event lookup.                                                                                                                                  |
| `exclude_io`       | No       | `false`          | If false, includes `input_args` and `output_result` previews capped at 64 KiB per field; if true, returns event metadata without IO. Use `/event-io` for a full single-event payload. |
| `connection_type`  | No       | `[]`             | String or array. Filters `conversation.metadata.transport_type`.                                                                                                                      |
| `agent_name`       | No       | `[]`             | String or array. Filters event `agent_name`.                                                                                                                                          |
| `client_config`    | No       | `""`             | Filters `conversation.metadata.client_config`.                                                                                                                                        |
| `success`          | No       | unset            | If true, only success events. If false, any non-success events.                                                                                                                       |
| `metadata_filters` | No       | `[]`             | Arbitrary metadata/user filters. Supports `event_metadata`, `conversation_metadata`, `user_metadata`, and `user_id`.                                                                  |

Filter guidance:

* Use dedicated fields for `exclude_io`, `agent_name`, and `success`; `metadata_filters` cannot express those event columns or output-shaping options.
* `connection_type` is equivalent to `metadata_filters[{source:"conversation_metadata", key:"transport_type"}]`; prefer `connection_type` for this common filter.
* `client_config` is equivalent to `metadata_filters[{source:"conversation_metadata", key:"client_config"}]`; prefer `client_config` for this common filter.
* Use `metadata_filters` for arbitrary event/conversation/user metadata keys and `user_id`.
* If dedicated filters and `metadata_filters` are both sent, the backend applies all of them with AND semantics.

Response schema:

| Field                    | Type       | Description                                                         |
| ------------------------ | ---------- | ------------------------------------------------------------------- |
| `logs`                   | `object[]` | Event rows matching the filters.                                    |
| `logs[].event_id`        | `string`   | Event UUID.                                                         |
| `logs[].conversation_id` | `string`   | Conversation UUID.                                                  |
| `logs[].event_type`      | `string`   | Event primitive type.                                               |
| `logs[].event_name`      | `string`   | Agent/tool/model/event name.                                        |
| `logs[].timestamp`       | `string`   | Event timestamp.                                                    |
| `logs[].latency`         | `integer`  | Latency in milliseconds.                                            |
| `logs[].success`         | `boolean`  | True when the event succeeded.                                      |
| `logs[].input_args`      | `string`   | Readable input preview, capped at 64 KiB, when `exclude_io:false`.  |
| `logs[].output_result`   | `string`   | Readable output preview, capped at 64 KiB, when `exclude_io:false`. |
| `logs[].checkpoints`     | `string`   | Raw checkpoint metadata string when present.                        |
| `logs[].metadata`        | `string`   | JSON string containing event metadata.                              |
| `logs[].parent_id`       | `string`   | Parent event id for child spans.                                    |
| `pagination`             | `object`   | Pagination object.                                                  |
| `pagination.page`        | `integer`  | Applied page number.                                                |
| `pagination.page_size`   | `integer`  | Applied page size.                                                  |

Call:

```bash theme={null}
curl -sS "https://api.agnost.ai/dashboard/api/raw-logs" \
  -H "x-api-key: $AGNOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"time_range":"all","page":1,"page_size":1,"exclude_io":false}'
```

## Ask Agnost AI

### POST `/dashboard/api/run-spotlight-query`

Runs an open-ended investigation from a natural-language question.
**Best for:** Questions that the deterministic intent, violation, conversation, and event endpoints cannot answer. Keep `limit` as small as possible.

Request:

| Field                 | Required                     | Default          | Description                                                                                  |
| --------------------- | ---------------------------- | ---------------- | -------------------------------------------------------------------------------------------- |
| `question` or `query` | Yes                          | None             | Natural-language investigation question. `question` is preferred.                            |
| `time_range`          | No                           | `1w`             | Default window the AskAI tool loop should use when choosing read-only analytics calls.       |
| `limit`               | No                           | `100`, max `500` | Default row/conversation limit for the AskAI tool loop.                                      |
| `response_format`     | No                           | Prose            | Set to `json` when a report builder needs a machine-readable final answer.                   |
| `response_schema`     | With `response_format: json` | None             | JSON object template, up to 4096 bytes, that guides the final answer's keys and value types. |

Response schema:

Output transport: `text/event-stream`. Decode SSE events. The final answer is sent in a `chunk` event as `{"text": string}`; completion metadata is sent in a `done` event. By default `text` is prose. With `response_format: "json"`, `text` contains one compact JSON object synthesized from the template after Spotlight chooses and runs its read-only MCP tools.

SSE event payloads:

| Field                | Type                                                                                                                                                                              | Description                                                                                                                                       |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `event: tool_start`  | `{tool:string, stage:string}`                                                                                                                                                     | Emitted before a read-only internal tool call starts.                                                                                             |
| `event: tool_result` | `{tool:string, ok:boolean, summary?:string, error?:string, evidence_ids?:object<string,string[]>}`                                                                                | Emitted after an internal tool call finishes.                                                                                                     |
| `event: chunk`       | `{text:string}`                                                                                                                                                                   | Final answer text: prose by default, or a compact JSON object string when `response_format: "json"`. Currently emitted as one final answer chunk. |
| `event: done`        | `{tool_count:integer, conversation_count:integer, total_checked:integer, total_relevant:integer, has_more:boolean, links:{label:string,url:string}[], conversation_ids:string[]}` | Completion metadata and openable evidence links.                                                                                                  |

Call:

```bash theme={null}
curl -N -sS "https://api.agnost.ai/dashboard/api/run-spotlight-query" \
  -H "x-api-key: $AGNOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"question":"Find users who were frustrated because email drafting failed","time_range":"1w","limit":25}'
```

## Metadata filters

```json theme={null}
{
  "metadata_filters": [
    {
      "source": "event_metadata | conversation_metadata | user_metadata | user_id",
      "key": "metadata_key_optional_for_user_id",
      "values": ["value-a", "value-b"]
    }
  ]
}
```

## Next steps

* [Authentication](/authentication): create and use an Agnost AI API key.
* [Intents](/using-intents): understand the intent data returned by these APIs.
* [Violations](/using-violations): review the violation model and workflow.
