> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dodopayments.com/llms.txt
> Use this file to discover all available pages before exploring further.

# LLM Blueprint

> Track LLM token usage per customer and bill for it in Dodo Payments. Works with AI SDK, OpenAI, Anthropic, OpenRouter, Groq, and Google Gemini.

The LLM Blueprint wraps your LLM client so that each completed call sends a usage event with its input, output, and total token counts to Dodo Payments. A meter sums those counts, so you can bill each customer for the tokens they use. The blueprint ships in the `@dodopayments/ingestion-blueprints` npm package as `createLLMTracker()`.

<CardGroup cols={2}>
  <Card title="Quick Start" icon="rocket" href="#quick-start">
    Install the SDK, create a meter, and wrap your LLM client.
  </Card>

  <Card title="API Reference - Events Ingestion" icon="code" href="/api-reference/usage-events/ingest-events">
    The API endpoint that receives usage events.
  </Card>

  <Card title="API Reference - Meters" icon="gauge" href="/api-reference/meters/create-meter">
    Create and configure meters for billing.
  </Card>

  <Card title="Usage-Based Billing Guide" icon="arrow-trend-up" href="/developer-resources/usage-based-billing-guide">
    Set up usage-based billing with meters from start to finish.
  </Card>
</CardGroup>

<Info>
  Use it in SaaS apps, AI chatbots, content generation tools, and any other LLM-powered application that bills by usage.
</Info>

## Quick Start

To track token usage, install the package, create a meter, and wrap your LLM client.

