Users & Sessions
This guide covers how to track users and group their interactions into sessions for effective debugging and analytics.
Overview
Langfuse provides powerful user and session tracking to help you:
- Debug specific users: Find all traces for a particular user
- Analyze conversations: View complete chat session threads
- Monitor usage: Understand patterns by user and session
- Attribute costs: Track spending per user
User Tracking
What is a User?
A User in Langfuse represents an end-user of your application. Each user is identified by a unique user_id that you provide in your code.
Setting User ID
from caip_agents_sdk.observability import observe, propagate_attributes
@observe(name="handle_request")
async def handle_request(user_id: str, message: str):
with propagate_attributes(user_id=user_id):
return await process_message(message)
User Identification Patterns
| Source | Example | Use Case |
|---|---|---|
| JWT Token | payload.get("sub") | Production APIs with authentication |
| API Key | request.headers["X-API-Key"] | Service-to-service calls |
| Session Cookie | request.cookies["session_id"] | Web applications |
| Anonymous | f"anon_{uuid4()}" | Unauthenticated users |
Extracting User from JWT (Production Pattern)
import jwt
from fastapi import Header
def extract_user_from_token(authorization: str) -> dict:
"""Extract user from BMW WebEAM JWT token."""
if not authorization or not authorization.startswith("Bearer "):
return {"user_id": "anonymous", "name": None, "email": None}
token = authorization[7:]
payload = jwt.decode(token, options={"verify_signature": False})
return {
"user_id": payload.get("sub") or payload.get("qxid") or "unknown",
"name": payload.get("name"),
"email": payload.get("email")
}
@observe(name="api:chat")
async def chat_endpoint(
message: str,
authorization: str = Header(None)
):
user = extract_user_from_token(authorization)
with propagate_attributes(
user_id=user["user_id"],
metadata={"user_name": user.get("name")}
):
return await agent.run(message)
Users View in Langfuse
The Users view shows aggregated analytics for each user.

User Metrics
| Metric | Description |
|---|---|
| User ID | Unique identifier you provided |
| Traces | Total number of traces from this user |
| Sessions | Number of distinct sessions |
| Total Tokens | Sum of all token usage |
| Total Cost | Aggregated spending |
| Last Active | Most recent trace timestamp |
User Cost Analytics
Langfuse provides detailed cost breakdowns per user, helping you understand usage patterns and identify high-cost users:

Key insights from User Analytics:
- Tokens by User: See which users consume the most tokens
- Cost per User: Track spending attribution across your user base
- Usage Trends: Identify power users vs occasional users
- Cost Optimization: Find opportunities to reduce costs for high-usage users
User Analytics Use Cases
| Use Case | How |
|---|---|
| Debug user issues | Filter traces by User ID |
| Identify power users | Sort by trace count or tokens |
| Cost attribution | View spending per user |
| Usage patterns | Analyze activity over time |
| Billing analysis | Export user costs for internal chargebacks |
Session Tracking
What is a Session?
A Session groups related traces into a logical conversation or interaction. Sessions help you:
- View complete conversations
- Track multi-turn interactions
- Analyze conversation flows
- Debug conversation failures

Setting Session ID
from caip_agents_sdk.observability import observe, propagate_attributes
@observe(name="handle_message")
async def handle_message(user_id: str, session_id: str, message: str):
with propagate_attributes(
user_id=user_id,
session_id=session_id # Groups related traces
):
return await process_message(message)
Session ID Patterns
| Pattern | Example | Use Case |
|---|---|---|
| Thread ID | thread_abc123 | Chat applications with threads |
| Conversation ID | conv_xyz789 | Multi-turn conversations |
| Request ID | req_123456 | Single request context |
| Browser Session | sess_browser_id | Web session tracking |
FastAPI Example with Session
from fastapi import FastAPI, Header
from caip_agents_sdk.observability import observe, propagate_attributes
app = FastAPI()
@app.post("/chat")
@observe(name="chat_endpoint")
async def chat(
message: str,
thread_id: str, # Session identifier
authorization: str = Header(None)
):
user = extract_user_from_token(authorization)
with propagate_attributes(
user_id=user["user_id"],
session_id=thread_id, # Use thread_id as session
tags=["chat-api"]
):
return await agent.run(message)
Sessions View in Langfuse
Sessions group related traces into conversation threads.

Session Overview
| Field | Description |
|---|---|
| Session ID | Identifier from propagate_attributes(session_id=...) |
| User | User ID if set |
| Trace Count | Number of traces in the session |
| Duration | Time span from first to last trace |
| Input Tokens | Total input tokens across traces |
| Output Tokens | Total output tokens across traces |
| Total Cost | Aggregated cost across all traces |
Navigating Sessions
- Sessions List: Click "Sessions" in the left sidebar
- Session Detail: Click any session to see all traces
- Trace View: Click a trace to see detailed execution
Session Use Cases
| Use Case | Benefit |
|---|---|
| Debug conversations | See full context of a chat |
| Identify patterns | Find where conversations fail |
| Conversation replay | Review user interactions step-by-step |
| Cost analysis | Track spending per conversation |
User ↔ Session ↔ Trace Relationships
Understanding the hierarchy:
User (user_id)
├── Session 1 (session_id)
│ ├── Trace 1 (request 1)
│ │ ├── Observation: validate_input
│ │ ├── Observation: retrieve_context
│ │ └── Observation: generate_response
│ ├── Trace 2 (request 2)
│ │ └── ...observations...
│ └── Trace 3 (request 3)
│ └── ...observations...
│
└── Session 2 (different conversation)
├── Trace 4
└── Trace 5
Querying Relationships
| Query | Filter |
|---|---|
| "All traces for user X" | Filter by user_id |
| "All traces in session Y" | Filter by session_id |
| "All sessions for user X" | Sessions view + user filter |
| "Average cost per user" | Users view, sort by cost |
Best Practices
User Tracking
Even for anonymous users, use a consistent anonymous identifier to track sessions:
user_id = user.id if user else f"anon_{session_cookie}"
Session Management
Use the same session_id across related requests to group them:
- Chat applications: Use the thread/conversation ID
- Web apps: Use a browser session cookie
- APIs: Accept session_id as a parameter
Privacy Considerations
Use opaque identifiers, not emails or names:
# ✅ Good
user_id = "usr_abc123"
# ❌ Bad
user_id = "john.doe@bmw.com"
Next Steps
- Best Practices — Production-ready patterns
- Complete Examples — Ready-to-run code samples
- Tracing Guide — Deep dive into trace instrumentation