> ## 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.

# Traces

> Discover, filter, and read traces and their event timelines

A trace is one user flow that can combine application activity with on-chain settlement. Investigate in this order:

<Steps>
  <Step title="Discover the field universe">
    `GET /v1/traces/tags` and `GET /v1/traces/attributes` — so filters use real names, not guesses.
  </Step>

  <Step title="Filter and page">
    `GET /v1/traces` with a filter and a bounded window, paging until the token is absent.
  </Step>

  <Step title="Read one aggregate">
    `GET /v1/traces/{trace_id}` — status, severity, duration, tags, attributes, rule matches.
  </Step>

  <Step title="Read its events">
    `GET /v1/traces/{trace_id}/events` — the complete, ordered timeline.
  </Step>
</Steps>

## Discovery

### `GET /v1/traces/tags`

Tags this project has stamped, over the last 30 days by default (optional `since`/`until`, max 30-day span):

```json theme={null}
{
  "project_id": "prj_...",
  "range_start": "2026-07-19T00:00:00Z",
  "range_end": "2026-08-18T00:00:00Z",
  "tags": { "items": ["prod", "staging", "bridge", "swap"], "truncated": false }
}
```

### `GET /v1/traces/attributes`

Attribute keys **and sampled values** in one call — there is no per-key values endpoint:

```json theme={null}
{
  "project_id": "prj_...",
  "range_start": "2026-07-19T00:00:00Z",
  "range_end": "2026-08-18T00:00:00Z",
  "attributes": {
    "items": [
      { "key": "header.country", "values": { "items": ["DE", "GB", "US"], "truncated": true } },
      { "key": "chain", "values": { "items": ["ethereum", "polygon"], "truncated": false } }
    ],
    "truncated": false
  }
}
```

Every bounded collection uses `{items, truncated}`. Absence of a tag, key, or value is conclusive for the echoed window **only** when the corresponding collection has `truncated: false` — and sampled values are hints, not exhaustive sets, so test an unlisted value with a direct filter.

## `GET /v1/traces`

List, filter, count, and page through traces (newest-first).

