---
title: "Ingest usage events"
method: POST
path: "/v1/events/ingest"
tags: ["Events"]
---

# Ingest usage events

`POST /v1/events/ingest`

## Request

- Base URL: `https://api.creem.io`
- URL: `https://api.creem.io/v1/events/ingest`
- Auth: API key in header `x-api-key`
- TypeScript: <https://skmtc.dev/creem/apis/creem-api/docs/v1/events/ingest/post.ts> (a function with typed inputs; its response is checked against the schema)

Send up to 100 usage events in one request.

Returns **202 Accepted** with the number of events accepted and the `event_id` of each, in submission order — your own value where you supplied one, a generated value where you did not.

Ingestion is **idempotent on (store, event_id)**: re-sending an event_id this store has already accepted records nothing and is not an error, so **retrying a whole batch after a timeout or a partial failure is always safe**. Duplicates are deduplicated silently rather than reported — a replayed batch returns the same 202 and the same `event_ids` as the original. Supply your own `event_id` to get that guarantee; when you omit it we generate one, which makes the event unique and a retry a second event.

Your `event_id` is **stored trimmed**, and the `event_ids` we return are the stored values — so `" abc "` is recorded and echoed as `"abc"`, which is what `reference=` on `GET /v1/events` matches. Two entries in one batch whose ids differ only by surrounding whitespace are therefore the **same** event: the second is deduplicated against the first, both are reported accepted, and both carry the same id. An `event_id` that is entirely whitespace is rejected like an empty one (422) rather than being replaced with a generated id — an unusable key is worth telling you about.

The **whole batch is validated before anything is accepted**: if any event is invalid the request is rejected with 422 and `param` pointing at the offending event (e.g. `events[3].customer_id`), and no event in the batch is ingested.

Billing attribution uses the `timestamp` you supply (defaulting to the time of ingestion), bounded by the late-event window: once a billing period has been closed for longer than that window, an event arriving for it is still stored durably but is no longer folded into that period's totals.

**202, not 200, is deliberate.** It means the batch has been accepted for processing, not that every downstream effect has completed: aggregation into meter totals is already asynchronous today, and the transport behind this endpoint may become queue-backed. A queue producer cannot know how many rows a consumer will ultimately insert, so this response reports acceptance rather than an insert/duplicate split — the contract you code against stays identical when that lands.

## Request body

- IngestUsageEventsApiRequestDto
  - `events` IngestUsageEventApiRequestDto[], required: The batch of usage events to ingest (1–100). The whole batch is validated before anything is written.
    - `name` string, required: The usage event name; must match a meter to be aggregated
    - `customer_id` string: The Creem customer this usage is attributed to. Exactly one of `customer_id` / `external_customer_id` is required per event; the id must belong to an existing customer of your store or the whole batch is rejected. Trimmed; letters, digits, `_` and `-` only; at most 255 characters.
    - `external_customer_id` string: Your own id for this customer, resolved to the Creem customer it was registered for via the customers API. Exactly one of `customer_id` / `external_customer_id` is required per event; an id no customer carries rejects the whole batch. Trimmed; letters, digits, `_` and `-` only; at most 255 characters.
    - `event_id` string: Your idempotency key for this event, unique within your store. Re-sending the same event_id is a no-op. Generated for you when omitted — supply your own if you want retries to be safe.
    - `timestamp` string: When the usage occurred (ISO 8601), on your clock. Defaults to the time of ingestion.
    - `properties` object: The event's attributes — the ONE bag. Meter filter clauses and aggregation properties (e.g. "tokens") resolve their keys here, unprefixed. At most 50 keys, keys at most 40 characters, string and serialized-object values at most 500 characters.

## Response `202`

Batch accepted for processing

- IngestUsageEventsApiResponseDto
  - `accepted` number, required: How many events were accepted. Always the size of the submitted batch — validation is all-or-nothing, so a 202 means every event in the batch was accepted.
  - `event_ids` string[], required: The `event_id` each accepted event was recorded under, in the order they were submitted — your own value where you supplied one, the generated value where you did not. This is the stored id after normalisation (surrounding whitespace is trimmed), so it is exactly what `reference` on GET /v1/events will match. Use it to correlate a submitted event with the row it became.
  - `warnings` IngestUsageEventWarningApiDto[]: Advisory warnings about accepted events that will not produce billable usage as sent — no active meter consumes the event name, only archived meters match, or the timestamp falls in an already-finalized billing period. Warnings never change what was accepted: the events are stored either way. Absent when every event will aggregate normally.
    - `index` number, required: Zero-based index of the event in the submitted batch
    - `code` 'no_matching_meter' | 'meter_archived' | 'timestamp_outside_late_window', required: Warning code. `no_matching_meter`: no meter consumes this event name. `meter_archived`: only archived meters match — unarchive one to resume aggregation. `timestamp_outside_late_window`: the billing period the timestamp falls in is finalized, so the event will never change its totals.
    - `message` string, required: Human-readable explanation

## Other responses

- `400`: Bad Request - Invalid input parameters
- `401`: Unauthorized - Invalid or missing API key
- `404`: Not Found - Resource does not exist
- `422`: An event in the batch is invalid — nothing was ingested; `param` identifies the event and field

## Changes

- **2026-09-15** `071301350724`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/creem/apis/creem-api/changes/v1/events/ingest/post.md)

---

[API](https://skmtc.dev/creem/apis/creem-api.md) - [All operations](https://skmtc.dev/creem/apis/creem-api/llms.txt) - [OpenAPI document](https://skmtc.dev/creem/apis/creem-api/revisions/3f39a8f266c3?raw)
