Install
npm install @fluxgate/sdk @fluxgate/anthropic @anthropic-ai/sdk
→ @fluxgate/anthropic · @fluxgate/sdk on npm
@anthropic-ai/sdk (≥ 0.50.0) is a peer dependency — install it alongside the wrapper. This package is ESM-only, matching @anthropic-ai/sdk v0.39+. Node.js ≥ 18 is required.
One-time setup
// lib/anthropic.ts
import Anthropic from "@anthropic-ai/sdk";
import { FluxGate } from "@fluxgate/sdk";
import { createAnthropicCostTracker } from "@fluxgate/anthropic";
const _client = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY!,
});
const fg = new FluxGate({
apiKey: process.env.FLUXGATE_API_KEY!,
});
export const anthropic = createAnthropicCostTracker(_client, fg);
Messages — non-streaming
import { anthropic } from "@/lib/anthropic";
const message = await anthropic
.withContext({
feature: "summarization",
user: { id: session.user.id, email: session.user.email },
sessionId: session.id,
})
.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1024,
messages: [{ role: "user", content: userPrompt }],
});
const text = message.content[0].type === "text" ? message.content[0].text : "";
const { cost, trackingId } = message.fluxGateCostTrackingResponse;
Messages — streaming
const stream = await anthropic
.withContext({ feature: "streaming-chat" })
.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1024,
messages: [{ role: "user", content: prompt }],
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);
}
}
// Available after the stream closes
console.log(stream.fluxGateCostTrackingResponse);
Rich user attribution
Pass an AiEventUser object (instead of a plain string ID) to enable per-user revenue attribution in FluxGate's Cost Breakdown views.
await anthropic
.withContext({
feature: "premium-chat",
user: {
id: currentUser.id,
name: currentUser.name,
email: currentUser.email,
monthlyRevenue: currentUser.mrr, // optional — used in spend/revenue ratio charts
},
conversationId: thread.id,
})
.messages.create({ model: "claude-sonnet-4-6", max_tokens: 2048, messages });
Per-call context fork (withTracking)
Use .withTracking() on messages or beta.messages to override context fields for a single call without touching the session. New fields are shallowly merged on top of the existing context.
const session = anthropic.withContext({
feature: "chat",
user: "user-123",
conversationId: "conv-abc",
});
// Tag only this turn — session context is unaffected
const reply = await session.messages
.withTracking({ step: "follow-up" })
.create({
model: "claude-opus-4-5",
max_tokens: 1024,
messages: history,
});
// Works for beta.messages too
const thinkingReply = await session.beta.messages
.withTracking({ step: "reasoning" })
.create({
model: "claude-opus-4-5",
max_tokens: 8000,
messages,
betas: ["interleaved-thinking-2025-05-14"],
});
Error handling
Errors are tracked automatically. Surface them to your own error handling pipeline — FluxGate records the event with status: "ERROR" before the exception propagates.
try {
const message = await anthropic
.withContext({ feature: "code-review" })
.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 4096,
messages,
});
} catch (err) {
// Already recorded in FluxGate — just re-throw
throw err;
}
Without context
Use the .client property when you need a tracked call with no feature tag (e.g. internal health checks):
const message = await anthropic.client.messages.create({
model: "claude-haiku-3",
max_tokens: 256,
messages: [{ role: "user", content: "ping" }],
});
Legacy text completions
The pre-Messages API completions endpoint is tracked for teams still migrating:
const completion = await anthropic
.withContext({ feature: "legacy-completion" })
.completions.create({
model: "claude-2.1",
max_tokens_to_sample: 512,
prompt: `\n\nHuman: ${userPrompt}\n\nAssistant:`,
});
console.log(completion.completion);
Prefer
messages.createfor all new work.
Beta features (extended thinking)
Use beta.messages.create to access Anthropic's extended thinking (reasoning) API. FluxGate captures reasoning tokens separately for accurate cost attribution.
const message = await anthropic
.withContext({ feature: "reasoning-agent" })
.beta.messages.create({
model: "claude-opus-4-5",
max_tokens: 16000,
thinking: { type: "enabled", budget_tokens: 10000 },
messages: [{ role: "user", content: complexProblem }],
betas: ["interleaved-thinking-2025-05-14"],
});
const { cost, trackingId } = message.fluxGateCostTrackingResponse;
// cost includes input + output + reasoning tokens
FluxGateCostTrackingResponse shape
interface FluxGateCostTrackingResponse {
status:
| "SUCCESS"
| "ERROR"
| "BLOCKED"
| "MAX_TOKENS"
| "CONTENT_FILTER"
| "RECITATION"
| "MALFORMED_REQUEST";
cost: number | null; // USD
trackingId: string | null;
createdAt: number | null; // Unix timestamp in milliseconds
errorMessage?: string;
}
Tracked automatically: input tokens, output tokens, reasoning tokens (extended thinking), cache read tokens, cache write tokens, model name, latency (ms), stream duration, and stop reason (end_turn, max_tokens, content_filter).
FluxGateContext fields
All .withContext() calls accept the following fields:
{
feature: string // e.g., "summarization", "code-review"
user?: string | AiEventUser // End-user ID or rich object
step?: string // Step within a multi-step pipeline
sessionId?: string
conversationId?: string
serviceTier?: "default" | "standard" | "batch" | "flex" | "priority"
metadata?: Record<string, unknown> // Arbitrary custom data
}
Supported methods
| Method | Non-streaming | Streaming |
|---|---|---|
messages.create | ✅ | ✅ |
messages.withTracking(ctx) | ✅ | ✅ |
completions.create | ✅ | ✅ |
beta.messages.create | ✅ | ✅ |
beta.messages.withTracking(ctx) | ✅ | ✅ |