---
title: "Users"
subtitle: "See requests, tokens, and cost for each of your end users, and cap their spend."
---

Tag each request with a `customer_identifier`, and Respan keeps a record for that user: requests, tokens, cost, and activity. On gateway requests, you can also give each user a budget or a rate limit.

To tag requests with your own record IDs, conversation threads, or custom properties, see [Custom IDs & attributes](/docs/documentation/features/observe/custom-ids).

## Send a customer identifier

Add `customer_params` to the request. Only `customer_identifier` is required. `name` and `email` fill the **Name** and **Email** columns the first time Respan sees the user. Later requests don't change them.

<Tabs>
<Tab title="Gateway">

`client` is the OpenAI client pointed at Respan, as in the [Gateway quickstart](/docs/documentation/features/gateway/gateway-quickstart).

<Tabs>
<Tab title={"Python"}>

```python
response = client.chat.completions.create(
    model="gpt-5.5",
    messages=[{"role": "user", "content": "Hello!"}],
    extra_body={
        "customer_params": {
            "customer_identifier": "user_8f21",
            "name": "Maya Patel",
            "email": "maya@example.com",
        }
    },
)
```

</Tab>
<Tab title={"TypeScript"}>

```typescript
const response = await client.chat.completions.create({
  model: "gpt-5.5",
  messages: [{ role: "user", content: "Hello!" }],
  // @ts-expect-error Respan field, not in the OpenAI types
  customer_params: {
    customer_identifier: "user_8f21",
    name: "Maya Patel",
    email: "maya@example.com",
  },
});
```

</Tab>
</Tabs>

If you only have an ID, send `"customer_identifier": "user_8f21"` at the top level of the body instead.

</Tab>
<Tab title="Tracing">

Wrap the code that serves the user. Every span created inside is tagged. For setup, see the [Tracing quickstart](/docs/documentation/features/tracing/quickstart).

<Tabs>
<Tab title={"Python"}>

```python
from respan import Respan, propagate_attributes

Respan()

with propagate_attributes(
    customer_identifier="user_8f21",
    customer_name="Maya Patel",
    customer_email="maya@example.com",
):
    ...  # your LLM calls
```

</Tab>
<Tab title={"TypeScript"}>

```typescript
import { Respan } from "@respan/respan";

const respan = new Respan();
await respan.initialize();

await respan.propagateAttributes(
  {
    customer_identifier: "user_8f21",
    customer_name: "Maya Patel",
    customer_email: "maya@example.com",
  },
  async () => {
    // your LLM calls
  },
);
```

</Tab>
</Tabs>

</Tab>
</Tabs>

Logging spans with the API? `customer_params` is a span field too. See [Create span](/docs/apis/spans/create-span).

A user appears the first time Respan sees its identifier. Production and test API keys keep separate records for the same identifier.

## Find a user

<Steps>
<Step title="Open the users list">

Go to **Observability** › **Users & identifiers**. The grouping reads **Users**.

<img src={"/docs/images/users/users-list-light.png"} className="block dark:hidden" alt="The Users list, grouped by Users, with each user's Customer ID, Name, Last active, Requests, Tokens and Cost" />
<img src={"/docs/images/users/users-list-dark.png"} className="hidden dark:block" alt="The Users list, grouped by Users, with each user's Customer ID, Name, Last active, Requests, Tokens and Cost" />

Each row is one `customer_identifier` with its name, email, first and last activity, requests, tokens, and cost, counted across all time. Type part of a customer ID, name, or email in **Search by users...** and press Enter. The environment switch shows **All environments**, **Production** or **Test**.

</Step>
<Step title="Open the user">

Click a row. The panel shows **Properties**, **Insights** (first seen, last active, and the current budget period), **Metrics** (tokens, cost, period usage, average TTFT and generation time) and **Budgets**.

<img src={"/docs/images/users/user-detail-light.png"} width="480" className="block dark:hidden" alt="A user's detail panel with Properties (Customer ID, Name, Email, Environment) and Insights (First seen, Last active, Period start, Period end, Updated at)" />
<img src={"/docs/images/users/user-detail-dark.png"} width="480" className="hidden dark:block" alt="A user's detail panel with Properties (Customer ID, Name, Email, Environment) and Insights (First seen, Last active, Period start, Period end, Updated at)" />

The icons in the panel header:

- **Open in Dashboard** (**Shift+D**) opens [Metrics](/docs/documentation/features/monitoring/metrics) filtered to this user.
- **Open in Logs** (**L**) opens [Logs](/docs/documentation/features/observe/explore-spans) filtered to this user's spans.
- **More actions** › **Delete user** removes the record.

To fix a name or email, click the pencil next to **Name** or **Email**, type, and press Enter.

</Step>
</Steps>

## Set a budget

Budgets apply to gateway requests only. Once a user's spend in the current period passes their **Period budget**, the gateway rejects their requests with a `402` error until the next period starts.

In **Observability** › **Users & identifiers**, click the user. Under **Budgets**, click the pencil next to **Period budget**, type an amount in USD, and press Enter. To remove the budget, clear the field and press Enter.

<img src={"/docs/images/users/user-budgets-light.png"} width="480" className="block dark:hidden" alt="The Budgets section with Total budget at $1500 and the Period budget field being edited to 250" />
<img src={"/docs/images/users/user-budgets-dark.png"} width="480" className="hidden dark:block" alt="The Budgets section with Total budget at $1500 and the Period budget field being edited to 250" />

The budget period is monthly by default. To switch it to `daily` or `weekly`, or to set the budget from code, call [Update a user](/docs/apis/users/update-user) with `budget_duration` or `period_budget`. Budget fields sent in a request's `customer_params` aren't applied.

For a rate limit that covers every user, see [Limits](/docs/documentation/features/gateway/limits#set-customer-limits).

## Limit requests per user

Rate limits apply to chat completions requests (`/api/chat/completions`). Add `rate_limit`, in requests per minute, to `customer_params`. Requests over the limit get a `429` error. Without it, the **Customer rate limit (requests/min)** set in [Limits](/docs/documentation/features/gateway/limits#set-customer-limits) applies.

```python
extra_body={
    "customer_params": {
        "customer_identifier": "user_8f21",
        "rate_limit": 60,
    }
}
```
