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

# Observability: users, sessions and tags

> Name the end user, group an agent's calls into a session and tag each call, then find, filter, export and get alerts on them.

Every call to the inference API is kept for 30 days in **Models > Observability**: its outcome, how long it took, its TTFT (time to first token), its speed, its tokens, why the model stopped, how many tools it called and what it cost. Never the prompt or the answer.

Above about 1,000 calls a minute from one workspace, the log keeps one call in 20 for the rest of that minute, so its counts run low for that minute. Your bill and balance count every call.

Three things you send with a call make that log answer your own questions:

| You send | Where | What you get |
| - | - | - |
| **End user** | OpenAI's `user` body field (chat, completions, embeddings, images, responses), or Anthropic's `metadata.user_id` on `/v1/messages` | Calls, failures and spend per user; filter by one user |
| **Session** | The `X-Runcrate-Session` header: 1 to 128 letters, digits or `. _ : @ / = + -` | One agent run's calls together, in the order they were made |
| **Tags** | The `X-Runcrate-Tags` header: a JSON object of at most 8 string pairs, keys up to 40 characters, values up to 200 | Filter by any pair, like `feature=search` or `tenant=acme` |

All three are optional. A session or tags that are not valid are ignored and named in the `X-Runcrate-Ignored` response header; the call itself always goes through.

## Send them

```python OpenAI (Python) theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
from openai import OpenAI

client = OpenAI(base_url="https://api.runcrate.ai/v1", api_key="rc_live_YOUR_API_KEY")

response = client.chat.completions.create(
    model="deepseek-ai/DeepSeek-V3",
    messages=[{"role": "user", "content": "Summarise this ticket"}],
    user="customer-42",
    extra_headers={
        "X-Runcrate-Session": "run_7f3a",
        "X-Runcrate-Tags": '{"feature":"support","tenant":"acme"}',
    },
)
```

```typescript OpenAI (TypeScript) theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
import OpenAI from "openai";

const client = new OpenAI({ baseURL: "https://api.runcrate.ai/v1", apiKey: "rc_live_YOUR_API_KEY" });

const response = await client.chat.completions.create(
  {
    model: "deepseek-ai/DeepSeek-V3",
    messages: [{ role: "user", content: "Summarise this ticket" }],
    user: "customer-42",
  },
  {
    headers: {
      "X-Runcrate-Session": "run_7f3a",
      "X-Runcrate-Tags": JSON.stringify({ feature: "support", tenant: "acme" }),
    },
  },
);
```

```python Anthropic (Python) theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
import anthropic

client = anthropic.Anthropic(base_url="https://api.runcrate.ai", api_key="rc_live_YOUR_API_KEY")

message = client.messages.create(
    model="zai-org/GLM-5.3",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Summarise this ticket"}],
    metadata={"user_id": "customer-42"},
    extra_headers={
        "X-Runcrate-Session": "run_7f3a",
        "X-Runcrate-Tags": '{"feature":"support"}',
    },
)
```

```typescript Anthropic (TypeScript) theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic({ baseURL: "https://api.runcrate.ai", apiKey: "rc_live_YOUR_API_KEY" });

const message = await client.messages.create(
  {
    model: "zai-org/GLM-5.3",
    max_tokens: 1024,
    messages: [{ role: "user", content: "Summarise this ticket" }],
    metadata: { user_id: "customer-42" },
  },
  { headers: { "X-Runcrate-Session": "run_7f3a", "X-Runcrate-Tags": JSON.stringify({ feature: "support" }) } },
);
```

```typescript AI SDK theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
import { generateText } from "ai";
import { runcrate } from "@runcrate/ai";

const { text } = await generateText({
  model: runcrate("deepseek-ai/DeepSeek-V3"),
  prompt: "Summarise this ticket",
  headers: {
    "X-Runcrate-Session": "run_7f3a",
    "X-Runcrate-Tags": JSON.stringify({ feature: "support", tenant: "acme" }),
  },
});
```

```bash cURL theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
curl https://api.runcrate.ai/v1/chat/completions \
  -H "Authorization: Bearer $RUNCRATE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Runcrate-Session: run_7f3a" \
  -H 'X-Runcrate-Tags: {"feature":"support","tenant":"acme"}' \
  -d '{
    "model": "deepseek-ai/DeepSeek-V3",
    "messages": [{"role": "user", "content": "Summarise this ticket"}],
    "user": "customer-42"
  }'
```

> [!TIP]
>
> For an agent loop, make one session id per run (a UUID, or your own run id) and send it on every call of that run. The dashboard then shows the run's calls oldest first, and `list_requests` with `session` gives your agent the same list.

## What is recorded

| Field | What it is |
| - | - |
| Took | From the call arriving to the end of the answer |
| TTFT | Time to first token, for a streamed answer |
| Speed | Output tokens a second after the first token, for a streamed text answer (`tokens_per_second`) |
| Why it stopped | The answer's own reason: `stop` or `end_turn` (finished), `length` or `max_tokens` (hit the token limit: raise `max_tokens`), `tool_calls` or `tool_use` (called a tool), `content_filter` |
| Tool calls | How many tool calls the answer asked for |
| Tokens and cost | Input, cached, output and reasoning tokens, and what was billed |

On the dashboard, **Typical time** is the median of what the successful calls took and **Slowest 1 in 20** the 95th percentile. Below the calls, **By model** and **By end user** break the same selection down: calls, failures, typical time, speed and spend.

## Read them from code or an agent

The same log is in the API and the MCP server, with the same filters. A key made for coding agents (inference only) can't read it; use a full API key or sign in with the CLI or MCP.

```bash API theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
# Failed calls of one session in the last day
curl "https://www.runcrate.ai/api/v1/requests?outcome=error&session=run_7f3a" \
  -H "Authorization: Bearer $RUNCRATE_API_KEY"

# One call, by the X-Request-Id of its answer
curl "https://www.runcrate.ai/api/v1/requests/req_8c1f2a" \
  -H "Authorization: Bearer $RUNCRATE_API_KEY"
```

```text MCP theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
list_requests  { "outcome": "error", "session": "run_7f3a" }
get_request    { "id": "req_8c1f2a" }
```

Filters: `from`, `to` (ISO 8601; the last day by default, 30 days at most), `model`, `key`, `environment`, `outcome` (`ok` or `error`), `endpoint`, `end_user`, `session`, `tag` (`key=value`, repeat for several; every one must match), `request_id`, `limit` (up to 100) and `cursor` (the previous page's `meta.cursor`). An API key reads its own environment.

Each failed call carries `error.message` (what happened, and whether anything was billed) and `error.next_step` (what to do about it), so an agent can act on its own failures. Both endpoints are in the API reference under Model calls.

## Download a CSV

**Download CSV** on the Observability page saves the calls you are looking at, with the same filters, up to 100,000 rows: time, request id, model, status, outcome, timings, tokens, speed, why it stopped, tool calls, cost, environment, key, end user, session and tags.

## Get told when calls fail

In **Settings > Webhooks > Failing model calls**, pick a share for an environment (over 5, 10, 25 or 50% failed). When more of that environment's calls fail over 15 minutes, with at least 20 calls, owners and admins are emailed and every endpoint that takes it gets the [`inference.errors_high`](https://runcrate.ai/docs/webhooks/events#inference-errors-high) event, at most once every 15 minutes. It is off until you pick a share.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.