---
title: "Custom IDs & attributes"
subtitle: "Tag requests with your own record IDs, conversation threads, and custom properties, then group and filter by them."
---

Respan indexes a few fields you can send with any request, so you can find every span that belongs to a ticket, an order, or a conversation. To tag the end user, use `customer_identifier` instead. See [Users](/docs/documentation/features/customer-identifier).

| Field | Use it for | Where you use it |
|---|---|---|
| `custom_identifier` | A record in your app, like a ticket, order, or document: `CS-1042` | **Users & identifiers** › **Custom IDs**, and the **Custom ID** filter in Logs |
| `thread_identifier` | One conversation: `conv_5d21` | The **Threads** grouping and **Thread ID** filter in Logs |
| `metadata` | Any other key-value pairs, shown as **Custom properties**: `{"feature": "support"}` | The **Custom property** filter in Logs |

## Send the fields

One request can carry all of them, plus `customer_identifier`.

<Tabs>
<Tab title="Gateway">

`client` is the OpenAI client pointed at Respan, as in the [Gateway quickstart](/docs/documentation/features/gateway/gateway-quickstart). For other clients and headers, see [Respan params](/docs/documentation/features/gateway/respan-params).

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

```python
response = client.chat.completions.create(
    model="gpt-5.5",
    messages=[{"role": "user", "content": "Draft a reply to ticket CS-1042."}],
    extra_body={
        "custom_identifier": "CS-1042",
        "thread_identifier": "conv_5d21",
        "metadata": {"feature": "support", "region": "us-east"},
    },
)
```

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

```typescript
const response = await client.chat.completions.create({
  model: "gpt-5.5",
  messages: [{ role: "user", content: "Draft a reply to ticket CS-1042." }],
  // @ts-expect-error Respan fields, not in the OpenAI types
  custom_identifier: "CS-1042",
  thread_identifier: "conv_5d21",
  metadata: { feature: "support", region: "us-east" },
});
```

</Tab>
</Tabs>

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

Every span created inside the block gets the fields. Nested blocks merge, and inner values win. 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(
    custom_identifier="CS-1042",
    thread_identifier="conv_5d21",
    metadata={"feature": "support", "region": "us-east"},
):
    ...  # your LLM calls
```

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

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

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

await respan.propagateAttributes(
  {
    custom_identifier: "CS-1042",
    thread_identifier: "conv_5d21",
    metadata: { feature: "support", region: "us-east" },
  },
  async () => {
    // your LLM calls
  },
);
```

</Tab>
</Tabs>

</Tab>
</Tabs>

Logging spans with the API? These are top-level span fields. See [Create span](/docs/apis/spans/create-span).

<Note>
Send `custom_identifier` as its own field. A `custom_identifier` key inside `metadata` is only a custom property, and doesn't show up under **Custom IDs**.
</Note>

## Check a span

In **Observability** › **Logs**, click a span and open the **Metadata** tab. The identifiers are under **Properties**, and your `metadata` is under **Custom properties**.

<img src={"/docs/images/users/span-identifiers-light.png"} width="480" className="block dark:hidden" alt="The Metadata tab of a span, showing Customer ID, Thread ID and Custom ID under Properties, and feature, team and region under Custom properties" />
<img src={"/docs/images/users/span-identifiers-dark.png"} width="480" className="hidden dark:block" alt="The Metadata tab of a span, showing Customer ID, Thread ID and Custom ID under Properties, and feature, team and region under Custom properties" />

Hover a row and click its filter icon to show only the spans with that value.

## Filter Logs

In **Observability** › **Logs**:

- **Custom ID** or **Thread ID**: click **More filters**, pick the field, and enter the value.
- A custom property: click **Custom property**, search for the key, and pick a value.
- A whole conversation: switch the grouping from **Spans** to **Threads**. Each row is one `thread_identifier`.

See [Logs](/docs/documentation/features/observe/explore-spans) for filter groups and saved views.

## Compare custom IDs

<Steps>
<Step title="Switch to Custom IDs">

Go to **Observability** › **Users & identifiers** and switch the grouping from **Users** to **Custom IDs**.

<img src={"/docs/images/users/custom-ids-light.png"} className="block dark:hidden" alt="The Custom IDs view: metric cards for Requests, Tokens, Cost, Errors, TTFT avg, Latency avg and TPS avg, a timeline of the top five custom IDs, and a table of custom IDs" />
<img src={"/docs/images/users/custom-ids-dark.png"} className="hidden dark:block" alt="The Custom IDs view: metric cards for Requests, Tokens, Cost, Errors, TTFT avg, Latency avg and TPS avg, a timeline of the top five custom IDs, and a table of custom IDs" />

Each row is one `custom_identifier`, with its requests, tokens, cost, errors, and latency in the selected range. Click a metric card to chart the top five custom IDs by that metric.

</Step>
<Step title="Pick a time range">

Use **5m**, **1h**, **1d**, **7d** or **Custom** above the chart. The default is 7 days, and the longest range is 31 days. Drag across the chart to zoom in. Type part of an ID in **Search custom IDs...** and press Enter to find it.

</Step>
<Step title="Open a custom ID">

Click a row to see its **Properties**, **Activity**, **Metrics** and **Performance**. Click **Open in logs** (**L**) to see its spans in Logs for the same range.

</Step>
</Steps>
