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

# Users

> Associate traces and sessions with end users, accounts, or tenants

Use `user_id` to associate traces and sessions with a specific end user, account, organization, or tenant in your application.

Unlike `session_id`, which groups traces into an agent lifecycle, `user_id` identifies who the activity belongs to. This makes it easier to filter traces, compare user experiences, and join PandaProbe data back to your own identity system.

<Info>
  User identifiers are strings you control. Use the same stable identifier your application already uses for the user, account, or tenant.
</Info>

## Scoped API

Prefer `pandaprobe.user()` when user attribution can be scoped to a block of code. The user is applied to all traces created inside the block and is reset when the block exits.

```python title="Python" theme={null}
import pandaprobe

with pandaprobe.user("user-abc"):
    with pandaprobe.start_trace("my-agent", input={"messages": [...]}) as t:
        ...
```

You can combine user and session scopes when you want both lifecycle grouping and identity attribution:

```python title="Python" theme={null}
with pandaprobe.session("conversation-123"), pandaprobe.user("user-abc"):
    # traces here get both session_id and user_id
    ...
```

TypeScript uses async callback scopes:

```ts title="TypeScript" theme={null}
import { session, user, withTrace } from "pandaprobe";

await session("conversation-123", () =>
  user("user-abc", () =>
    withTrace("my-agent", async () => {
      // Traces inherit both sessionId and userId.
    }),
  ),
);
```

## Imperative API (global)

Use `set_user()` when request middleware or worker setup needs to set the user once before calling into application code.

```python title="Python" theme={null}
pandaprobe.set_user("user-abc")

# All subsequent traces inherit this user_id
```

```ts title="TypeScript" theme={null}
import { setUser } from "pandaprobe";

setUser("user-abc");
```

<Warning>
  The imperative API sets the user globally for the current context. It does not automatically reset. Prefer the context manager API when possible.
</Warning>

## Explicit parameters

Explicit parameters take precedence over scoped or global values:

```python title="Python" theme={null}
@pandaprobe.trace(user_id="explicit-user")
def my_agent(query: str):
    ...

with pandaprobe.start_trace("agent", user_id="explicit-user") as t:
    ...
```

```ts title="TypeScript" theme={null}
import { trace, withTrace } from "pandaprobe";

class Agent {
  @trace({ userId: "explicit-user" })
  async runAgent(query: string) {
    // ...
  }
}

await withTrace("agent", { userId: "explicit-user" }, async () => {
  // ...
});
```

Integration constructors also accept `user_id`:

```python title="Python" theme={null}
from pandaprobe.integrations.langgraph import LangGraphCallbackHandler

handler = LangGraphCallbackHandler(
    session_id="conversation-123",
    user_id="user-abc",
)
```

```ts title="TypeScript" theme={null}
import { LangGraphCallbackHandler } from "pandaprobe/integrations/langgraph";

const handler = new LangGraphCallbackHandler({
  sessionId: "conversation-123",
  userId: "user-abc",
});
```


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