Metadata enriches your traces with contextual information, who made the request, which conversation it belongs to, and any custom data relevant to your application. Labels help you categorize and filter traces in the dashboard.
This guide provides a unified reference for sending metadata across all integration methods. For SDK-specific details, see the tutorials linked below.
Quick Reference
Concept OTEL Attribute REST API Description Thread/Conversation gen_ai.conversation.idmetadata.thread_idGroups messages in a conversation User ID langwatch.user.idmetadata.user_idIdentifies the end user Customer ID langwatch.customer.idmetadata.customer_idYour platform’s customer/tenant Labels langwatch.labelsmetadata.labelsCategorization tags Custom Metadata metadata attributemetadata.*Any additional context
SDK Examples
For detailed SDK-specific tutorials, see:
import { setupObservability } from "langwatch/observability/node" ;
import { getLangWatchTracer } from "langwatch" ;
setupObservability ();
const tracer = getLangWatchTracer ( "my-service" );
async function handleUserMessage ( userId : string , conversationId : string ) {
return await tracer . withActiveSpan ( "HandleMessage" , async ( span ) => {
// Thread/conversation ID (OTEL semconv)
span . setAttribute ( "gen_ai.conversation.id" , conversationId );
// User and customer identification
span . setAttribute ( "langwatch.user.id" , userId );
span . setAttribute ( "langwatch.customer.id" , "tenant-123" );
// Labels for filtering (JSON array)
span . setAttribute ( "langwatch.labels" , JSON . stringify ([ "production" , "premium-user" ]));
// Custom metadata (JSON object)
span . setAttribute ( "metadata" , JSON . stringify ({
feature_flags: [ "new-ui" , "beta-model" ],
request_source: "mobile-ios"
}));
// Your application logic...
});
}
Raw OpenTelemetry
If you’re using vanilla OpenTelemetry without the LangWatch SDK:
import { trace } from "@opentelemetry/api" ;
const tracer = trace . getTracer ( "my-service" );
tracer . startActiveSpan ( "operation" , ( span ) => {
// OTEL semconv for conversation/thread
span . setAttribute ( "gen_ai.conversation.id" , "conv-456" );
// LangWatch-specific attributes
span . setAttribute ( "langwatch.user.id" , "user-123" );
span . setAttribute ( "langwatch.customer.id" , "customer-789" );
span . setAttribute ( "langwatch.labels" , JSON . stringify ([ "urgent" , "support" ]));
// Custom metadata as JSON string
span . setAttribute ( "metadata" , JSON . stringify ({
priority: "high" ,
department: "engineering"
}));
// ... your code ...
span . end ();
});
Exporter configuration:
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http" ;
const exporter = new OTLPTraceExporter ({
url: "https://app.langwatch.ai/api/otel/v1/traces" ,
headers: {
Authorization: `Bearer ${ process . env . LANGWATCH_API_KEY } ` ,
},
});
The OTEL endpoint is /api/otel/v1/traces (not /v1/traces).
REST API
Send traces directly via HTTP. See REST API for full details.
curl -X POST "https://app.langwatch.ai/api/collector" \
-H "X-Auth-Token: $LANGWATCH_API_KEY " \
-H "Content-Type: application/json" \
-d '{
"trace_id": "trace-123",
"spans": [
{
"type": "llm",
"span_id": "span-456",
"name": "chat-completion",
"model": "gpt-4",
"input": {"type": "text", "value": "Hello"},
"output": {"type": "text", "value": "Hi there!"},
"timestamps": {
"started_at": 1699900000000,
"finished_at": 1699900001000
}
}
],
"metadata": {
"user_id": "user-123",
"thread_id": "conversation-456",
"customer_id": "customer-789",
"labels": ["production", "premium"],
"any_custom_field": "any value"
}
}'
Reserved vs Custom Fields
In the REST API metadata object:
Field Type Description user_idstring End user identifier thread_idstring Conversation/session ID customer_idstring Your tenant/customer ID labelsstring[] Categorization tags other keys any Stored as custom metadata
Best Practices
Always set user_id Required for user-level analytics and filtering by specific users.
Use thread_id for conversations Groups related messages together. Essential for chatbots and multi-turn interactions.
Labels for categorization Use consistent labels like production, staging, support for filtering.
Custom metadata for context Add any relevant context: feature flags, A/B variants, request sources.
What You Get
Once traces include metadata:
Filter by user : Find all traces for a specific user
View conversations : See all messages in a thread grouped together
Filter by labels : Quickly filter to specific categories
Search custom fields : Find traces by any custom metadata value
User analytics : View per-user metrics and patterns