| Query param      | Description                                                                                                            |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `filter`         | AIP-160 expression (see [Trace filters](#trace-filters)). URL-encode it.                                               |
| `since`, `until` | RFC 3339 timestamps or relative ages (`24h`). **`since` is unbounded by default — always set it**, even when counting. |
| `page_size`      | 1–1000, default 100.                                                                                                   |
| `page_token`     | Opaque cursor from the previous page — see [Pagination](/api/endpoints#pagination).                                    |

```bash theme={null}
curl -sS --fail-with-body -G \
  -H "Authorization: Bearer $MIRADOR_SERVER_KEY" \
  --data-urlencode 'since=24h' \
  --data-urlencode 'filter=status="completed" AND severity="error"' \
  --data-urlencode 'page_size=20' \
  https://api.mirador.org/v1/traces
```

```json theme={null}
{
  "project_id": "prj_...",
  "traces": [
    {
      "trace_id": "...",
      "trace_number": 317,
      "name": "SwapExecution",
      "status": "completed",
      "highest_severity": "error",
      "duration_ms": 11686,
      "created_at": "2026-06-18T17:02:11Z",
      "tags": ["dex", "swap"],
      "attributes": { "header.country": "US" },
      "rule_matches": [ /* see GET /v1/traces/{trace_id} */ ]
    }
  ],
  "total": 317,
  "next_page_token": "...",
  "range_start": "2026-08-17T09:00:00Z",
  "range_end": "2026-08-18T09:00:00Z"
}
```

<Tip>
  To answer "how many traces match X?" in one cheap request, set `page_size=1`
  and read the top-level `total` — it's an exact count recomputed as each page
  is read, not an estimate.
</Tip>

## Trace filters

| Field             | Operators       | `OR` | `NOT` | Meaning                                                                              |
| ----------------- | --------------- | ---- | ----- | ------------------------------------------------------------------------------------ |
| `status`          | `=`, `!=`       | yes  | yes   | `running`, `completed` (`deleted` parses, but deleted traces never appear)           |
| `severity`        | `=`, `!=`       | yes  | yes   | `error`, `warning`, `info` — the trace contains **at least one event** in that band  |
| `tag`             | `=`, `!=`, `:`  | yes  | yes   | Match a stamped tag; one OR group can't mix `=` and `!=`                             |
| `attribute.<key>` | `=`, `!=`, `:*` | no   | yes   | At most 16 attribute-equality predicates; keys ≤128 UTF-8 bytes; existence uses `:*` |

<Warning>
  Trace severity is **not** "highest severity." A trace containing both error
  and info events matches both `severity="error"` and `severity="info"`.
  And the *filter* accepts `error`/`warning`/`info`, while the *response* field
  `highest_severity` uses `debug`/`info`/`notice`/`warn`/`error` — don't feed a
  response value straight back into a filter.
</Warning>

The shared grammar rules (quoting, OR-on-one-field, dotted keys, presence vs. emptiness, size caps) are on [Endpoints & Conventions](/api/endpoints#filtering).

<Note>
  For OTLP-ingested traces, `attribute.<key>` searches the trace attribute bag — Resource attributes plus keys promoted with [`mirador.trace.attribute.<key>`](/opentelemetry/enrichment-hints#trace-attribute-promotion). A plain span attribute stays on its span and is not filterable here. `severity` matches only events carrying [`mirador.event.severity`](/opentelemetry/enrichment-hints#event-severity), since OTLP span events are stored neutral by default.
</Note>

## `GET /v1/traces/{trace_id}`

The full aggregate for one trace, wrapped as `{ "project_id": ..., "trace": { ... } }`. Returns `404` (`TRACE_NOT_FOUND`) if it doesn't exist in this project — absent, deleted, and out-of-scope are deliberately indistinguishable.

Only `trace_id`, `trace_number`, `name`, `status`, `created_at`, and `duration_ms` are always present. `highest_severity`, `tags`, `attributes`, and `rule_matches` are **omitted when empty** — don't assume them.

<Note>
  `duration_ms` is wall-clock milliseconds between first and last events. For an
  auto-closed trace it can represent a close timeout rather than flow latency —
  check status and lifecycle events before interpreting it. And this endpoint
  returns the **summary only**; for events, call
  `GET /v1/traces/{trace_id}/events`. To assemble a complete export, combine
  both — see [Exporting traces](/api/trace-export).
</Note>

## `GET /v1/traces/{trace_id}/events`

The complete event timeline in one response, in ascending `version` (state) order:

```json theme={null}
{
  "trace_id": "...",
  "events": [
    {
      "event_type": "started",
      "version": 1,
      "source": "client_web",
      "created_at": "2026-06-18T17:02:11Z",
      "trace_timestamp": "2026-06-18T17:02:11Z",
      "payload": { /* shape varies by event_type */ }
    }
  ]
}
```

The `payload` is a **discriminated union keyed by `event_type`**, and event types are open-ended — handle unknown types defensively:

* **Trace-level events** (`started`, `finished`, `info_event_added`, `warn_event_added`, `error_event_added`, `attributes_updated`, `tags_updated`, `rule_match_added`, `trace_closed`, `trace_deleted`) describe the trace itself and carry no chain.
* **Plugin events** (EVM/Solana/Canton transactions and receipts, bridge detected/matched, Safe message/transaction lifecycle, relay quote/deposit/fill/refund, tx hints) are emitted by resolvers tracking a specific flow. Each carries a `plugin_correlation_id` — group on it to reconstruct one plugin lifecycle. Read chain identity from the payload-specific `chain`, `source_chain`/`destination_chain`, or `origin_chain`/`destination_chain`; `caip2` is canonical.

<Info>
  The full `event_type → payload` mapping is the `TraceEvent` union in the
  [OpenAPI spec](https://api.mirador.org/openapi.yaml). The
  [`llms.txt`](https://api.mirador.org/llms.txt) brief documents the most common
  payloads inline.
</Info>

## Correlating with logs

`trace_id` is the pivot between traces and logs: filter `/v1/logs` with `trace_id="<id>"` over a window bounded around the trace's lifetime. See [Correlating traces and logs](/api/logs#correlating-traces-and-logs) for the exact recipe.

## Next Steps

<CardGroup cols={2}>
  <Card title="Exporting traces" icon="file-export" href="/api/trace-export">
    Combine summary + events to export a full trace into tickets
  </Card>

  <Card title="Logs" icon="rectangle-list" href="/api/logs">
    Find the log records a trace produced
  </Card>
</CardGroup>
