> ## 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.

# POST /usage/transactions

> Ingest AI usage transactions for cost tracking. Costs are calculated automatically based on the provider's pricing. Supports both single transaction and bulk submissions. Use this endpoint after each AI provider call to track cost.

Send AI usage transactions for cost tracking.

## Request Formats

### Single Transaction

```json theme={null}
{
  "provider": "openai",
  "model": "gpt-4o",
  "usage": [{
    "type": "tokens",
    "metrics": {
      "input_tokens": 100,
      "output_tokens": 50,
      "total_tokens": 150
    }
  }],
  "context": {
    "billable_customer_id": "my-company"
  }
}
```

### Bulk Request

```json theme={null}
{
  "transactions": [
    { "provider": "openai", "model": "gpt-4o", ... },
    { "provider": "anthropic", "model": "claude-3-5-sonnet", ... }
  ]
}
```

## Context

Only `billable_customer_id` is required in the `context` object. You can add any additional fields. They will be stored and available for filtering in your dashboard.

```json theme={null}
{
  "context": {
    "billable_customer_id": "acme-corp"
  }
}
```

## Response Codes

| Code                 | Meaning                     |
| -------------------- | --------------------------- |
| `202 Accepted`       | All transactions queued     |
| `207 Multi-Status`   | Some succeeded, some failed |
| `400 Bad Request`    | Validation error            |
| `401 Unauthorized`   | Invalid API key             |
| `500 Internal Error` | Server error                |

## Examples

### Basic Request

```bash theme={null}
curl -X POST 'https://ingest.fenra.io/usage/transactions' \
  -H 'Content-Type: application/json' \
  -H 'X-Api-Key: YOUR_API_KEY' \
  -d '{
    "provider": "openai",
    "model": "gpt-4o",
    "usage": [{
      "type": "tokens",
      "metrics": {
        "input_tokens": 100,
        "output_tokens": 50,
        "total_tokens": 150
      }
    }],
    "context": {
      "billable_customer_id": "my-company"
    }
  }'
```

**Response:**

```json theme={null}
{
  "status": "queued",
  "events_queued": 1,
  "message": "1 transaction(s) queued for processing",
  "results": [{
    "transaction_index": 0,
    "message_id": "abc123-def456-ghi789"
  }]
}
```

## Error Responses

### Validation Error (400)

```json theme={null}
{
  "error": {
    "code": "validation_error",
    "message": "Request validation failed",
    "details": [
      {
        "field": "context.billable_customer_id",
        "code": "required",
        "message": "billable_customer_id is required"
      }
    ]
  }
}
```

### Unauthorized (401)

```json theme={null}
{
  "error": {
    "code": "unauthorized",
    "message": "Invalid or missing API key"
  }
}
```

### Partial Success (207)

```json theme={null}
{
  "status": "partial_success",
  "events_queued": 1,
  "events_failed": 1,
  "message": "1 transaction(s) queued, 1 transaction(s) failed",
  "results": [
    {
      "transaction_index": 0,
      "message_id": "abc123"
    },
    {
      "transaction_index": 1,
      "message_id": null,
      "error": "Validation failed"
    }
  ]
}
```

## See Also

* [Transactions Guide](/guides/transactions)
* [Error Reference](/api-reference/errors)
* [Authentication](/api-reference/authentication)


## OpenAPI

````yaml POST /usage/transactions
openapi: 3.1.0
info:
  title: Fenra Usage Ingestion API
  description: >-
    Send AI usage data to Fenra for automatic cost calculation and analytics.
    Supports all major providers (OpenAI, Anthropic, Google, AWS Bedrock, xAI,
    DeepSeek) with both single and bulk transaction submissions. Costs are
    calculated automatically based on each provider's pricing.
  version: 1.0.0
  contact:
    name: Fenra
servers:
  - url: https://ingest.fenra.io
    description: Production server
security:
  - ApiKeyAuth: []
tags:
  - name: Usage
    description: Operations for ingesting LLM usage data
