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

# Event Ingestion API Reference

> Complete API reference for sending events to Flexprice

This document provides the complete API reference for sending events to Flexprice. It includes all endpoints, request/response formats, error codes, and examples.

## Base URL

```
https://api.cloud.flexprice.io/v1
```

## Authentication

All requests require authentication using an API key in the request header:

```
x-api-key: <your_api_key>
```

**Security Note**: Keep your API key secure and never expose it in client-side code or public repositories.

## Endpoints

### Single Event

Send a single event to Flexprice.

**Endpoint**: `POST /events`

**Headers**:

```
Content-Type: application/json
x-api-key: <your_api_key>
```

**Request Body**:

```json theme={null}
{
  "event_name": "string",
  "external_customer_id": "string",
  "properties": {
    "string": "any"
  },
  "event_id": "string",
  "timestamp": "string",
  "source": "string"
}
```

**Response**:

* **Status**: `202 Accepted`
* **Body**:

```json theme={null}
{
  "event_id": "string",
  "message": "Event accepted for processing"
}
```

### Bulk Events

Send multiple events in a single request for better performance.

**Endpoint**: `POST /events/bulk`

**Headers**:

```
Content-Type: application/json
x-api-key: <your_api_key>
```

**Request Body**:

```json theme={null}
{
  "events": [
    {
      "event_name": "string",
      "external_customer_id": "string",
      "properties": {
        "string": "any"
      },
      "event_id": "string",
      "timestamp": "string",
      "source": "string"
    }
  ]
}
```

**Response**:

* **Status**: `202 Accepted`
* **Body**:

```json theme={null}
{
  "event_ids": ["string"],
  "message": "Events accepted for processing"
}
```

## Request Fields

### Required Fields

| Field                  | Type   | Description                                  | Example         |
| ---------------------- | ------ | -------------------------------------------- | --------------- |
| `event_name`           | string | Must exactly match your feature's Event Name | `"model.usage"` |
| `external_customer_id` | string | Your identifier for the customer             | `"cust_123"`    |

### Conditional Fields

| Field                | Type          | When Required                      | Description                          | Example             |
| -------------------- | ------------- | ---------------------------------- | ------------------------------------ | ------------------- |
| `properties`         | object        | For Sum, Max, Latest, Unique Count | Contains values to aggregate         | `{"credits": 2}`    |
| `properties.<field>` | number/string | When aggregation field is set      | Exact field name from feature config | `2` or `"user_123"` |

### Optional Fields

| Field       | Type   | Description                          | Example                      |
| ----------- | ------ | ------------------------------------ | ---------------------------- |
| `event_id`  | string | Your unique identifier for the event | `"evt_abc123"`               |
| `timestamp` | string | ISO 8601 UTC timestamp               | `"2025-08-22T07:05:49.441Z"` |
| `source`    | string | Origin of the event                  | `"api"`, `"worker"`          |

## Data Types

### Property Values

* **Numbers**: Use for Sum, Max aggregations

  ```json theme={null}
  "properties": { "credits": 2 }
  ```

* **Strings**: Use for Unique Count, Latest aggregations
  ```json theme={null}
  "properties": { "user_id": "user_123" }
  ```

### Timestamps

* **Format**: ISO 8601 UTC
* **Example**: `"2025-08-22T07:05:49.441Z"`
* **Default**: Server time if omitted

## Examples

### Basic Event (Count Aggregation)

```bash theme={null}
curl --request POST \
  --url https://api.cloud.flexprice.io/v1/events \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <your_api_key>' \
  --data '{
    "event_name": "api.calls",
    "external_customer_id": "cust_123"
  }'
```

### Event with Properties (Sum Aggregation)

```bash theme={null}
curl --request POST \
  --url https://api.cloud.flexprice.io/v1/events \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <your_api_key>' \
  --data '{
    "event_name": "model.usage",
    "external_customer_id": "cust_123",
    "properties": {
      "credits": 2
    }
  }'
```

### Complete Event

