We're in development! Things may crash or break

Integrations

Anthropic Integration

Track Claude Messages API calls — streaming and non-streaming — automatically with @fluxgate/anthropic.

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.create for 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

MethodNon-streamingStreaming
messages.create
messages.withTracking(ctx)
completions.create
beta.messages.create
beta.messages.withTracking(ctx)