<Steps>
  <Step title="Install the SDK">
    Install the Dodo Payments Ingestion Blueprints package:

    ```bash theme={null}
    npm install @dodopayments/ingestion-blueprints
    ```

    Also install the SDK for your LLM provider, such as `openai`, `@anthropic-ai/sdk`, `groq-sdk`, `@google/genai`, or `ai` with `@ai-sdk/google`.
  </Step>

  <Step title="Get Your API Keys">
    You need two API keys:

    * **Dodo Payments API key**: Create one under **Developer → API Keys** in the [Dodo Payments dashboard](https://app.dodopayments.com/developer/api-keys), and store it in `DODO_PAYMENTS_API_KEY`. Use a test mode key while you build. A test mode key works only with `test_mode`.
    * **LLM provider API key**: The key for the provider you call, such as OpenAI, Anthropic, Groq, OpenRouter, or Google. The examples read it from variables such as `OPENAI_API_KEY`.

    <Tip>
      Store your API keys in environment variables. Don't commit them to version control.
    </Tip>
  </Step>

  <Step title="Create a Meter in Dodo Payments">
    Create a meter before you track usage:

    1. In the [Dodo Payments dashboard](https://app.dodopayments.com/), go to **Products → Meters**.
    2. Click **Create Meter**.
    3. Configure the meter:
       * **Meter Name**: A descriptive name, such as `LLM Token Usage`.
       * **Event Name**: A unique event identifier, such as `llm.chat_completion`.
       * **Aggregation Type**: **Sum**, to add up token counts.
       * **Over Property**: The token count to bill for:
         * `inputTokens`: input (prompt) tokens.
         * `outputTokens`: output (completion) tokens, including reasoning tokens when the model reports them.
         * `totalTokens`: input and output tokens combined.
       * **Measurement Unit**: The unit shown on invoices, such as `tokens`.
    4. Click **Create Meter**.

    <Info>
      The **Event Name** you set here must match the `eventName` you pass to the SDK exactly (case-sensitive).
    </Info>

    For detailed instructions, see the [Usage-Based Billing Guide](/developer-resources/usage-based-billing-guide).
  </Step>

  <Step title="Track Token Usage">
    Create a tracker, wrap your LLM client, and call the client as usual:

    <CodeGroup>
      ```javascript AI SDK theme={null}
      import { createLLMTracker } from '@dodopayments/ingestion-blueprints';
      import { generateText } from 'ai';
      import { google } from '@ai-sdk/google';

      const llmTracker = createLLMTracker({
        apiKey: process.env.DODO_PAYMENTS_API_KEY,
        environment: 'test_mode',
        eventName: 'aisdk.usage',
      });

      const client = llmTracker.wrap({
        client: { generateText },
        customerId: 'cus_123'
      });

      const response = await client.generateText({
        model: google('gemini-2.0-flash'),
        prompt: 'Hello!',
        maxOutputTokens: 500
      });

      console.log('Usage:', response.usage);
      ```

      ```javascript OpenRouter theme={null}
      import { createLLMTracker } from '@dodopayments/ingestion-blueprints';
      import OpenAI from 'openai';

      const openrouter = new OpenAI({
        baseURL: 'https://openrouter.ai/api/v1',
        apiKey: process.env.OPENROUTER_API_KEY
      });

      const llmTracker = createLLMTracker({
        apiKey: process.env.DODO_PAYMENTS_API_KEY,
        environment: 'test_mode',
        eventName: 'openrouter.usage'
      });

      const client = llmTracker.wrap({
        client: openrouter,
        customerId: 'cus_123'
      });

      const response = await client.chat.completions.create({
        model: 'qwen/qwen3-max',
        messages: [{ role: 'user', content: 'Hello!' }],
        max_tokens: 500
      });

      console.log('Response:', response.choices[0].message.content);
      console.log('Usage:', response.usage);
      ```

      ```javascript OpenAI theme={null}
      import { createLLMTracker } from '@dodopayments/ingestion-blueprints';
      import OpenAI from 'openai';

      // 1. Create your LLM client (normal way)
      const openai = new OpenAI({ 
        apiKey: process.env.OPENAI_API_KEY 
      });

      // 2. Create tracker ONCE at startup
      const tracker = createLLMTracker({
        apiKey: process.env.DODO_PAYMENTS_API_KEY,
        environment: 'test_mode', // Use 'live_mode' for production
        eventName: 'llm.chat_completion' // Match your meter's event name
      });

      // 3. Wrap & use - automatic tracking!
      const client = tracker.wrap({ 
        client: openai, 
        customerId: 'cus_123' 
      });

      // Every API call is now automatically tracked
      const response = await client.chat.completions.create({
        model: 'gpt-4',
        messages: [{ role: 'user', content: 'Hello!' }]
      });

      // ✨ Usage automatically sent to Dodo Payments!
      console.log('Tokens used:', response.usage);
      ```

      ```javascript Anthropic theme={null}
      import { createLLMTracker } from '@dodopayments/ingestion-blueprints';
      import Anthropic from '@anthropic-ai/sdk';

      const anthropic = new Anthropic({ 
        apiKey: process.env.ANTHROPIC_API_KEY 
      });

      const tracker = createLLMTracker({
        apiKey: process.env.DODO_PAYMENTS_API_KEY,
        environment: 'test_mode',
        eventName: 'anthropic.usage'
      });

      const client = tracker.wrap({ 
        client: anthropic, 
        customerId: 'cus_123' 
      });

      const response = await client.messages.create({
        model: 'claude-sonnet-4-0',
        max_tokens: 1024,
        messages: [{ role: 'user', content: 'Hello Claude!' }]
      });

      console.log('Tokens used:', response.usage);
      ```

      ```javascript Groq theme={null}
      import { createLLMTracker } from '@dodopayments/ingestion-blueprints';
      import Groq from 'groq-sdk';

      const groq = new Groq({ 
        apiKey: process.env.GROQ_API_KEY 
      });

      const tracker = createLLMTracker({
        apiKey: process.env.DODO_PAYMENTS_API_KEY,
        environment: 'test_mode',
        eventName: 'groq.usage'
      });

      const client = tracker.wrap({ 
        client: groq, 
        customerId: 'cus_123' 
      });

      const response = await client.chat.completions.create({
        model: 'llama-3.1-8b-instant',
        messages: [{ role: 'user', content: 'Hello!' }]
      });

      console.log('Tokens:', response.usage);
      ```

      ```javascript Google Gemini theme={null}
      import { createLLMTracker } from '@dodopayments/ingestion-blueprints';
      import { GoogleGenAI } from '@google/genai';

      const googleGenai = new GoogleGenAI({
        apiKey: process.env.GOOGLE_GENERATIVE_AI_API_KEY
      });

      const llmTracker = createLLMTracker({
        apiKey: process.env.DODO_PAYMENTS_API_KEY,
        environment: 'test_mode',
        eventName: 'gemini.usage'
      });

      const client = llmTracker.wrap({
        client: googleGenai,
        customerId: 'cus_123'
      });

      const response = await client.models.generateContent({
        model: 'gemini-2.5-flash',
        contents: 'Why is the sky blue?'
      });

      console.log('Response:', response.text);
      console.log('Usage:', response.usageMetadata);
      ```
    </CodeGroup>

    <Check>
      Each completed call through the wrapped client now sends a usage event with its token counts to Dodo Payments for billing.
    </Check>
  </Step>
</Steps>

## Configuration

### Tracker Configuration

Create a tracker once at application startup and reuse it for every customer. `createLLMTracker()` takes these options, and it throws an error if `apiKey` or `eventName` is missing or empty:

<ParamField path="apiKey" type="string" required>
  Your Dodo Payments API key. Get it from the [API Keys page](https://app.dodopayments.com/developer/api-keys).

  ```javascript theme={null}
  apiKey: process.env.DODO_PAYMENTS_API_KEY
  ```
</ParamField>

<ParamField path="environment" type="string">
  The environment mode for the tracker:

  * `test_mode`: for development and testing. This is the default.
  * `live_mode`: for production.

  The Dodo Payments SDKs default to `live_mode` instead, so set `live_mode` explicitly in production.

  ```javascript theme={null}
  environment: 'test_mode' // or 'live_mode'
  ```

  <Warning>
    Use `test_mode` during development, so test traffic doesn't create live usage events.
  </Warning>
</ParamField>

<ParamField path="eventName" type="string" required>
  The event name that triggers your meter. It must match the **Event Name** of your Dodo Payments meter exactly (case-sensitive).

  ```javascript theme={null}
  eventName: 'llm.chat_completion'
  ```

  <Info>
    This event name links your tracked usage to the correct meter for billing calculations.
  </Info>
</ParamField>

Besides `wrap()`, the tracker has `track(response, customerId, metadata)`, which records usage from a response you already have, and `healthCheck()`, which returns `true` when the Dodo Payments API is reachable.

### Wrapper Configuration

Pass these parameters to `wrap()`:

<ParamField path="client" type="object" required>
  Your LLM client instance, such as an OpenAI, Anthropic, Groq, or Google GenAI client, or an object that holds AI SDK functions, such as `{ generateText }`.

  ```javascript theme={null}
  client: openai
  ```
</ParamField>

<ParamField path="customerId" type="string" required>
  The Dodo Payments customer ID of the customer to bill. It starts with `cus_`.

  ```javascript theme={null}
  customerId: 'cus_123'
  ```

  <Tip>
    Store each user's Dodo Payments customer ID with your user record, and pass it here. Your application's own user ID doesn't match a Dodo Payments customer.
  </Tip>
</ParamField>

<ParamField path="metadata" type="object">
  Optional additional data to attach to each tracking event, for filtering and analysis. Each value must be a string, number, or boolean. A key named `inputTokens`, `outputTokens`, `totalTokens`, or `model` replaces the tracked value.

  ```javascript theme={null}
  metadata: {
    feature: 'chat',
    userTier: 'premium',
    sessionId: 'session_123',
    modelVersion: 'gpt-4'
  }
  ```
</ParamField>

### Complete Configuration Example

This example tracks an AI SDK call and attaches `provider` metadata to the event:

<CodeGroup>
  ```javascript Full Configuration theme={null}
  import { createLLMTracker } from "@dodopayments/ingestion-blueprints";
  import { generateText } from "ai";
  import { google } from "@ai-sdk/google";
  import "dotenv/config";

  async function aiSdkExample() {
    console.log("🤖 AI SDK Simple Usage Example\n");

    try {
      // 1. Create tracker
      const llmTracker = createLLMTracker({
        apiKey: process.env.DODO_PAYMENTS_API_KEY,
        environment: "test_mode",
        eventName: "your_meter_event_name",
      });

      // 2. Wrap the ai-sdk methods
      const client = llmTracker.wrap({
        client: { generateText },
        customerId: "cus_123",
        metadata: {
          provider: "ai-sdk",
        },
      });

      // 3. Use the wrapped function
      const response = await client.generateText({
        model: google("gemini-2.5-flash"),
        prompt: "Hello, I am a cool guy! Tell me a fun fact.",
        maxOutputTokens: 500,
      });

      console.log(response);
      console.log(response.usage);
      console.log("✅ Automatically tracked for customer\n");
    } catch (error) {
      console.error(error);
    }
  }

  aiSdkExample().catch(console.error);
  ```
</CodeGroup>

<Info>
  **Automatic Tracking:** The wrapper returns the provider's response unchanged, so your code stays the same as with the original provider SDK. It sends the usage event before it returns the response, so each call waits for the ingestion request, and a failed ingestion request makes the wrapped call throw even when the provider call succeeded. Streaming responses don't carry final token counts on the returned object, so the wrapper doesn't track them.
</Info>

## Supported Providers

The tracker reads token counts from the response formats of these providers and SDKs:

<AccordionGroup>
  <Accordion title="AI SDK (Vercel)" icon="code">
    Track usage with the Vercel AI SDK, which gives one interface to many LLM providers.

    <CodeGroup>
      ```javascript AI SDK Integration theme={null}
      import { createLLMTracker } from '@dodopayments/ingestion-blueprints';
      import { generateText } from 'ai';
      import { google } from '@ai-sdk/google';

      const llmTracker = createLLMTracker({
        apiKey: process.env.DODO_PAYMENTS_API_KEY,
        environment: 'test_mode',
        eventName: 'aisdk.usage',
      });

      const client = llmTracker.wrap({
        client: { generateText },
        customerId: 'cus_123',
        metadata: {
          model: 'gemini-2.0-flash',
          feature: 'chat'
        }
      });

      const response = await client.generateText({
        model: google('gemini-2.0-flash'),
        prompt: 'Explain neural networks',
        maxOutputTokens: 500
      });

      console.log('Usage:', response.usage);
      ```
    </CodeGroup>

    **Tracked Metrics:**

    * `inputTokens` → `inputTokens`
    * `outputTokens` + `reasoningTokens` → `outputTokens`
    * `totalTokens` → `totalTokens`
    * Model name: AI SDK results have no top-level `model` field, so the tracker records `unknown`. To record the model, pass it as `model` in the wrapper `metadata`, as this example does.

    <Note>
      When you use a reasoning-capable model through the AI SDK, such as Google's Gemini 2.5 Flash with thinking mode, the tracker adds the reported reasoning tokens to `outputTokens`.
    </Note>
  </Accordion>

  <Accordion title="OpenRouter" icon="route">
    Track token usage across 200+ models through OpenRouter's unified API.

    <CodeGroup>
      ```javascript OpenRouter Integration theme={null}
      import { createLLMTracker } from '@dodopayments/ingestion-blueprints';
      import OpenAI from 'openai';

      // OpenRouter uses OpenAI-compatible API
      const openrouter = new OpenAI({
        baseURL: 'https://openrouter.ai/api/v1',
        apiKey: process.env.OPENROUTER_API_KEY
      });

      const tracker = createLLMTracker({
        apiKey: process.env.DODO_PAYMENTS_API_KEY,
        environment: 'test_mode',
        eventName: 'openrouter.usage'
      });

      const client = tracker.wrap({ 
        client: openrouter, 
        customerId: 'cus_123',
        metadata: { provider: 'openrouter' }
      });

      const response = await client.chat.completions.create({
        model: 'qwen/qwen3-max',
        messages: [{ role: 'user', content: 'What is machine learning?' }],
        max_tokens: 500
      });

      console.log('Response:', response.choices[0].message.content);
      console.log('Usage:', response.usage);
      ```
    </CodeGroup>

    **Tracked Metrics:**

    * `prompt_tokens` → `inputTokens`
    * `completion_tokens` → `outputTokens`
    * `total_tokens` → `totalTokens`
    * Model name

    <Tip>
      OpenRouter gives access to models from OpenAI, Anthropic, Google, Meta, and other providers through a single API.
    </Tip>
  </Accordion>

  <Accordion title="OpenAI" icon="robot">
    Track token usage from OpenAI's GPT models.

    <CodeGroup>
      ```javascript OpenAI Integration theme={null}
      import { createLLMTracker } from '@dodopayments/ingestion-blueprints';
      import OpenAI from 'openai';

      const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

      const tracker = createLLMTracker({
        apiKey: process.env.DODO_PAYMENTS_API_KEY,
        environment: 'test_mode',
        eventName: 'openai.usage'
      });

      const client = tracker.wrap({ 
        client: openai, 
        customerId: 'cus_123' 
      });

      // All OpenAI methods work automatically
      const response = await client.chat.completions.create({
        model: 'gpt-4',
        messages: [{ role: 'user', content: 'Explain quantum computing' }]
      });

      console.log('Total tokens:', response.usage.total_tokens);
      ```
    </CodeGroup>

    **Tracked Metrics:**

    * `prompt_tokens` → `inputTokens`
    * `completion_tokens` → `outputTokens`
    * `total_tokens` → `totalTokens`
    * Model name
  </Accordion>

  <Accordion title="Anthropic Claude" icon="robot">
    Track token usage from Anthropic's Claude models.

    <CodeGroup>
      ```javascript Anthropic Integration theme={null}
      import { createLLMTracker } from '@dodopayments/ingestion-blueprints';
      import Anthropic from '@anthropic-ai/sdk';

      const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });

      const tracker = createLLMTracker({
        apiKey: process.env.DODO_PAYMENTS_API_KEY,
        environment: 'test_mode',
        eventName: 'anthropic.usage'
      });

      const client = tracker.wrap({ 
        client: anthropic, 
        customerId: 'cus_123' 
      });

      const response = await client.messages.create({
        model: 'claude-sonnet-4-0',
        max_tokens: 1024,
        messages: [{ role: 'user', content: 'Explain machine learning' }]
      });

      console.log('Input tokens:', response.usage.input_tokens);
      console.log('Output tokens:', response.usage.output_tokens);
      ```
    </CodeGroup>

    **Tracked Metrics:**

    * `input_tokens` → `inputTokens`
    * `output_tokens` → `outputTokens`
    * `totalTokens`, calculated as `input_tokens` + `output_tokens`
    * Model name
  </Accordion>

  <Accordion title="Groq" icon="gauge-high">
    Track token usage from models served by Groq.

    <CodeGroup>
      ```javascript Groq Integration theme={null}
      import { createLLMTracker } from '@dodopayments/ingestion-blueprints';
      import Groq from 'groq-sdk';

      const groq = new Groq({ apiKey: process.env.GROQ_API_KEY });

      const tracker = createLLMTracker({
        apiKey: process.env.DODO_PAYMENTS_API_KEY,
        environment: 'test_mode',
        eventName: 'groq.usage'
      });

      const client = tracker.wrap({ 
        client: groq, 
        customerId: 'cus_123' 
      });

      const response = await client.chat.completions.create({
        model: 'llama-3.1-8b-instant',
        messages: [{ role: 'user', content: 'What is AI?' }]
      });

      console.log('Tokens:', response.usage);
      ```
    </CodeGroup>

    **Tracked Metrics:**

    * `prompt_tokens` → `inputTokens`
    * `completion_tokens` → `outputTokens`
    * `total_tokens` → `totalTokens`
    * Model name
  </Accordion>

  <Accordion title="Google Gemini" icon="sparkles">
    Track token usage from Google's Gemini models through the Google GenAI SDK.

    <CodeGroup>
      ```javascript Google Gemini Integration theme={null}
      import { createLLMTracker } from '@dodopayments/ingestion-blueprints';
      import { GoogleGenAI } from '@google/genai';

      const googleGenai = new GoogleGenAI({ 
        apiKey: process.env.GOOGLE_GENERATIVE_AI_API_KEY 
      });

      const tracker = createLLMTracker({
        apiKey: process.env.DODO_PAYMENTS_API_KEY,
        environment: 'test_mode',
        eventName: 'gemini.usage'
      });

      const client = tracker.wrap({ 
        client: googleGenai, 
        customerId: 'cus_123' 
      });

      const response = await client.models.generateContent({
        model: 'gemini-2.5-flash',
        contents: 'Explain quantum computing'
      });

      console.log('Response:', response.text);
      console.log('Usage:', response.usageMetadata);
      ```
    </CodeGroup>

    **Tracked Metrics:**

    * `promptTokenCount` → `inputTokens`
    * `candidatesTokenCount` + `thoughtsTokenCount` → `outputTokens`
    * `totalTokenCount` → `totalTokens`
    * Model version, from `modelVersion`

    <Note>
      **Gemini Thinking Mode:** For Gemini models that think before they answer, such as Gemini 2.5 Pro, the tracker adds `thoughtsTokenCount` (reasoning tokens) to `outputTokens`, so the event reflects the full output the model produced.
    </Note>
  </Accordion>
</AccordionGroup>

## Advanced Usage

### Multiple Providers

To track usage across LLM providers separately, create one tracker per provider:

<CodeGroup>
  ```javascript Multiple Provider Setup theme={null}
  import { createLLMTracker } from '@dodopayments/ingestion-blueprints';
  import OpenAI from 'openai';
  import Groq from 'groq-sdk';
  import Anthropic from '@anthropic-ai/sdk';
  import { GoogleGenAI } from '@google/genai';

  // Create separate trackers for different providers
  const openaiTracker = createLLMTracker({
    apiKey: process.env.DODO_PAYMENTS_API_KEY,
    environment: 'live_mode',
    eventName: 'openai.usage'
  });

  const groqTracker = createLLMTracker({
    apiKey: process.env.DODO_PAYMENTS_API_KEY,
    environment: 'live_mode',
    eventName: 'groq.usage'
  });

  const anthropicTracker = createLLMTracker({
    apiKey: process.env.DODO_PAYMENTS_API_KEY,
    environment: 'live_mode',
    eventName: 'anthropic.usage'
  });

  const geminiTracker = createLLMTracker({
    apiKey: process.env.DODO_PAYMENTS_API_KEY,
    environment: 'live_mode',
    eventName: 'gemini.usage'
  });

  const openrouterTracker = createLLMTracker({
    apiKey: process.env.DODO_PAYMENTS_API_KEY,
    environment: 'live_mode',
    eventName: 'openrouter.usage'
  });

  // Initialize clients
  const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
  const groq = new Groq({ apiKey: process.env.GROQ_API_KEY });
  const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
  const googleGenai = new GoogleGenAI({ apiKey: process.env.GOOGLE_GENERATIVE_AI_API_KEY });
  const openrouter = new OpenAI({ 
    baseURL: 'https://openrouter.ai/api/v1',
    apiKey: process.env.OPENROUTER_API_KEY 
  });

  // Wrap clients
  const trackedOpenAI = openaiTracker.wrap({ client: openai, customerId: 'cus_123' });
  const trackedGroq = groqTracker.wrap({ client: groq, customerId: 'cus_123' });
  const trackedAnthropic = anthropicTracker.wrap({ client: anthropic, customerId: 'cus_123' });
  const trackedGemini = geminiTracker.wrap({ client: googleGenai, customerId: 'cus_123' });
  const trackedOpenRouter = openrouterTracker.wrap({ client: openrouter, customerId: 'cus_123' });

  // Use whichever provider you need
  const response = await trackedOpenAI.chat.completions.create({
    model: 'gpt-4',
    messages: [{ role: 'user', content: 'Hello!' }]
  });
  // or
  const geminiResponse = await trackedGemini.models.generateContent({
    model: 'gemini-2.5-flash',
    contents: 'Hello!'
  });
  // or
  const openrouterResponse = await trackedOpenRouter.chat.completions.create({
    model: 'qwen/qwen3-max',
    messages: [{ role: 'user', content: 'Hello!' }]
  });
  ```
</CodeGroup>

<Tip>
  Use a different event name for each provider, with a meter for each, to track usage separately.
</Tip>

### Express.js API Integration

This Express.js API tracks each chat completion for the customer who made the request. For brevity, it reads `userId` from the request body. `userId` must be the user's Dodo Payments customer ID. In production, read it from the authenticated session instead of trusting the request body.

<CodeGroup>
  ```javascript Express.js Server theme={null}
  import express from 'express';
  import { createLLMTracker } from '@dodopayments/ingestion-blueprints';
  import OpenAI from 'openai';

  const app = express();
  app.use(express.json());

  // Initialize OpenAI client
  const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

  // Create tracker once at startup
  const tracker = createLLMTracker({
    apiKey: process.env.DODO_PAYMENTS_API_KEY,
    environment: process.env.NODE_ENV === 'production' ? 'live_mode' : 'test_mode',
    eventName: 'api.chat_completion'
  });

  // Chat endpoint with automatic tracking
  app.post('/api/chat', async (req, res) => {
    try {
      const { message, userId } = req.body;
      
      // Validate input
      if (!message || !userId) {
        return res.status(400).json({ error: 'Missing message or userId' });
      }
      
      // Wrap client for this specific user
      const trackedClient = tracker.wrap({
        client: openai,
        customerId: userId,
        metadata: { 
          endpoint: '/api/chat',
          timestamp: new Date().toISOString()
        }
      });
      
      // Make LLM request - automatically tracked
      const response = await trackedClient.chat.completions.create({
        model: 'gpt-4',
        messages: [{ role: 'user', content: message }],
        temperature: 0.7
      });
      
      const completion = response.choices[0].message.content;
      
      res.json({ 
        message: completion,
        usage: response.usage
      });
    } catch (error) {
      console.error('Chat error:', error);
      res.status(500).json({ error: 'Internal server error' });
    }
  });

  app.listen(3000, () => {
    console.log('Server running on port 3000');
  });
  ```
</CodeGroup>

## What Gets Tracked

Each tracked call sends one usage event to Dodo Payments with this structure:

<CodeGroup>
  ```json Event Structure theme={null}
  {
    "event_id": "llm_1704709800000_abc123",
    "customer_id": "cus_123",
    "event_name": "llm.chat_completion",
    "timestamp": "2024-01-08T10:30:00.000Z",
    "metadata": {
      "inputTokens": 10,
      "outputTokens": 25,
      "totalTokens": 35,
      "model": "gpt-4"
    }
  }
  ```
</CodeGroup>

### Event Fields

<ParamField path="event_id" type="string">
  Unique identifier for this event. The SDK generates it.

  Format: `llm_[timestamp]_[random]`, where `timestamp` is the time in milliseconds and `random` is six random characters.
</ParamField>

<ParamField path="customer_id" type="string">
  The customer ID you passed when you wrapped the client. Dodo Payments bills this customer.
</ParamField>

<ParamField path="event_name" type="string">
  The event name that triggers your meter. It comes from your tracker configuration.
</ParamField>

<ParamField path="timestamp" type="string">
  ISO 8601 timestamp, set when the tracker sends the event after the provider responds.
</ParamField>

<ParamField path="metadata" type="object">
  Token usage and additional tracking data:

  * `inputTokens`: number of input (prompt) tokens used.
  * `outputTokens`: number of output (completion) tokens used, including reasoning tokens when applicable.
  * `totalTokens`: total tokens (input + output).
  * `model`: the LLM model used, such as `gpt-4`, or `unknown` if the response doesn't name one.
  * `provider`: the LLM provider, if you included it in the wrapper metadata.
  * Any custom metadata you provided when you wrapped the client.

  <Note>
    **Reasoning Tokens:** For models with reasoning capabilities, `outputTokens` includes both the completion tokens and the reasoning tokens.
  </Note>
</ParamField>

<Info>
  Your Dodo Payments meter uses the `metadata` fields, usually `inputTokens`, `outputTokens`, or `totalTokens`, to calculate usage and billing.
</Info>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.