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

# Usage Events API — ingest metered usage data

> Ingest usage events for metered billing individually or in batches of up to 1,000. Use idempotency keys to safely retry without double-counting.

Usage events are the raw data that meters aggregate into billable quantities. Send an event every time a metered action occurs — an API call, a file upload, a message sent. Borga matches each event to the appropriate meter by `event_name` and accumulates usage for invoicing.

<Tip>
  Always set `idempotency_key` on every event you ingest. If a network failure causes your request to time out, you can retry the same payload safely — Borga will deduplicate events with the same key and never count them twice.
</Tip>

***

## Ingest a single event

`POST /v1/events`

Ingests one usage event.

### Request parameters

<ParamField body="event_name" type="string" required>
  Name of the event. Must match the `event_name` configured on a meter. Maximum 200 characters.
</ParamField>

<ParamField body="customer" type="string">
  ID of the Borga customer this event belongs to. Either `customer` or `external_customer_id` must be provided.
</ParamField>

<ParamField body="external_customer_id" type="string">
  Your own identifier for the customer. Borga will resolve this to a Borga customer ID. Either `customer` or `external_customer_id` must be provided.
</ParamField>

<ParamField body="idempotency_key" type="string">
  A unique string for this event. If you send two events with the same key, only the first is recorded. Maximum 200 characters. Recommended for all production ingestion.
</ParamField>

<ParamField body="timestamp" type="string">
  ISO 8601 timestamp indicating when the event occurred. Defaults to the current time if omitted. Use this to backfill historical events.
</ParamField>

<ParamField body="metadata" type="object">
  Arbitrary key-value pairs describing the event. For meters with `aggregate_type` other than `count`, the `aggregate_property` value is read from this object.
</ParamField>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://api.borga.is/v1/events \
    --header "Authorization: Bearer sk_live_..." \
    --header "X-Merchant-Id: mer_xxx" \
    --header "Content-Type: application/json" \
    --data '{
      "event_name": "api.request",
      "customer": "cus_abc123",
      "idempotency_key": "req_2026-04-29-00001",
      "timestamp": "2026-04-29T10:15:00Z",
      "metadata": {
        "endpoint": "/v1/payments",
        "method": "POST"
      }
    }'
  ```
</RequestExample>

### Response fields

<ResponseField name="id" type="string" required>
  Unique identifier assigned to this event.
</ResponseField>

<ResponseField name="event_name" type="string" required>
  Name of the ingested event.
</ResponseField>

<ResponseField name="customer" type="string" required>
  ID of the customer this event is associated with.
</ResponseField>

<ResponseField name="idempotency_key" type="string">
  The idempotency key provided with this event.
</ResponseField>

<ResponseField name="timestamp" type="string" required>
  ISO 8601 timestamp of when the event occurred.
</ResponseField>

<ResponseField name="metadata" type="object">
  Key-value pairs attached to the event.
</ResponseField>

<ResponseExample>
  ```json 201 theme={null}
  {
    "id": "evt_01hx9z3k2mfq7nbvd4cw8ej001",
    "event_name": "api.request",
    "customer": "cus_abc123",
    "idempotency_key": "req_2026-04-29-00001",
    "timestamp": "2026-04-29T10:15:00Z",
    "metadata": {
      "endpoint": "/v1/payments",
      "method": "POST"
    }
  }
  ```
</ResponseExample>

***

## Ingest a batch of events

`POST /v1/events/batch`

Ingests up to 1,000 events in a single request. Borga processes each event independently — if one event fails validation, the others are still ingested.

<Note>
  Each batch request can contain a maximum of 1,000 events. If you need to send more, split them across multiple batch requests.
</Note>

### Request parameters

<ParamField body="events" type="object[]" required>
  Array of event objects to ingest. Minimum 1, maximum 1,000 items. Each object accepts the same fields as a single event ingestion request.

  <Expandable title="IngestEventDto fields">
    <ParamField body="event_name" type="string" required>
      Name of the event. Maximum 200 characters.
    </ParamField>

    <ParamField body="customer" type="string">
      ID of the Borga customer this event belongs to.
    </ParamField>

    <ParamField body="external_customer_id" type="string">
      Your own identifier for the customer.
    </ParamField>

    <ParamField body="idempotency_key" type="string">
      Unique key for deduplication. Maximum 200 characters.
    </ParamField>

    <ParamField body="timestamp" type="string">
      ISO 8601 timestamp of when the event occurred.
    </ParamField>

    <ParamField body="metadata" type="object">
      Arbitrary key-value pairs describing the event.
    </ParamField>
  </Expandable>
</ParamField>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://api.borga.is/v1/events/batch \
    --header "Authorization: Bearer sk_live_..." \
    --header "X-Merchant-Id: mer_xxx" \
    --header "Content-Type: application/json" \
    --data '{
      "events": [
        {
          "event_name": "api.request",
          "customer": "cus_abc123",
          "idempotency_key": "req_2026-04-29-00002",
          "timestamp": "2026-04-29T10:16:00Z",
          "metadata": { "endpoint": "/v1/subscriptions", "method": "GET" }
        },
        {
          "event_name": "api.request",
          "customer": "cus_abc123",
          "idempotency_key": "req_2026-04-29-00003",
          "timestamp": "2026-04-29T10:17:00Z",
          "metadata": { "endpoint": "/v1/events", "method": "POST" }
        }
      ]
    }'
  ```
</RequestExample>

### Response fields

<ResponseField name="ingested" type="number" required>
  Number of events successfully ingested.
</ResponseField>

<ResponseField name="errors" type="object[]">
  Array of error objects for any events that failed validation. Each object includes an `index` indicating which event in the request array failed, and a `message` describing the error.
</ResponseField>

<ResponseExample>
  ```json 200 theme={null}
  {
    "ingested": 2,
    "errors": []
  }
  ```
</ResponseExample>

***

## List events

`GET /v1/events`

Returns a paginated list of ingested events, ordered by timestamp descending.

### Query parameters

<ParamField query="customer" type="string">
  Filter events by customer ID.
</ParamField>

<ParamField query="event_name" type="string">
  Filter events by event name.
</ParamField>

<ParamField query="from" type="string">
  Return only events at or after this ISO 8601 timestamp.
</ParamField>

<ParamField query="to" type="string">
  Return only events at or before this ISO 8601 timestamp.
</ParamField>

<ParamField query="starting_after" type="string">
  Cursor for pagination. Pass the `id` of the last event from the previous page to retrieve the next page.
</ParamField>

<ParamField query="limit" type="number">
  Maximum number of events to return per page.
</ParamField>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
    --url "https://api.borga.is/v1/events?customer=cus_abc123&event_name=api.request&from=2026-04-01T00%3A00%3A00Z&limit=50" \
    --header "Authorization: Bearer sk_live_..." \
    --header "X-Merchant-Id: mer_xxx"
  ```
</RequestExample>

### Response fields

<ResponseField name="data" type="object[]" required>
  Array of event objects. Each object has the same fields as the single event response above.
</ResponseField>

<ResponseField name="has_more" type="boolean" required>
  Whether more events exist beyond this page. Pass the last `id` as `starting_after` to retrieve the next page.
</ResponseField>
