Skip to main content
wrapAnthropicSdk instruments an existing Anthropic client. Your call sites stay the same; each request is captured as an LLM event with model, prompts, output, token usage, duration, and status.

Quick setup

import Anthropic from "@anthropic-ai/sdk";
import { Carbon } from "@carbon-js/sdk";
import { wrapAnthropicSdk } from "@carbon-js/sdk/ai";

const carbon = new Carbon();
const anthropic = wrapAnthropicSdk(new Anthropic(), carbon);

const message = await anthropic.messages.create({
  model: "claude-haiku-4-5",
  max_tokens: 1024,
  messages: [{ role: "user", content: "What is the capital of France?" }],
});

// Flush : Only needed in serverless and other short-lived environments
await carbon.flushPendingEvents();
wrapAnthropicSdk mutates the client you pass in and returns the same instance, typed to accept the optional carbon field on requests. Wrap the client once at startup and share the wrapped instance.

What gets captured

MethodCaptured as
messages.createOne LLM event
messages.create with stream: trueOne LLM event, recorded after the stream completes
messages.streamOne LLM event, recorded after the stream completes

Attach metadata

Add a carbon field to any request to set a trace ID, context identifiers, or custom properties. The SDK strips the field before the request reaches Anthropic.
const traceId = carbon.createTraceId();

const message = await anthropic.messages.create({
  model: "claude-haiku-4-5",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Summarize this ticket." }],
  carbon: {
    traceId,
    context: { agentId: "support-agent", userId: "user-481" },
    additionalProperties: { release: "2026-06" },
  },
});
See Traces and context for what each field powers in the dashboard.

Streaming

Streamed responses are captured after the stream finishes, so the stream must be fully consumed:
const stream = await anthropic.messages.create({
  model: "claude-haiku-4-5",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Write a haiku about ledgers." }],
  stream: true,
});

for await (const event of stream) {
  if (event.type === "content_block_delta" && event.delta.type === "text_delta") {
    process.stdout.write(event.delta.text);
  }
}
A stream that is abandoned before it completes produces no event.

Flushing

In serverless and other short-lived environments, flush buffered events before exiting:
await carbon.flushPendingEvents();
Buffering and flush behavior are configurable — see Configuration.