# Usage events

> Report what customers consume. Events are matched to meters by name and aggregated at period end.

Source: https://docs.borga.is/api/usage-events

Usage events live at `/v1/events`. They are the raw input to [meters](/api/meters), and are unrelated to webhook events, which are delivered to you rather than read from the API.

**The usage event object**

- `id` (string): Prefixed `ue_`.
- `object` (string): `usage_event`
- `event_name` (string): <Param name="customer" type="string | null">Resolved customer, from `customer` or `external_customer_id`.
- `external_customer_id` (string | null): - `timestamp` (timestamp) - `metadata` (object) - `idempotency_key` (string | null) - `created_at` (timestamp) ## Ingest one event `POST /v1/events` **Body** <Param name="event_name" type="string" required>Matches a meter's `event_name`. Up to 200 characters.
- `customer` (string): The `cus_` id. Provide this or `external_customer_id`.
- `external_customer_id` (string): Your id, matched to `Customer.external_id`. Unmatched events are stored and linked when the customer is created.
- `timestamp` (timestamp): Defaults to now. Rejected if more than 60 seconds in the future.
- `idempotency_key` (string): Dedup key, up to 200 characters. Repeats are dropped and counted in `duplicates`.
- `metadata` (object): Values a meter's `aggregate_property` and `filter` refer to.

```ts Node.js
// Single event
await borga.usageEvents.create({
  event_name: "api_request",
  customer: "cus_8Jk2Lm4Np6Qr8St0Uv2Wx4Yz",
  idempotency_key: "req_01J9X2K7M3", // dedupes retries
  metadata: { endpoint: "/v1/chat" },
});

// Batches of up to 1000, identified by your own customer ids
await borga.usageEvents.createBatch([
  {
    event_name: "llm_completion",
    external_customer_id: "user_12345",
    timestamp: "2026-09-07T12:00:00Z",
    idempotency_key: "cmpl_a1",
    metadata: { total_tokens: 640 },
  },
  {
    event_name: "llm_completion",
    external_customer_id: "user_12345",
    timestamp: "2026-09-07T12:00:02Z",
    idempotency_key: "cmpl_a2",
    metadata: { total_tokens: 1024 },
  },
]);
```
```bash curl
curl https://api.borga.is/v1/events \
  -H "Authorization: Bearer sk_test_…" \
  -H "Content-Type: application/json" \
  -d '{
    "event_name": "api_request",
    "customer": "cus_8Jk2Lm4Np6Qr8St0Uv2Wx4Yz",
    "idempotency_key": "req_01J9X2K7M3",
    "metadata": { "endpoint": "/v1/chat" }
  }'
```

```json title="Response"
{ "inserted": 1, "duplicates": 0 }
```

Errors: [`invalid_timestamp`](/errors/invalid_timestamp), [`future_timestamp`](/errors/future_timestamp), [`resource_not_found`](/errors/resource_not_found) for `customer`.

## Ingest a batch

`POST /v1/events/batch`

**Body**

- `events` (array, required): 1 to 1000 event objects with the fields above.

```bash
curl https://api.borga.is/v1/events/batch \
  -H "Authorization: Bearer sk_test_…" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [
      { "event_name": "api_request", "external_customer_id": "user_12345", "idempotency_key": "req_1" },
      { "event_name": "api_request", "external_customer_id": "user_12345", "idempotency_key": "req_2" }
    ]
  }'
```

The whole batch is accepted or rejected together; the response counts `inserted` and `duplicates`.

## List usage events

`GET /v1/events`

Newest first by `timestamp`.

**Query parameters**

- `customer` (string): - `event_name` (string) <Param name="from" type="timestamp">Inclusive lower bound on `timestamp`.
- `to` (timestamp): Exclusive upper bound on `timestamp`.
- `limit` (integer)
- `starting_after` (string)
