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

# Worked Investigation

> How an AI agent answers "why are errors up in the past 24 hours?" against the Mirador REST API — request by request

This page traces one investigation end to end, so you can see what an agent actually does with the [REST API](/api/overview) — and judge whether its conclusions are grounded. The prompt:

> Explain why traces in the past 24 hours have been having more errors, and tell me where to investigate.

Every request below is plain `GET` against `https://api.mirador.org` with a [server key](/api/authentication). An agent briefed with [`llms.txt`](https://api.mirador.org/llms.txt) composes these itself; nothing here is special to AI — you could run the same requests by hand.

## Step 1 — Quantify the spike

First, is there actually a spike? The agent counts error traces in the last 24 hours and in the 24 hours before that. Setting `per_page=10` makes `pagination.total` do the counting — two cheap requests, exact numbers.

```bash theme={null}
# Last 24h (assume now = 2026-07-15T09:00:00Z)
GET /v1/traces?filter=severity%3D%22error%22&since=2026-07-14T09:00:00Z&per_page=10
# → "pagination": { ..., "total": 214 }

# Prior 24h baseline
GET /v1/traces?filter=severity%3D%22error%22&since=2026-07-13T09:00:00Z&until=2026-07-14T09:00:00Z&per_page=10
# → "pagination": { ..., "total": 61 }
```

The agent also counts *all* traces in both windows (drop the filter) to rule out "more errors because more traffic": total volume is flat, so the error **rate** is genuinely up \~3.5×.

## Step 2 — Discover the dimensions

Before slicing, the agent asks the project what fields exist, so its filters use real names rather than guesses:

```bash theme={null}
GET /v1/metadata/tags
# → { "tags": ["prod", "staging", "bridge", "swap", "dex", ...] }

GET /v1/metadata/attributes
# → { "attributes": ["header.country", "header.platform", "chain", "user_id", ...] }

GET /v1/metadata/attributes/chain
# → { "key": "chain", "values": ["ethereum", "polygon", "arbitrum"] }
```

## Step 3 — Find where errors concentrate

Now the agent re-runs the error count from Step 1, once per slice, and compares totals. Each slice is one request:

```bash theme={null}
GET /v1/traces?filter=severity%3D%22error%22%20AND%20tag%3D%22bridge%22&since=2026-07-14T09:00:00Z&per_page=10
# → total: 178   ← most of the spike

GET /v1/traces?filter=severity%3D%22error%22%20AND%20tag%3D%22swap%22&since=2026-07-14T09:00:00Z&per_page=10
# → total: 22    ← normal levels

GET /v1/traces?filter=severity%3D%22error%22%20AND%20tag%3D%22bridge%22%20AND%20attribute.chain%3D%22polygon%22&since=2026-07-14T09:00:00Z&per_page=10
# → total: 171   ← nearly all of it
```

Three requests in, the shape of the answer exists: **the spike is bridge traces involving Polygon**. The agent can also narrow *when* it started by bisecting `since` — errors were flat until \~22:40 UTC, then jumped.

## Step 4 — Read the evidence

Counts say *where*; event timelines say *why*. The agent lists a few recent matching traces and pulls their full timelines:

```bash theme={null}
GET /v1/traces?filter=severity%3D%22error%22%20AND%20tag%3D%22bridge%22%20AND%20attribute.chain%3D%22polygon%22&since=2026-07-14T09:00:00Z&per_page=10&page=1
# → picks 3 representative trace_ids from the results

GET /v1/traces/4bf92f3577b34da6a3ce929d0e0e4736/events
```

In each timeline, the same pattern: a `bridge_detected` event on the source chain, then no `bridge_matched` within the expected window, then an `error_event_added` like:

```json theme={null}
{
  "event_type": "error_event_added",
  "created_at": "2026-07-14T22:47:03Z",
  "payload": {
    "message": "bridge leg timeout: destination transaction not observed within 900s",
    "attributes": { "source_chain": "ethereum", "dest_chain": "polygon" }
  }
}
```

Consistent story across all sampled traces: destination legs on Polygon stopped being observed — pointing at the Polygon ingestion path or the bridge itself, not user error or a code regression in the app.

## Step 5 — Report with pointers

The agent's answer, grounded in the requests above:

> **Errors are up \~3.5× (61 → 214) over the last 24h, and 171 of the 214 are bridge traces with `attribute.chain="polygon"`.** The spike began around **22:40 UTC on July 14**. Sampled event timelines (e.g. traces `4bf92f35…`, `9a1e6c0b…`, `7b3d4f88…`) all fail the same way: the source-chain leg is detected, but no destination transaction is observed on Polygon within the 900s window, producing a bridge-leg timeout.
>
> **Where to investigate:**
>
> 1. The Polygon ingestion path (WebSocket endpoint health, ingestor logs) from \~22:40 UTC onward — if Mirador stopped *seeing* Polygon transactions, everything downstream times out.
> 2. The bridge's Polygon-bound deliveries in the same window, in case the bridge itself degraded.
> 3. Open the sampled traces in the dashboard to confirm the pattern holds beyond the sample.

That's the whole loop: two counts to confirm the spike, three counts to localize it, a handful of timelines to explain it — roughly a dozen read-only requests.

## Adapting the pattern

The same **count → slice → read timelines → report** loop answers most investigative questions; only the filters change:

| Question                                     | What changes                                                                    |
| -------------------------------------------- | ------------------------------------------------------------------------------- |
| "Are errors worse after yesterday's deploy?" | Windows split at the deploy time instead of 24h ago                             |
| "Which countries see the most failures?"     | Slice on `attribute."header.country"` (values from the metadata endpoint)       |
| "Is this failing trace a one-off?"           | Start from one trace's timeline, then count traces matching its tags/attributes |
| "What's our error rate per flow?"            | Slice on trace `tag` or name, errors vs. total per slice                        |

## Next Steps

<CardGroup cols={2}>
  <Card title="Connect an agent" icon="plug" href="/ai/connecting-an-agent">
    Set up Claude Code or a custom agent to run this loop
  </Card>

  <Card title="Filter syntax" icon="list" href="/api/endpoints#filtering">
    The AIP-160 grammar behind every slice above
  </Card>
</CardGroup>