```bash theme={null}
curl --request POST \
  --url https://api.cloud.flexprice.io/v1/events \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <your_api_key>' \
  --data '{
    "event_name": "model.usage",
    "external_customer_id": "cust_123",
    "properties": {
      "credits": 2,
      "model": "gpt-4",
      "region": "us-east-1"
    },
    "event_id": "evt_abc123",
    "timestamp": "2025-08-22T07:05:49.441Z",
    "source": "api"
  }'
```

### Bulk Events

```bash theme={null}
curl --request POST \
  --url https://api.cloud.flexprice.io/v1/events/bulk \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <your_api_key>' \
  --data '{
    "events": [
      {
        "event_name": "model.usage",
        "external_customer_id": "cust_123",
        "properties": { "credits": 2 }
      },
      {
        "event_name": "model.usage",
        "external_customer_id": "cust_456",
        "properties": { "credits": 5 }
      }
    ]
  }'
```

## Language Examples

### JavaScript (Node.js)

```javascript theme={null}
const fetch = require("node-fetch");

async function sendEvent() {
  const response = await fetch("https://api.cloud.flexprice.io/v1/events", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-api-key": process.env.FLEXPRICE_API_KEY,
    },
    body: JSON.stringify({
      event_name: "model.usage",
      external_customer_id: "cust_123",
      properties: { credits: 2 },
      source: "api",
    }),
  });

  if (response.status === 202) {
    const result = await response.json();
    console.log("Event sent:", result);
  } else {
    const error = await response.text();
    console.error("Error:", error);
  }
}
```

### Python

```python theme={null}
import os
import requests
import json

def send_event():
    url = "https://api.cloud.flexprice.io/v1/events"
    headers = {
        "Content-Type": "application/json",
        "x-api-key": os.environ["FLEXPRICE_API_KEY"]
    }

    data = {
        "event_name": "model.usage",
        "external_customer_id": "cust_123",
        "properties": {"credits": 2},
        "source": "api"
    }

    response = requests.post(url, headers=headers, json=data)

    if response.status_code == 202:
        result = response.json()
        print("Event sent:", result)
    else:
        print("Error:", response.text)
```

### Go

```go theme={null}
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
    "os"
)

type Event struct {
    EventName           string                 `json:"event_name"`
    ExternalCustomerID  string                 `json:"external_customer_id"`
    Properties          map[string]interface{} `json:"properties,omitempty"`
    Source              string                 `json:"source,omitempty"`
}

func sendEvent() error {
    event := Event{
        EventName:          "model.usage",
        ExternalCustomerID: "cust_123",
        Properties: map[string]interface{}{
            "credits": 2,
        },
        Source: "api",
    }

    jsonData, err := json.Marshal(event)
    if err != nil {
        return err
    }

    req, err := http.NewRequest("POST", "https://api.cloud.flexprice.io/v1/events", bytes.NewBuffer(jsonData))
    if err != nil {
        return err
    }

    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("x-api-key", os.Getenv("FLEXPRICE_API_KEY"))

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return err
    }
    defer resp.Body.Close()

    if resp.StatusCode == 202 {
        fmt.Println("Event sent successfully")
    } else {
        fmt.Printf("Error: HTTP %d\n", resp.StatusCode)
    }

    return nil
}
```

### PHP

```php theme={null}
<?php

function sendEvent() {
    $url = 'https://api.cloud.flexprice.io/v1/events';
    $apiKey = $_ENV['FLEXPRICE_API_KEY'];

    $data = [
        'event_name' => 'model.usage',
        'external_customer_id' => 'cust_123',
        'properties' => [
            'credits' => 2
        ],
        'source' => 'api'
    ];

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Content-Type: application/json',
        'x-api-key: ' . $apiKey
    ]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($httpCode == 202) {
        echo "Event sent successfully\n";
    } else {
        echo "Error: HTTP $httpCode\n";
        echo "Response: $response\n";
    }
}
```

## Error Responses

### HTTP Status Codes

| Status                  | Description                                |
| ----------------------- | ------------------------------------------ |
| `202 Accepted`          | Event accepted for processing              |
| `400 Bad Request`       | Invalid payload or missing required fields |
| `401 Unauthorized`      | Invalid or missing API key                 |
| `429 Too Many Requests` | Rate limit exceeded                        |