paths:
  /usage/transactions:
    post:
      tags:
        - Usage
      summary: Send AI usage data
      description: >-
        Ingest AI usage transactions for cost tracking. Costs are calculated
        automatically based on the provider's pricing. Supports both single
        transaction and bulk submissions. Use this endpoint after each AI
        provider call to track cost.
      operationId: ingestUsage
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UsageIngestionRequest'
            examples:
              basicExample:
                summary: Basic token usage
                description: Minimal example with text generation tokens
                value:
                  provider: openai
                  model: gpt-4o
                  usage:
                    - type: tokens
                      metrics:
                        input_tokens: 150
                        output_tokens: 50
                        total_tokens: 200
                  context:
                    billable_customer_id: acme-corp
              withContext:
                summary: With custom context
                description: Include environment, feature, and user tracking
                value:
                  provider: openai
                  model: gpt-4o
                  usage:
                    - type: tokens
                      metrics:
                        input_tokens: 500
                        output_tokens: 200
                        total_tokens: 700
                  context:
                    billable_customer_id: acme-corp
                    environment: production
                    feature: chat-assistant
                    user_id: user_abc123
              reasoning:
                summary: Reasoning tokens (o1/o3)
                description: Track reasoning tokens for OpenAI o1 and o3 models
                value:
                  provider: openai
                  model: o1-preview
                  usage:
                    - type: tokens
                      metrics:
                        input_tokens: 200
                        output_tokens: 1500
                        total_tokens: 1700
                        reasoning_tokens: 1200
                  context:
                    billable_customer_id: acme-corp
                    feature: code-review
              imageGeneration:
                summary: Image generation
                description: Track DALL-E or similar image generation
                value:
                  provider: openai
                  model: dall-e-3
                  usage:
                    - type: images
                      metrics:
                        generated: 2
                        size_px: 1024
                  context:
                    billable_customer_id: acme-corp
                    feature: image-creator
              audioGeneration:
                summary: Audio transcription (Whisper)
                description: Track speech-to-text transcription
                value:
                  provider: openai
                  model: whisper-1
                  usage:
                    - type: audio_seconds
                      metrics:
                        input_seconds: 125.5
                  context:
                    billable_customer_id: acme-corp
                    feature: voice-notes
              textToSpeech:
                summary: Text-to-speech
                description: Track TTS audio generation
                value:
                  provider: openai
                  model: tts-1-hd
                  usage:
                    - type: audio_seconds
                      metrics:
                        output_seconds: 45.2
                  context:
                    billable_customer_id: acme-corp
                    feature: podcast-generator
              videoGeneration:
                summary: Video processing
                description: Track video analysis or generation
                value:
                  provider: gemini
                  model: gemini-1.5-pro
                  usage:
                    - type: video_seconds
                      metrics:
                        processed_seconds: 60
                  context:
                    billable_customer_id: acme-corp
                    feature: video-analyzer
      responses:
        '202':
          description: All transactions queued successfully
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
            Idempotency-Key:
              $ref: '#/components/headers/Idempotency-Key'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
              example:
                status: queued
                events_queued: 2
                message: 2 transaction(s) queued for processing
                results:
                  - transaction_index: 0
                    message_id: abc123-def456
                  - transaction_index: 1
                    message_id: ghi789-jkl012
        '207':
          description: Partial success - some transactions queued, some failed
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
            Idempotency-Key:
              $ref: '#/components/headers/Idempotency-Key'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PartialSuccessResponse'
              example:
                status: partial_success
                events_queued: 1
                events_failed: 1
                message: 1 transaction(s) queued, 1 transaction(s) failed
                results:
                  - transaction_index: 0
                    message_id: abc123-def456
                  - transaction_index: 1
                    message_id: null
                    error: Failed to queue transaction
        '400':
          description: Bad request - validation error or invalid JSON
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                validationError:
                  summary: Validation error
                  value:
                    error:
                      code: validation_error
                      message: Request validation failed
                      details:
                        - field: provider
                          code: invalid_value
                          message: >-
                            provider must be one of: openai, gemini, bedrock,
                            anthropic, xai, deepseek, custom
                invalidJson:
                  summary: Invalid JSON
                  value:
                    error:
                      code: invalid_json
                      message: Request body is not valid JSON
        '401':
          description: Unauthorized - missing or invalid API key
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                missingApiKey:
                  summary: Missing API key
                  value:
                    error:
                      code: unauthorized
                      message: X-Api-Key header is required
                invalidApiKey:
                  summary: Invalid API key
                  value:
                    error:
                      code: unauthorized
                      message: Invalid or missing API key
        '500':
          description: Internal server error - all transactions failed
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FailureResponse'
              example:
                error:
                  code: internal_error
                  message: Failed to queue all transactions
                events_failed: 2
                results:
                  - transaction_index: 0
                    message_id: null
                    error: Failed to queue transaction
                  - transaction_index: 1
                    message_id: null
                    error: Failed to queue transaction
