Skip to main content
The gateway is the recommended way to send your calls: one change of base URL, no instrumentation, and your calls can then follow a routing strategy. Use the SDK below when your calls must keep going to your providers directly.
1

Get your API key

Create an API key with the Management scope in API keys. Either expose it as the WEFLAYR_API_KEY environment variable, or pass it directly to autoInstrument() in the next step.
.env
2

Install and instrument

Pick the AI provider SDK you use, install it with its instrumentation package, then call autoInstrument() / auto_instrument() once at startup. Frameworks that call these SDKs underneath (LangChain, LlamaIndex…) are covered too.
Install:
Instrument:
string, enum member, or a list of them
required
'openai', 'anthropic', 'bedrock', 'cohere', 'google-genai', 'elevenlabs', 'mistral', or an array of them.
Each value picks the matching OpenTelemetry instrumentation package to load. Install the package separately.
string
Defaults to the WEFLAYR_API_KEY environment variable. Pass explicitly to override.
string
default:"\"https://api.weflayr.com\""
Where spans are sent. Defaults to the WEFLAYR_BASE_URL environment variable when set.
object
Tags stamped on every emitted span.
"batch" | "immediate"
default:"\"batch\""
Use "immediate" to export telemetry after every call.
boolean
default:"true"
Set false to keep prompts, answers and tool definitions in your process: only tokens, cost and latency are sent.
function
Redact or rewrite fields before they leave your process. See the mask example below.
boolean
default:"true"
Set false to silence the warning written to stderr on each failed export.

Mask example

Use mask to redact personal data (emails, card numbers, names) before it leaves your process. Your callback receives the span attributes right before export, and what it returns is what Weflayr stores. Returning null keeps the attributes unchanged; throwing an error drops all of them.
3

Add metadata to your LLM calls for finer analysis

Wrap your LLM calls in propagateMetadata / propagate_metadata to stamp each emitted span with who the call is for (customer_name) and why it’s happening (feature_name). Anything inside the scope, including nested calls, picks up the same metadata.
Tagging feature_name and customer_name unlock per-customer and per-feature breakdowns across the dashboard, for analysis (e.g. AI Margin) and for benchmarking (e.g. model benchmarks).
As a callback / context managerWrap a single block of code. Every LLM call made inside picks up the metadata.
As a decoratorApply once to a function or method, and every call automatically picks up the metadata.
string
required
The product-side feature triggering the LLM call (e.g. "support-chat", "onboarding-summary").
string
Name of the end-user or tenant customer the call is made for. When omitted, the calls are recorded with no customer. A call sent through the Weflayr AI gateway can name its customer in the request body instead (weflayr: { customer_name: "..." }).
object
Any scalar key/value pairs (string / number / boolean) that you may need to analyze your costs in more detail.
string
The run id to record the calls under. By default each propagateMetadata() call starts a new run. Pass the same value to group the calls of one run made in several scopes or processes, e.g. a job whose workers each open their own scope: create it once with createFeatureRunUuid() / create_feature_run_uuid() where the run starts. Any other value must have the same shape, 32 lowercase hexadecimal characters.
enum member
Override the auto-detected provider of a call recorded by the SDK’s telemetry instrumentation. Use this when the SDK on the wire differs from the provider to bill (e.g. Mistral reached through the OpenAI SDK). Ignored for a call sent through the Weflayr AI gateway, which reports the provider it called itself.
4

Flush before exit (short-lived runtimes only)

In long-running servers, spans are flushed automatically. In short-lived runtimes (Lambda, scripts, edge functions), call flush() before exit so the final spans reach Weflayr.
5

See your calls

Your calls now show on the Overview, and can be benchmarked.

Examples

Multiple AI providers

autoInstrument() accepts an array of providers, so one call wires both up; subsequent propagateMetadata() blocks tag every LLM call inside them regardless of which provider runs.

Install

Setup

Use

propagateMetadata wraps an entire request handler. Both LLM calls inside, regardless of provider, are stamped with the same customer_name and feature_name.
In your dashboard, both spans land under the same customer_name + feature_name, but the provider field (auto-detected) is openai for the first and anthropic for the second.

AI framework

AI frameworks like LangChain, LlamaIndex or Haystack call the underlying provider SDK (OpenAI, Anthropic, …) so the OpenTelemetry instrumentation for that provider catches every call your chain makes, transparently. You don’t need a framework-specific instrumentation. The example below uses LangChain, but the same approach works with any framework that calls an instrumented provider. This example also turns on exportMode: 'immediate', suitable for short-lived runtimes (Lambda, edge functions, scripts) where you don’t want to risk losing buffered spans on exit.

Install

Setup

Use

A simple RAG-style chain: retrieve context, then ask the LLM. Decorate the feature function with propagateMetadata / propagate_metadata: every instrumented call it makes (here the OpenAI call inside .invoke(...)) is stamped with the metadata, and each invocation gets a fresh correlation id.
The same pattern works for any LangChain construct that ultimately calls an instrumented provider: chains, agents, runnables, LCEL pipelines. If the chain makes several LLM calls (e.g. a tool-calling agent loop), every call inside the decorated function carries the same customer_name and feature_name, so the dashboard can correlate them as one logical request. The decorator fixes its metadata at decoration time; when a value varies per request (a per-call customer_name, say), use the inline form instead (weflayr.propagateMetadata(options, fn) in JS, or with weflayr.propagate_metadata(...): in Python).