Skip to main content

Events API

Query usage events: what the charging log recorded for authorize, charge and release requests. This is not the billing record, which Get Customer Transactions returns.

List Events​

GET /api/v1/events

Query Parameters:

ParameterTypeDescription
limitintItems per page
offsetintItems to skip
customer_idstringFilter to one customer by their id. Use this rather than the account id alone: an account id can be changed and reused by another customer, while the id names one customer for as long as it exists. When no history of the customer's account ids is on file, the read uses their current account id, and answers an empty list if they have none. A value that cannot be an id is a 422
customer_external_idstringFilter by the account id you gave a customer, their external_id. On its own it returns everything charged under that id, including charges made while it belonged to a different customer. Send customer_id as well to filter to one customer
service_codestringFilter by service
statusstringFilter by status — one of the tokens under Event Statuses
session_idstringFilter to one session: returns only the events that make up that session
lock_idstringFilter to one reservation: every event charged against that session identity
interaction_idstringFilter to the legs of one API call. A three-service charge is one interaction and three events, so this is a different axis from session_id, which spans a whole session. Only events recorded by the current charging path carry one. Each repeat of the call under the same idempotency_key adds a copy of every event the call recorded, marked by replay_of
searchstringFree-text match across the customer name and account ID, the service name and code, and the item code. With a customer filter, only the service and item fields are searched
from_datestringStart date (ISO 8601)
to_datestringEnd date (ISO 8601)

Response:

{
"events": [
{
"event_id": "tx_abc123",
"type": "charge",
"customer_external_id": "acme-42",
"customer_name": "Acme Corp",
"service_code": "AI-TOKENS",
"item_code": "GPT-4-TURBO",
"session_id": "cv-review-42",
"lock_id": "0123ABCD 456789EF 0123ABCD 456789EF",
"interaction_id": "ia_9f2c…",
"requested_units": 1500,
"granted_units": null,
"reservation_units": 2000,
"charged_quantity": 1500,
"charged_amount": "0.00450",
"currency": "USD",
"status": "charged",
"succeeded": true,
"partial": false,
"created_at": "2026-03-23T10:30:00Z"
}
],
"total": 15420,
"limit": 20,
"offset": 0,
"has_more": true
}

event_id is the identity of that one event, and it is the same value the Charging API's own reply reports — one name for one value, so a POST /api/v1/charging result can be read back here directly. Money is a decimal string, never a number: null means "no figure", which is not the same as zero.

On a charge, reservation_units is what the reported usage was measured against: everything the billing engine had granted the session, including what it had already written off as used. It is null when there is none to name, and null on every event that is not a charge.

over_grant is true when the session has reported more units than reservation_units covered. Every charge of the session is measured against the reservation, so a session that closes in several charges is flagged from the charge that crosses the reservation onward. It is informational: the charge still billed in full.

All list endpoints return the same pagination envelope: total (rows matching the query), limit/offset (the page you requested), and has_more (whether more rows exist beyond this page).

Get Single Event​

GET /api/v1/events/{event_id}

Returns full event details including metadata and processing timestamps. Pass customer_external_id to refuse the read unless the event was charged under that account id; an event belonging to someone else answers exactly as one that never existed.

An event still awaiting a reply from the billing backend is returned as it stands, with state: "in_flight" and the charge figures null. That means the result is unknown, never that nothing was charged.

Event Statuses​

One event has one status, whichever read you meet it in: the event list, a single event, or the members of a session.

StatusDescription
chargedSuccessfully rated and billed
in_flightSent to the billing backend, which has not answered yet — the result is not known
authorizedResources locked (session in progress) — see the reservation fields below
releasedThe reservation was handed back without a final charge
skippedA repeat of a settle that had already been applied; nothing was charged again
failedProcessing error

Reservation fields on an authorized event​

An authorization reserves units, it does not consume them, and the billing backend may reserve fewer than were asked for when the customer's funds fall short.

FieldMeaning
requested_unitsUnits the caller asked for.
granted_unitsUnits actually reserved. Null for an uncapped grant, and also when nothing was granted: unlimited_grant separates the two.
unlimited_granttrue when an uncapped quantity was granted.
partialtrue when fewer units were reserved than were asked for. A success, not a refusal.

So an authorization that asked for 120 units against a balance covering 50 reports requested_units: 120, granted_units: 50, partial: true. Consume at most granted_units, and extend the reservation to reserve more.

Sessions​

Charges that share a session_id (see the Charging API) can be read back as grouped sessions, each with per-session revenue, cost and margin. See the Events guide for the UI.

List sessions​

GET /api/v1/sessions

Query Parameters: customer_id, customer_external_id, service_code, search, from_date, to_date, limit, offset. The two customer filters work as they do on List Events.

Returns one entry per session with total_charged, total_cost, margin, event_count, lock_count, label, service_codes, the started_at/ended_at window and the counts of events still awaiting a reply (in_flight_count) or failed (failed_count). A copy that a repeated request writes is not counted again in either. A copy carries the repeat's time, so a resend after a session ends moves its ended_at. The list uses the same pagination envelope as List Events. lock_count counts the lock ids the events name rather than live reservations, so a session that reserved nothing still reports a non-zero count.

totals carries the same money split per currency, as decimal strings. It is not a restatement of total_charged: a session that billed in more than one currency reports currency: null and no single total, because amounts in different currencies cannot be added, and totals is where its money is still readable.

Get a single session​

GET /api/v1/sessions/{session_id}

Returns the session summary, its member events, the reservations it opened (locks) and a per-service rollup (services).

Each member event is the same object List Events returns for that event, with the same fields and the same values.

A session is identified by customer and session_id together, because a session_id is yours to choose and may be reused. Pass customer_external_id to say which customer you mean. If you omit it for a shared session_id, the read answers 409 and error.code is session_id_ambiguous. The candidates' account ids are in error.details.customer_external_ids, most recently active first, so you can repeat the request for the one you meant. A session holding more events than one read returns reports its newest ones and sets truncated, whether or not you named the customer.

Each entry in locks is one reservation — its state (open, settled or released), granted_units, consumed_units (the last report on its own), session_reported_units (everything the session has reported across its charges, and the figure over_grant is measured against), expires_at, authorize_count, whether the session rates incrementally, and lapsed (still open past its expiry, computed on every read). A session none of whose events opened a reservation reports an empty list. A standalone charge names a lock id without opening one, and so does a refused authorization.

To fetch only the raw events of a session, use GET /api/v1/events?session_id={session_id}. To reach one reservation when you know only its lock_id, use GET /api/v1/events?lock_id={lock_id}: the events name the session they belong to, and that session's detail carries the reservation.