components:
  schemas:
    UsageIngestionRequest:
      oneOf:
        - $ref: '#/components/schemas/Transaction'
        - $ref: '#/components/schemas/BulkUsageIngestionRequest'
      description: >-
        Either a single transaction or a bulk request containing multiple
        transactions
    SuccessResponse:
      type: object
      required:
        - status
        - events_queued
        - message
        - results
      properties:
        status:
          type: string
          const: queued
          description: Status indicating all transactions were queued
        events_queued:
          type: integer
          minimum: 1
          description: Number of transactions successfully queued
        message:
          type: string
          description: Human-readable success message
        results:
          type: array
          items:
            $ref: '#/components/schemas/TransactionResult'
    PartialSuccessResponse:
      type: object
      required:
        - status
        - events_queued
        - events_failed
        - message
        - results
      properties:
        status:
          type: string
          const: partial_success
          description: Status indicating some transactions succeeded and some failed
        events_queued:
          type: integer
          minimum: 0
          description: Number of transactions successfully queued
        events_failed:
          type: integer
          minimum: 1
          description: Number of transactions that failed to queue
        message:
          type: string
          description: Human-readable status message
        results:
          type: array
          items:
            $ref: '#/components/schemas/TransactionResultWithError'
    ErrorResponse:
      type: object
      required:
        - error
      properties:
        error:
          $ref: '#/components/schemas/Error'
    FailureResponse:
      type: object
      required:
        - error
        - events_failed
        - results
      properties:
        error:
          $ref: '#/components/schemas/Error'
        events_failed:
          type: integer
          minimum: 1
          description: Number of transactions that failed to queue
        results:
          type: array
          items:
            $ref: '#/components/schemas/TransactionResultWithError'
    Transaction:
      title: Single Transaction
      type: object
      required:
        - provider
        - model
        - usage
        - context
      properties:
        provider:
          $ref: '#/components/schemas/LLMProvider'
        model:
          type: string
          minLength: 1
          description: The model identifier (e.g., 'gpt-4o', 'claude-3-opus', 'gemini-pro')
          examples:
            - gpt-4o
            - claude-3-opus
            - gemini-1.5-pro
        model_tier:
          type: string
          description: Optional model tier classification for custom pricing tiers
        usage:
          type: array
          description: >-
            Array of usage entries. Each usage type may appear at most once per
            transaction.
          minItems: 1
          items:
            $ref: '#/components/schemas/UsageEntry'
        context:
          $ref: '#/components/schemas/Context'
        provider_usage_raw:
          type: object
          description: Optional raw usage data from the provider for debugging or auditing
          additionalProperties: true
    BulkUsageIngestionRequest:
      title: Bulk Request
      type: object
      required:
        - transactions
      properties:
        transactions:
          type: array
          description: Array of transactions to process
          minItems: 1
          items:
            $ref: '#/components/schemas/Transaction'
    TransactionResult:
      type: object
      required:
        - transaction_index
        - message_id
      properties:
        transaction_index:
          type: integer
          minimum: 0
          description: Index of the transaction in the request
        message_id:
          type: string
          description: SQS message ID for the queued transaction
    TransactionResultWithError:
      type: object
      required:
        - transaction_index
        - message_id
      properties:
        transaction_index:
          type: integer
          minimum: 0
          description: Index of the transaction in the request
        message_id:
          type:
            - string
            - 'null'
          description: SQS message ID if successful, null if failed
        error:
          type: string
          description: Error message if the transaction failed to queue
    Error:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: string
          enum:
            - validation_error
            - invalid_json
            - unauthorized
            - internal_error
            - partial_failure
          description: Error code for programmatic handling
        message:
          type: string
          description: Human-readable error message
        details:
          type: array
          items:
            $ref: '#/components/schemas/FieldError'
          description: Detailed field-level errors for validation failures
    LLMProvider:
      type: string
      enum:
        - openai
        - gemini
        - bedrock
        - anthropic
        - xai
        - deepseek
        - custom
      description: >-
        AI provider. Fenra automatically applies the correct pricing for each
        provider. Use 'custom' for providers not yet supported, combined with
        custom pricing configuration in your dashboard.
    UsageEntry:
      oneOf:
        - $ref: '#/components/schemas/TokensUsageEntry'
        - $ref: '#/components/schemas/ImagesUsageEntry'
        - $ref: '#/components/schemas/AudioUsageEntry'
        - $ref: '#/components/schemas/VideoUsageEntry'
        - $ref: '#/components/schemas/RequestsUsageEntry'
        - $ref: '#/components/schemas/CustomUsageEntry'
      discriminator:
        propertyName: type
        mapping:
          tokens: '#/components/schemas/TokensUsageEntry'
          images: '#/components/schemas/ImagesUsageEntry'
          audio_seconds: '#/components/schemas/AudioUsageEntry'
          video_seconds: '#/components/schemas/VideoUsageEntry'
          requests: '#/components/schemas/RequestsUsageEntry'
          custom: '#/components/schemas/CustomUsageEntry'
    Context:
      type: object
      required:
        - billable_customer_id
      properties:
        billable_customer_id:
          type: string
          minLength: 1
          description: >-
            Your internal identifier for grouping and billing. This is the
            primary dimension for cost attribution in your Fenra dashboard.
      additionalProperties: true
      description: >-
        Only billable_customer_id is required. Add any additional fields
        (environment, feature, user_id, team, etc.) and they will appear in your
        dashboard for filtering, alerts, and reports.
    FieldError:
      type: object
      required:
        - field
        - code
        - message
      properties:
        field:
          type: string
          description: >-
            Path to the field that caused the error (e.g., 'provider',
            'usage.0.metrics.input_tokens')
        code:
          type: string
          enum:
            - required
            - invalid_type
            - invalid_value
            - too_small
            - too_large
            - invalid_format
          description: Field error code for programmatic handling
        message:
          type: string
          description: Human-readable error message for this field
    TokensUsageEntry:
      title: Tokens
      type: object
      required:
        - type
        - metrics
      properties:
        type:
          type: string
          const: tokens
        metrics:
          $ref: '#/components/schemas/TokensUsageMetrics'
    ImagesUsageEntry:
      title: Images
      type: object
      required:
        - type
        - metrics
      properties:
        type:
          type: string
          const: images
        metrics:
          $ref: '#/components/schemas/ImagesUsageMetrics'
    AudioUsageEntry:
      title: Audio
      type: object
      required:
        - type
        - metrics
      properties:
        type:
          type: string
          const: audio_seconds
        metrics:
          $ref: '#/components/schemas/AudioUsageMetrics'
    VideoUsageEntry:
      title: Video
      type: object
      required:
        - type
        - metrics
      properties:
        type:
          type: string
          const: video_seconds
        metrics:
          $ref: '#/components/schemas/VideoUsageMetrics'
    RequestsUsageEntry:
      title: Requests
      type: object
      required:
        - type
        - metrics
      properties:
        type:
          type: string
          const: requests
        metrics:
          $ref: '#/components/schemas/RequestsUsageMetrics'
    CustomUsageEntry:
      title: Custom
      type: object
      required:
        - type
        - metrics
      properties:
        type:
          type: string
          const: custom
        metrics:
          $ref: '#/components/schemas/CustomUsageMetrics'
    TokensUsageMetrics:
      type: object
      required:
        - input_tokens
        - output_tokens
        - total_tokens
      properties:
        input_tokens:
          type: integer
          minimum: 1
          description: >-
            Number of input/prompt tokens. All providers (OpenAI, Anthropic,
            Gemini, xAI, Bedrock, DeepSeek)
        output_tokens:
          type: integer
          minimum: 1
          description: >-
            Number of output/completion tokens. All providers (OpenAI,
            Anthropic, Gemini, xAI, Bedrock, DeepSeek)
        total_tokens:
          type: integer
          minimum: 1
          description: >-
            Total tokens (input + output). All providers (OpenAI, Anthropic,
            Gemini, xAI, Bedrock, DeepSeek)
        cached_tokens:
          type: integer
          minimum: 0
          description: >-
            Number of cached tokens. OpenAI
            (prompt_tokens_details.cached_tokens), Gemini
            (cachedContentTokenCount), xAI (cached_tokens)
        cache_read_input_tokens:
          type: integer
          minimum: 0
          description: >-
            Number of tokens read from cache. Anthropic
            (cache_read_input_tokens)
        cache_creation_input_tokens:
          type: integer
          minimum: 0
          description: >-
            Number of tokens used to create cache. Anthropic
            (cache_creation_input_tokens)
        reasoning_tokens:
          type: integer
          minimum: 0
          description: >-
            Number of reasoning tokens. OpenAI o1/o3/o4 models
            (completion_tokens_details.reasoning_tokens), xAI Grok-3/4
            (reasoning_tokens)
        thinking_tokens:
          type: integer
          minimum: 0
          description: >-
            Number of thinking tokens. Anthropic extended thinking
            (thinking_tokens), Gemini (thoughts_token_count)
        prompt_tokens:
          type: integer
          minimum: 0
          description: Alias for input_tokens. OpenAI legacy (prompt_tokens)
        completion_tokens:
          type: integer
          minimum: 0
          description: Alias for output_tokens. OpenAI legacy (completion_tokens)
        prompt_text_tokens:
          type: integer
          minimum: 0
          description: >-
            Number of text tokens in prompt. OpenAI multimodal
            (input_tokens_details.text_tokens)
        text_tokens:
          type: integer
          minimum: 0
          description: >-
            Number of text tokens. OpenAI, xAI multimodal
            (prompt_tokens_details.text_tokens)
        audio_tokens:
          type: integer
          minimum: 0
          description: >-
            Number of audio tokens. OpenAI, xAI realtime/audio
            (prompt_tokens_details.audio_tokens)
        image_tokens:
          type: integer
          minimum: 0
          description: >-
            Number of image tokens. OpenAI, xAI vision
            (prompt_tokens_details.image_tokens)
      additionalProperties: true
    ImagesUsageMetrics:
      type: object
      required:
        - generated
      properties:
        generated:
          type: integer
          minimum: 0
          description: >-
            Number of images generated. All providers (OpenAI DALL-E, GPT Image,
            Gemini Imagen, xAI Grok-2 Image)
        size_px:
          type: integer
          minimum: 0
          description: >-
            Image size in pixels (width or height for square images). OpenAI
            DALL-E, Gemini Imagen (1024 = 1024x1024)
        quality:
          type: string
          enum:
            - low
            - medium
            - high
            - standard
            - hd
          description: >-
            Image quality tier. OpenAI GPT Image 1/1.5 (low/medium/high), DALL-E
            (standard/hd)
        width_px:
          type: integer
          minimum: 1
          description: Explicit width for non-square images. OpenAI, Gemini
        height_px:
          type: integer
          minimum: 1
          description: Explicit height for non-square images. OpenAI, Gemini
      additionalProperties: true
    AudioUsageMetrics:
      type: object
      properties:
        input_seconds:
          type: number
          minimum: 0
          description: >-
            Duration of audio input in seconds. OpenAI Whisper transcription,
            xAI voice
        output_seconds:
          type: number
          minimum: 0
          description: Duration of audio output in seconds. OpenAI TTS, Gemini TTS
        total_seconds:
          type: number
          minimum: 0
          description: Total audio duration in seconds. All audio providers
      additionalProperties: true
    VideoUsageMetrics:
      type: object
      required:
        - processed_seconds
      properties:
        processed_seconds:
          type: number
          minimum: 0
          description: >-
            Duration of video generated/processed in seconds. OpenAI Sora,
            Gemini Veo
      additionalProperties: true
    RequestsUsageMetrics:
      type: object
      required:
        - count
      properties:
        count:
          type: integer
          minimum: 0
          description: Number of tool invocations. All providers
        request_type:
          type: string
          enum:
            - web_search
            - code_execution
            - file_search
            - x_search
            - live_search
            - google_grounding
            - maps_grounding
            - document_search
            - collections_search
          description: >-
            Type of tool invocation. Anthropic (web_search, code_execution), xAI
            (web_search, x_search, code_execution, document_search,
            collections_search), Gemini (google_grounding, maps_grounding,
            file_search)
        sources_used:
          type: integer
          minimum: 0
          description: Number of sources used. xAI Live Search (billed separately)
      additionalProperties: true
    CustomUsageMetrics:
      type: object
      required:
        - units
      properties:
        units:
          type: number
          minimum: 0
          description: Number of custom units consumed
      additionalProperties: true
  headers:
    X-Request-Id:
      description: Unique identifier for the request, useful for debugging and support
      schema:
        type: string
        format: uuid
        example: 550e8400-e29b-41d4-a716-446655440000
    Idempotency-Key:
      description: >-
        Message ID of the first successfully queued transaction, can be used for
        idempotency tracking
      schema:
        type: string
        example: abc123-def456-ghi789
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-Api-Key
      description: >-
        API key for authentication. Must be associated with an active
        organization.

````