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

# Observability

> What Orbitrage captures on every call, and how it's attributed to runs, workflows, and end-users.

Every request flows through the gateway, so observability is **automatic** — no
collector to run, no spans to export. Each LLM call becomes one rich record in
your project's data store.

## What's captured per call

Every call records \~150 fields. The ones you'll use most:

<CardGroup cols={2}>
  <Card title="Models & routing" icon="route">
    Requested model, routed model, provider, tier, priority score, routing
    signals, fallback chain, and whether BYOK was used.
  </Card>

  <Card title="Tokens & cost" icon="coins">
    Input/output tokens, reasoning and cache tokens, cost, provider cost,
    baseline cost, and savings.
  </Card>

  <Card title="Latency" icon="gauge-high">
    Total latency, provider latency, queue time, first-token latency, and
    inter-token p50/p95 for streamed responses.
  </Card>

  <Card title="Content & outcome" icon="message">
    Input messages, output messages, tool definitions, finish reasons, refusal
    detection, and an output format signature.
  </Card>
</CardGroup>

## The attribution ladder

Slice the same data at any grain:

```
Call → Session (run) → Workflow (project) → End-user → Organization
```

| Level            | Keyed by      | What it answers                                         |
| ---------------- | ------------- | ------------------------------------------------------- |
| **Call**         | `step_id`     | One provider invocation — every field above.            |
| **Session**      | `run_id`      | The calls that belong to one agent execution.           |
| **Workflow**     | `workflow_id` | A deployed project over all time.                       |
| **End-user**     | `end_user_id` | One of *your* customers, across sessions and workflows. |
| **Organization** | `org_id`      | The whole account — billing, totals, anomalies.         |

### How attribution is stamped

* **Workflow** — from the API key; every key belongs to exactly one workflow, so
  you pass nothing.
* **Run** — `x-orbitrage-run-id` if set, else a W3C `traceparent`, else a sticky
  30-second per-key window groups bursts into one session automatically.
* **End-user** — `x-orbitrage-end-user-id`, set via the SDK (below).

## Per-end-user attribution

Running a B2B app for many end-users? Tag calls with a user id so the dashboard
can build per-user cost and flow graphs inside one workflow.

<CodeGroup>
  ```python Python theme={null}
  # At startup — one user for the whole process:
  orbitrage.init(api_key, user_id=current_user.id)

  # Or per request in a long-running server (the next client picks it up):
  orbitrage.set_user(current_user.id)
  ```

  ```typescript Node.js theme={null}
  // At startup:
  await orbitrage.init({ apiKey, userId: currentUser.id });

  // Or per request:
  orbitrage.setUser(currentUser.id);

  // Or scoped to a single block of work:
  await orbitrage.withAssociationProperties({ user_id: currentUser.id }, async () => {
    // calls in here are tagged with this user
  });
  ```
</CodeGroup>

This sets the `x-orbitrage-end-user-id` header on the next OpenAI client you
construct and every subsequent call.

<Warning>
  Don't send `x-orbitrage-user-id` yourself. That header is the gateway's
  authoritative account identity (derived from your API key), protected by a
  signature — sending your own value causes a `403`. Use
  `x-orbitrage-end-user-id` for per-customer attribution.
</Warning>

## Custom attribution headers

Beyond the SDK defaults, the gateway reads optional headers you can set to enrich
analytics — pass them as request headers (or via your client's `default_headers`).

| Header                                              | Purpose                                    |
| --------------------------------------------------- | ------------------------------------------ |
| `x-orbitrage-end-user-id`                           | Your customer's user id (per-user graphs). |
| `x-orbitrage-run-id`                                | Group calls into one session/run.          |
| `x-orbitrage-workflow-id`                           | Override the key's workflow (advanced).    |
| `x-orbitrage-session-id`                            | A session identifier within a run.         |
| `x-orbitrage-customer-id` / `x-orbitrage-tenant-id` | Multi-tenant identifiers.                  |
| `x-orbitrage-parent-request-id`                     | Parent span, to build call graphs.         |
| `x-orbitrage-step-index`                            | Position of this call in a sequence.       |

See the [API reference](/api-reference/introduction#request-headers) for the full list.

## Where to see it

* **Telemetry** / **Traces** — the live span stream and org-wide drilldown.
* **Routing** — every router decision with the score and signals.
* **Workflows** — a per-project flow graph that reconstructs each run.
* **Overview** — org-wide cost, savings, and incident KPIs.

See the [Dashboard tour](/dashboard/overview) for what each page shows.

## Closing the loop with outcomes

Optionally report what your user did with a response (kept, regenerated, edited,
abandoned, thumbs up/down) by calling the outcome endpoint with the call's step
id. These opt-in signals power the frustration and churn analytics in the
[Intelligence layer](/concepts/intelligence).
