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

# Endpoints

> Every REST API endpoint, its parameters, filters, and response shapes

All endpoints are under `https://api.mirador.org` and require a [server key](/api/authentication). Responses are scoped to the key's project.

<Note>
  This page is the practical reference. For the exact, always-current contract —
  including the full polymorphic event-payload schema — use the
  [Swagger UI](https://api.mirador.org/docs),
  [OpenAPI spec](https://api.mirador.org/openapi.yaml), or
  [`llms.txt`](https://api.mirador.org/llms.txt).
</Note>

## Identity

### `GET /v1/identity`

Confirm a key resolves to an org/project. Cheap — no backend lookup. Good for health checks and key validation.

```json theme={null}
{ "organization_id": "org_...", "project_id": "prj_..." }
```

## Metadata

Use these to discover the real field universe of a project **before** composing a filter.

### `GET /v1/metadata/tags`

```json theme={null}
{ "tags": ["prod", "staging", "bridge", ...] }
```

### `GET /v1/metadata/attributes`

```json theme={null}
{ "attributes": ["header.country", "header.platform", "user_id", ...] }
```

### `GET /v1/metadata/attributes/{key}`

Lists every value seen for one attribute key. Returns `404` (`ATTRIBUTE_NOT_FOUND`) if the key was never observed.

```bash theme={null}
GET /v1/metadata/attributes/header.country
```

```json theme={null}
{ "key": "header.country", "values": ["DE", "GB", "US", ...] }
```

## Traces

### `GET /v1/traces`

List, filter, count, and page through traces.

| Query param        | Description                                                                       |
| ------------------ | --------------------------------------------------------------------------------- |
| `filter`           | AIP-160 expression (see [Filtering](#filtering)). URL-encode it.                  |
| `page`, `per_page` | Pagination. `per_page` is `10`–`100`. Omit both for an unpaginated dump.          |
| `since`, `until`   | RFC 3339 timestamps bounding `created_at`. Compute the absolute time client-side. |

**Paginated response** (when `per_page` is set):

```json theme={null}
{
  "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} */ ]
    }
  ],
  "pagination": { "page": 1, "per_page": 100, "total": 317, "total_pages": 4 }
}
```

<Tip>
  To answer "how many traces match X?" in one request, set `per_page=10` and
  read `pagination.total` — it's an exact count, not an estimate. Omitting
  `per_page` returns every match in a single `{"traces": [...]}` envelope with no
  `pagination` field.
</Tip>

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

The full summary for one trace. Returns `404` (`TRACE_NOT_FOUND`) if it doesn't exist in this project.

```json theme={null}
{
  "trace": {
    "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": [
      {
        "match_event_id": "...",
        "rule_id": "...",
        "rule_name": "Failed Bridge Transfers",
        "match_reason": "trace.attributes[\"status\"] == \"failed\"",
        "matched_at": "2026-06-18T17:02:12Z"
      }
    ]
  }
}
```

<Note>
  This endpoint returns the trace **summary** — it does **not** include the event
  timeline. For events, call `GET /v1/traces/{trace_id}/events`. To assemble a
  complete trace export, combine both — see [Exporting traces](/api/trace-export).
</Note>

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

The complete, ordered event timeline for one trace, returned in a single response.

```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 **polymorphic** — its shape is determined by `event_type`. There are 40+ event types across two categories:

* **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 nests chain identity under `payload.chain` (`caip2` is canonical) and carries a `plugin_correlation_id` you can group by to reconstruct one lifecycle.

<Info>
  The full `event_type → payload` mapping is the `TraceEvent` `oneOf` 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. Treat unfamiliar `event_type` values defensively — the plugin
  list is open and grows without breaking the envelope shape.
</Info>

## Filtering

`filter` is an [AIP-160](https://google.aip.dev/160) expression. Supported fields:

| Field             | Operators | Values                                                                |
| ----------------- | --------- | --------------------------------------------------------------------- |
| `status`          | `=`, `!=` | `"running"`, `"completed"`, `"deleted"`                               |
| `severity`        | `=`       | `"error"`, `"warning"`, `"info"` (severity of any event in the trace) |
| `tag`             | `=`, `:`  | Any string                                                            |
| `attribute.<key>` | `=`       | Any string                                                            |

**Combinators:** `AND`, `OR`, `NOT`.

* `AND` works across fields.
* `OR` only works **between leaves on a single field** — `status="running" OR status="completed"` ✅; `status="x" OR severity="error"` ❌.
* `NOT` inverts a single leaf.
* Quote every value with double quotes. Dotted attribute keys take the explicit-quoted form: `attribute."header.country"="US"`.

```bash theme={null}
# Error traces from US users in the last hour (decoded for readability)
filter=severity="error" AND attribute."header.country"="US"
since=2026-06-18T16:00:00Z

# Anything tagged prod or staging
filter=tag="prod" OR tag="staging"

# Exclude deleted
filter=NOT status="deleted"
```

URL-encode the `filter` value when issuing the request (spaces → `%20`, `=` → `%3D`, `"` → `%22`).

## Errors

Every error uses a typed envelope. Switch on `error.code`, not on the human-readable `message`.

```json theme={null}
{ "error": { "code": "INVALID_FILTER", "message": "status \"zombie\" is not one of running, completed, deleted", "details": [] } }
```

| Code                       | Meaning                                      |
| -------------------------- | -------------------------------------------- |
| `INVALID_FILTER`           | Malformed/unsupported filter expression      |
| `INVALID_ARGUMENT`         | Bad query parameter                          |
| `TRACE_NOT_FOUND`          | No such trace in this project                |
| `ATTRIBUTE_NOT_FOUND`      | Attribute key never observed                 |
| `UNAUTHENTICATED`          | Missing/invalid key, or a non-server key     |
| `PERMISSION_DENIED`        | Key lacks access to the resource             |
| `DEADLINE_EXCEEDED`        | Request timed out                            |
| `UNAVAILABLE`              | Transient backend issue — retry with backoff |
| `INTERNAL` / `UNSPECIFIED` | Unexpected server error                      |

## Next Steps

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

  <Card title="Best Practices" icon="star" href="/best-practices">
    Patterns for SDKs, automations, and the REST API
  </Card>
</CardGroup>