### Error Response Format

```json theme={null}
{
  "error": "Error description",
  "details": "Additional error details"
}
```

### Common Error Messages

#### Missing Required Field

```json theme={null}
{
  "error": "Missing required field: event_name"
}
```

#### Invalid Event Name

```json theme={null}
{
  "error": "Event name 'unknown.event' not found"
}
```

#### Invalid Customer

```json theme={null}
{
  "error": "Customer with external_customer_id 'invalid_id' not found"
}
```

#### Invalid JSON

```json theme={null}
{
  "error": "Invalid JSON format"
}
```

#### Rate Limit Exceeded

```json theme={null}
{
  "error": "Rate limit exceeded. Try again later."
}
```

## Rate Limits

* **Single events**: 1000 requests per minute
* **Bulk events**: 100 requests per minute (up to 1000 events per request)
* **Response**: 429 status code when exceeded

### Rate Limit Headers

When approaching rate limits, responses include headers:

```
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 50
X-RateLimit-Reset: 1640995200
```

## Best Practices

### 1. Error Handling

Implement proper error handling with retries:

```javascript theme={null}
async function sendEventWithRetry(eventData, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch("https://api.cloud.flexprice.io/v1/events", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "x-api-key": process.env.FLEXPRICE_API_KEY,
        },
        body: JSON.stringify(eventData),
      });

      if (response.status === 202) {
        return await response.json();
      }

      if (response.status === 429) {
        // Rate limited - wait and retry
        const retryAfter =
          response.headers.get("Retry-After") || attempt * 1000;
        await new Promise((resolve) => setTimeout(resolve, retryAfter));
        continue;
      }

      throw new Error(`HTTP ${response.status}: ${await response.text()}`);
    } catch (error) {
      if (attempt === maxRetries) {
        throw error;
      }
      // Wait before retry
      await new Promise((resolve) => setTimeout(resolve, 1000 * attempt));
    }
  }
}
```

### 2. Bulk Events for High Volume

Use bulk events when sending many events:

```javascript theme={null}
const events = [
  { event_name: "api.calls", external_customer_id: "cust_1" },
  { event_name: "api.calls", external_customer_id: "cust_2" },
  // ... more events
];

const response = await fetch("https://api.cloud.flexprice.io/v1/events/bulk", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "x-api-key": process.env.FLEXPRICE_API_KEY,
  },
  body: JSON.stringify({ events }),
});
```

### 3. Idempotency

Use event\_id for idempotency:

```json theme={null}
{
  "event_name": "model.usage",
  "external_customer_id": "cust_123",
  "properties": { "credits": 2 },
  "event_id": "unique_event_id_123"
}
```

### 4. Timestamps

Include timestamps for historical events:

```json theme={null}
{
  "event_name": "model.usage",
  "external_customer_id": "cust_123",
  "properties": { "credits": 2 },
  "timestamp": "2025-08-22T07:05:49.441Z"
}
```

## Validation

### Event Name Validation

* Must match a feature's Event Name exactly
* Case-sensitive
* Cannot be changed after feature creation

### Customer Validation

* external\_customer\_id must exist in Flexprice
* Customer must be active

### Property Validation

* Aggregation field must exist for Sum, Max, Latest, Unique Count
* Data types must match aggregation function requirements

## Testing

### Test Endpoint

For testing purposes, you can use the same endpoints with test data:

```bash theme={null}
curl --request POST \
  --url https://api.cloud.flexprice.io/v1/events \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <your_api_key>' \
  --data '{
    "event_name": "test.feature",
    "external_customer_id": "test_customer",
    "properties": {
      "value": 1
    }
  }'
```

### Validation Checklist

Before sending production events:

* Feature exists and is active
* Event Name matches exactly
* Customer exists in Flexprice
* Required properties are included
* Data types are correct
* API key has proper permissions

## Support

For API-related issues:

1. Check error messages for specific details
2. Verify your request format matches the examples
3. Test with a simple event first
4. Contact support with specific error details and examples
