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

# Sending Transactions

> How to track AI usage with Fenra

Every AI API call can be tracked as a transaction.

## Basic Flow

1. Make your AI provider call
2. Extract usage from the response
3. Send to Fenra's API

```javascript theme={null}
// 1. Make the AI call
const response = await openai.chat.completions.create({
  model: "gpt-4o",
  messages: [{ role: "user", content: "Hello!" }]
});

// 2. Extract usage and send to Fenra
await fetch('https://ingest.fenra.io/usage/transactions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Api-Key': process.env.FENRA_API_KEY
  },
  body: JSON.stringify({
    provider: 'openai',
    model: response.model,
    usage: [{
      type: 'tokens',
      metrics: {
        input_tokens: response.usage.prompt_tokens,
        output_tokens: response.usage.completion_tokens,
        total_tokens: response.usage.total_tokens
      }
    }],
    context: {
      billable_customer_id: 'my-company',
      environment: 'production',
      feature: 'chat'
    }
  })
});
```

## Transaction Schema

Every transaction needs:

| Field                          | Required | Description                                                             |
| ------------------------------ | -------- | ----------------------------------------------------------------------- |
| `provider`                     | Yes      | `openai`, `anthropic`, `gemini`, `bedrock`, `xai`, `deepseek`, `custom` |
| `model`                        | Yes      | Model identifier (e.g., `gpt-4o`)                                       |
| `usage`                        | Yes      | What resources the AI call consumed (tokens, images, audio, etc.)       |
| `context.billable_customer_id` | Yes      | Your internal identifier for billing                                    |

## Context

Only `billable_customer_id` is required. Everything else is flexible context you can use for filtering, alerts, and reports.

**Example with common fields:**

```json theme={null}
{
  "context": {
    "billable_customer_id": "acme-corp",
    "environment": "production",
    "feature": "chat-assistant",
    "user_id": "user-123",
    "request_id": "req-abc",
    "session_id": "sess-xyz",
    "team": "growth",
    "experiment": "pricing-v2"
  }
}
```

**What you can do with context fields:**

* **Filter in Cost Explorer**: Show only production costs, or only a specific feature
* **Create targeted alerts**: Alert when a specific feature exceeds a threshold
* **Build custom dashboards**: Widgets filtered to specific context
* **Generate filtered reports**: Reports scoped to specific environments or teams

<Tip>
  Send whatever context makes sense for your use case. If you can filter by it, you can track it.
</Tip>

## Usage Types

### Tokens (most common)

For text generation, chat, and reasoning models:

```json theme={null}
{
  "type": "tokens",
  "metrics": {
    "input_tokens": 100,
    "output_tokens": 50,
    "total_tokens": 150
  }
}
```

**Optional token metrics:**

| Field                         | Description                          |
| ----------------------------- | ------------------------------------ |
| `cached_tokens`               | Tokens served from provider cache    |
| `cache_read_input_tokens`     | Tokens read from cache               |
| `cache_creation_input_tokens` | Tokens used to create cache          |
| `reasoning_tokens`            | Tokens for reasoning (o1, o3 models) |
| `thinking_tokens`             | Alias for reasoning\_tokens          |

### Images

For image generation:

```json theme={null}
{
  "type": "images",
  "metrics": {
    "generated": 1,
    "size_px": 1024
  }
}
```

### Audio

For speech-to-text or text-to-speech:

```json theme={null}
{
  "type": "audio_seconds",
  "metrics": {
    "input_seconds": 30.5,
    "output_seconds": 0,
    "total_seconds": 30.5
  }
}
```

### Video

For video processing:

```json theme={null}
{
  "type": "video_seconds",
  "metrics": {
    "processed_seconds": 45.0
  }
}
```

### Requests

For flat per-request pricing:

```json theme={null}
{
  "type": "requests",
  "metrics": {
    "count": 1
  }
}
```

### Custom

For custom billing models:

```json theme={null}
{
  "type": "custom",
  "metrics": {
    "units": 3.5
  }
}
```

## Bulk Transactions

Send multiple transactions in one request:

```javascript theme={null}
await fetch('https://ingest.fenra.io/usage/transactions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Api-Key': process.env.FENRA_API_KEY
  },
  body: JSON.stringify({
    transactions: [
      { provider: 'openai', model: 'gpt-4o', ... },
      { provider: 'anthropic', model: 'claude-3-5-sonnet', ... }
    ]
  })
});
```

## Response Codes

| Code  | Meaning                                 |
| ----- | --------------------------------------- |
| `202` | Transaction queued successfully         |
| `207` | Partial success (some failed in bulk)   |
| `400` | Validation error. Check request format. |
| `401` | Invalid API key                         |
| `500` | Server error. Safe to retry.            |

## Next Steps

* See [OpenAI Integration](/integrations/openai) for provider-specific examples
* See [API Reference](/api-reference/endpoint/create) for full schema details
