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

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 a TrackedUser 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 });

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" }],
});

FluxGateCostTrackingResponse shape

interface FluxGateCostTrackingResponse {
  status:
    | "SUCCESS"
    | "ERROR"
    | "BLOCKED"
    | "MAX_TOKENS"
    | "CONTENT_FILTER"
    | "RECITATION"
    | "MALFORMED_REQUEST";
  cost: number | null; // USD
  trackingId: string | null;
  createdAt: string | null; // ISO 8601
  errorMessage?: string;
}

Tracked automatically: input tokens, output tokens, model name, latency (ms), stream duration, and stop reason (end_turn, max_tokens, content_filter).

Supported methods

MethodNon-streamingStreaming
messages.create