---
title: "Preview usage events"
method: POST
path: "/v1/events/preview"
tags: ["Events"]
---

# Preview usage events

`POST /v1/events/preview`

## Request

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

Dry-run of `POST /v1/events/ingest`: send the exact batch you would ingest and get a per-event report of what would happen — **nothing is stored**.

For each event: whether it passes validation (invalid events are reported in place instead of rejecting the batch, unlike ingest), the `event_id` it would be recorded under, whether it would deduplicate against an already-stored event or an earlier entry of the same batch (advisory — another writer can land between preview and ingest), the active meters that would consume it, and the same warnings ingest returns.

Usage events are immutable once ingested, so preview is the way to gain first-run confidence: wire your integration against preview, check `valid` is true and every event matches the meters you expect, then switch the path to `ingest`.

## 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 `200`

Per-event report; nothing was stored

- PreviewUsageEventsApiResponseDto
  - `valid` boolean, required: Whether the batch as submitted would be accepted by POST /v1/events/ingest (ingest validation is all-or-nothing)
  - `events` PreviewUsageEventReportApiDto[], required: Per-event report, in submission order
    - `valid` boolean, required: Whether this event would pass ingest validation
    - `event_id` string, nullable: The idempotency key the event would be recorded under — your `event_id`, trimmed. Null when the event is invalid, and also null when you omitted `event_id`: ingest generates a fresh id at accept time, so previewing one would be misleading.
    - `customer_id` string, nullable: The Creem customer the usage would be attributed to — resolved from your `external_customer_id` where you sent one. Null when the event is invalid or the reference does not resolve.
    - `error` PreviewUsageEventErrorApiDto
      - `code` string, required: Validation error code
      - `message` string, required: What is wrong with the event
      - `param` string, nullable: The offending field, named as you sent it
    - `duplicate` boolean, nullable: Advisory: would this event deduplicate against an already-stored event (or an earlier entry of this batch)? Null when the event is invalid. Racy by nature — a signal for debugging retries, not a guarantee.
    - `matched_meters` string[], required: Ids of the active meters that would consume this event, at preview time
    - `warnings` PreviewUsageEventWarningApiDto[], required: Advisory warnings — same semantics as ingest warnings
      - `code` 'no_matching_meter' | 'meter_archived' | 'timestamp_outside_late_window', required: Warning code — same set as ingest warnings
      - `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`: The request envelope itself is malformed (e.g. no events array, or more than the batch maximum)

## Changes

- **2026-09-16** `ab9722a7eb0c`: 4 breaking
  - the `events/items/customer_id` response's property type changed from `object` to `string` for status `200`
  - the `events/items/duplicate` response's property type changed from `object` to `boolean` for status `200`
  - the `events/items/error/allOf[#/components/schemas/PreviewUsageEventErrorApiDto]/param` response's property type changed from `object` to `string` for status `200`
  - the `events/items/event_id` response's property type changed from `object` to `string` for status `200`
- **2026-09-15** `071301350724`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/creem/apis/creem-api/changes/v1/events/preview/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)
