Skip to main content

What is Metadata?

Metadata allows you to attach custom context to each LLM call. This additional information helps you:
  • Debug issues - Add request IDs, user IDs, and error context
  • Filter traces - Find specific calls by feature, environment, or user
  • Analyze patterns - Group traces by tenant, version, or experiment
  • Track business metrics - Associate calls with revenue, user segments, etc.

Use Cases

User Context

Track which users make which callsuser_id, tenant_id, organization

Feature Tracking

Identify which features are being usedfeature, experiment_id, variant

Environment Info

Separate different deployment environmentsenvironment, version, build_id

Request Tracing

Correlate with other systemsrequest_id, trace_id, span_id

Business Context

Track business metricsplan_type, revenue, priority

Debugging

Add context for troubleshootingerror_type, retry_count, source

Vercel AI SDK

Pass metadata via providerOptions.observ.metadata:
import { generateText } from "ai";

// Attach custom metadata to traces
const result = await generateText({
  model, // Your wrapped model
  prompt: "Explain React hooks",
  providerOptions: {
    observ: {
      metadata: {
        user_id: "user_123",
        feature: "documentation",
        version: "2.0",
      },
    },
  },
});

// Metadata appears in your Observ dashboard
// for filtering, debugging, and analytics

Provider SDKs

Chain .withMetadata() (TypeScript) or .with_metadata() (Python) to attach data.

Basic Example

const response = await wrappedClient.messages
  .withMetadata({
    user_id: "user_123",
    feature: "chat",
    version: "1.0.0"
  })
  .create({
    model: "claude-sonnet-4-20250514",
    max_tokens: 1024,
    messages: [{ role: "user", content: "Hello!" }],
  });

All Providers

Metadata works with all supported providers:
const response = await wrappedClient.messages
  .withMetadata({
    user_id: "user_123",
    feature: "chat",
    environment: "production",
  })
  .create({
    model: "claude-sonnet-4-20250514",
    max_tokens: 1024,
    messages: [{ role: "user", content: "Hello!" }],
  });
const response = await wrappedClient.chat.completions
  .withMetadata({
    user_id: "user_123",
    feature: "chat",
  })
  .create({
    model: "gpt-4",
    messages: [{ role: "user", content: "Hello!" }],
  });
const response = await wrappedClient.chat.completions
  .withMetadata({
    user_id: "user_123",
    feature: "chat",
  })
  .create({
    model: "mistral-large-latest",
    messages: [{ role: "user", content: "Hello!" }],
  });
const response = await wrappedClient.chat.completions
  .withMetadata({
    user_id: "user_123",
    feature: "chat",
  })
  .create({
    model: "grok-beta",
    messages: [{ role: "user", content: "Hello!" }],
  });
const response = await wrappedClient.chat.completions
  .withMetadata({
    user_id: "user_123",
    feature: "chat",
  })
  .create({
    model: "anthropic/claude-3.5-sonnet",
    messages: [{ role: "user", content: "Hello!" }],
  });

Common Metadata Fields

Here are commonly used metadata fields and their purposes:
{
  "user_id": "user_123",
  "feature": "chat",
  "environment": "production",
  "version": "1.0.0",
  "request_id": "req_abc123",
  "tenant_id": "tenant_456"
}
Purpose: Track which user made the requestExample values: "user_123", "customer_abc", "anonymous_xyz"Useful for debugging user-specific issues and analyzing usage patterns.
Purpose: Identify which feature or flow triggered the callExample values: "chat", "documentation", "support", "autocomplete"Useful for feature analytics and A/B testing.
Purpose: Separate traces by deployment environmentExample values: "production", "staging", "development", "test"Useful for debugging environment-specific issues. Also available as a top-level configuration option.
Purpose: Track which version of your app made the callExample values: "1.0.0", "2.3.1", "2024-01-08"Useful for identifying regressions and comparing versions.
Purpose: Correlate with your application’s request tracingExample values: "req_abc123", "trace_xyz789"Useful for end-to-end debugging across systems.
Purpose: Track multi-tenant applicationsExample values: "tenant_456", "org_acme", "company_123"Useful for per-tenant analytics and billing.

Best Practices

Standardize your metadata fields across your application for easier filtering and analysis.
// Good - consistent naming
{ user_id: "123", feature: "chat" }

// Bad - inconsistent naming
{ userId: "123", featureName: "chat" }
Avoid adding PII, passwords, or other sensitive information to metadata.
// Good - anonymized
{ user_id: "user_123" }

// Bad - PII
{ email: "[email protected]", ssn: "123-45-6789" }
Use strings and numbers for metadata values. Avoid complex objects.
// Good - simple values
{ user_id: "123", plan: "pro", count: 5 }

// Bad - complex object
{ user: { id: 123, profile: { ... } } }
Design metadata fields that will be useful for filtering in the dashboard.Common filters: user_id, feature, environment, version, tenant_id

Combining with Sessions

You can use both metadata and session tracking together:
const response = await wrappedClient.messages
  .withSessionId("conversation_abc123")
  .withMetadata({
    user_id: "user_123",
    feature: "chat",
    environment: "production",
  })
  .create({
    model: "claude-sonnet-4-20250514",
    max_tokens: 1024,
    messages: [{ role: "user", content: "Hello!" }],
  });

Next Steps