Skip to main content
Events are the foundation of usage-based billing in FlexPrice. An event represents a single measurable activity from your application (e.g., API request, GB stored, compute minute).

Event Structure

Every event must include these required fields:
FieldTypeRequiredDescription
event_namestringYesIdentifier for the event type (e.g., api_request)
external_customer_idstringYesCustomer ID in your system
timestampISO 8601YesWhen the event occurred (UTC)
propertiesobjectNoCustom event data for filtering and aggregation
event_idstringNoIdempotency key (auto-generated if omitted)
customer_idstringNoFlexPrice customer ID (resolved automatically)
sourcestringNoEvent source identifier

Event Schema

{
  "event_name": "api_request",
  "external_customer_id": "customer_123",
  "timestamp": "2024-03-20T15:04:05Z",
  "event_id": "evt_unique_12345",
  "source": "production-api",
  "properties": {
    "endpoint": "/api/v1/users",
    "method": "GET",
    "response_time_ms": 45,
    "status_code": 200,
    "bytes_transferred": 2048
  }
}

Sending Events

Single Event Ingestion

Use POST /v1/events to send individual events:
curl -X POST https://us.api.flexprice.io/v1/events \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "event_name": "api_request",
    "external_customer_id": "customer_123",
    "timestamp": "2024-03-20T15:04:05Z",
    "properties": {
      "endpoint": "/api/v1/users",
      "method": "GET"
    }
  }'
Response (HTTP 202):
{
  "message": "Event accepted for processing",
  "event_id": "evt_unique_12345"
}
Events are processed asynchronously. A 202 response means the event was accepted and queued, not that processing is complete. Processing typically completes within seconds.

Bulk Event Ingestion

For high-volume scenarios or backfilling historical data, use POST /v1/events/bulk:
curl -X POST https://us.api.flexprice.io/v1/events/bulk \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [
      {
        "event_name": "api_request",
        "external_customer_id": "customer_123",
        "timestamp": "2024-03-20T15:04:05Z"
      },
      {
        "event_name": "api_request",
        "external_customer_id": "customer_456",
        "timestamp": "2024-03-20T15:05:12Z"
      }
    ]
  }'
Limits:
  • Maximum 1,000 events per bulk request
  • For larger datasets, send multiple bulk requests
Response (HTTP 202):
{
  "message": "Events accepted for processing"
}

Event Properties

The properties field contains custom data specific to your event:

Best Practices

1

Use Consistent Property Names

Maintain consistent naming across events of the same type. Use snake_case for property keys.
2

Include Filterable Dimensions

Add properties you’ll use for filtering (region, product tier, feature flag, etc.).
3

Store Aggregatable Values

Include numeric fields you’ll aggregate (bytes, duration, count, etc.).
4

Keep Properties Flat

Nested objects work, but top-level properties are easier to filter and aggregate.

Example Properties by Use Case

{
  "properties": {
    "endpoint": "/api/v1/users",
    "method": "GET",
    "response_time_ms": 45,
    "status_code": 200,
    "bytes_sent": 1024,
    "bytes_received": 2048
  }
}

Idempotency

FlexPrice uses event_id for idempotency to prevent duplicate billing:
{
  "event_id": "unique_key_from_your_system",
  "event_name": "api_request",
  "external_customer_id": "customer_123",
  "timestamp": "2024-03-20T15:04:05Z"
}
  • If you send the same event_id twice, only the first event is processed
  • If omitted, FlexPrice generates a UUID (no deduplication)
  • Use your own IDs (request ID, transaction ID) for guaranteed deduplication

Timestamps

Format

Timestamps must be in ISO 8601 format with timezone:
{
  "timestamp": "2024-03-20T15:04:05Z"
}
Supported formats:
  • 2024-03-20T15:04:05Z (UTC)
  • 2024-03-20T15:04:05.123456789Z (with nanoseconds)
  • 2024-03-20T15:04:05-07:00 (with timezone offset)

Backfilling Historical Events

You can send events with past timestamps for backfilling:
{
  "event_name": "api_request",
  "external_customer_id": "customer_123",
  "timestamp": "2024-01-01T00:00:00Z"
}
Backfilled events are processed normally and will affect usage calculations for their respective time periods. Ensure billing periods haven’t been finalized before backfilling.

Error Handling

Validation Errors (400)

{
  "error": "invalid request payload",
  "hint": "Invalid request payload",
  "details": {
    "field": "external_customer_id",
    "error": "required field missing"
  }
}
Common validation errors:
  • Missing required fields (event_name, external_customer_id, timestamp)
  • Invalid timestamp format
  • Invalid data types in properties

Authentication Errors (401)

{
  "error": "unauthorized",
  "hint": "Invalid or missing API key"
}

Rate Limiting (429)

FlexPrice applies rate limits per tenant:
{
  "error": "rate limit exceeded",
  "hint": "Too many requests. Please try again later."
}
Implement exponential backoff when retrying:
import time
import random

def send_with_retry(event, max_retries=3):
    for attempt in range(max_retries):
        try:
            return client.events.ingest_event(**event)
        except RateLimitError:
            if attempt == max_retries - 1:
                raise
            sleep_time = (2 ** attempt) + random.random()
            time.sleep(sleep_time)

Querying Events

Retrieve raw events for debugging or export:
curl -X POST https://us.api.flexprice.io/v1/events/query \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "external_customer_id": "customer_123",
    "event_name": "api_request",
    "start_time": "2024-03-01T00:00:00Z",
    "end_time": "2024-03-31T23:59:59Z",
    "page_size": 50
  }'
Response:
{
  "events": [
    {
      "id": "evt_abc123",
      "event_name": "api_request",
      "external_customer_id": "customer_123",
      "timestamp": "2024-03-20T15:04:05Z",
      "properties": {
        "endpoint": "/api/v1/users",
        "method": "GET"
      }
    }
  ],
  "has_more": true,
  "total_count": 150
}
See Query Events API for full documentation.

Best Practices

1

Send Events in Real-Time

Send events as they occur for accurate, up-to-date usage tracking. Don’t batch unnecessarily.
2

Use Bulk API for Historical Data

When backfilling or importing data, use /events/bulk for better performance.
3

Implement Retry Logic

Network failures happen. Retry failed requests with exponential backoff.
4

Set Idempotency Keys

Always provide event_id to prevent duplicate billing from retries.
5

Monitor Event Processing

Use the monitoring endpoint to track ingestion lag and event counts.
6

Validate Before Sending

Check for required fields and valid formats in your application before sending.

Next Steps

Configure Meters

Create meters to aggregate events into usage

Aggregation Methods

Learn about aggregation types

API Reference

Full API documentation

Query Usage

Retrieve aggregated usage