Skip to content

Ingest Usage Event

Submits a raw usage event to a published extractor rule set. The ingest API accepts the event, writes it to the usage queue, and returns a raw event ID. Normalization, charge item expansion, rating, wallet debit, and analytics projection run asynchronously after the response.

POST /v1/projects/:project_id/extractor-rule-sets/:id/events/ingest
Terminal window
Authorization: Bearer <api-key>
Content-Type: application/json

Requires usage ingest access. Use this endpoint only from a trusted backend, gateway, or server-side SDK.

Field Required Description
project_id Yes Project that owns the ingest stream and usage analytics boundary.
id Yes Published extractor rule set ID used to normalize this event.
{
"source": "openai-gateway",
"external_event_id": "chatcmpl_123",
"idempotency_key": "openai-gateway:chatcmpl_123",
"subject_type": "organization",
"subject_id": "org_001",
"occurred_at": 1781222400,
"raw_json": {
"type": "response",
"provider": "openai",
"usage": {
"model": "gpt-4.1-mini",
"prompt_tokens": 1200,
"output_tokens": 300
},
"meta": {
"org_id": "org_001",
"user_id": "user_123",
"api_key_id": "key_001"
}
},
"x-billing": {
"items": [
{
"metric": "tool_call.web_search",
"quantity": 1,
"unit": "call"
}
],
"pricing_hints": {
"tool_call.web_search": {
"unit_price": 0.1,
"pricing_unit": 1,
"currency": "USD"
}
}
},
"x-usage-control": {
"budget_lease_id": "ble_..."
},
"metadata": {
"gateway_region": "us-east-1"
}
}
Field Required Description
source Yes Stable event source name, for example openai-gateway, agent-runtime, or workflow-engine.
external_event_id No Upstream event or request ID. Useful for support and reconciliation.
idempotency_key No Stable retry key. Reusing the same key with the same tenant_id, project_id, subject_type, subject_id, and occurred_at produces the same raw_event_id and prevents duplicate raw event facts during processing.
subject_type Conditionally Explicit subject type for this event. Must match [a-z][a-z0-9_]{0,63} and be provided together with subject_id.
subject_id Conditionally Explicit subject ID for this event. Must be provided together with subject_type.
occurred_at Yes Event occurrence time as Unix seconds. Required for partition routing and idempotency.
raw_json Yes Original usage JSON. The extractor rule set decides which fields become subject, dimensions, and charge items.
x-billing No Dynamic billing extension at the same request level as metadata. Use it for sparse request-level charge items and pricing or discount hints.
x-usage-control No Usage-control extension. Set budget_lease_id for a one-time agent-run budget. Billing snapshots it for audit and advances that Lease only after rating.
metadata No Ingest-side metadata for audit or operations. Keep this small and non-sensitive.

The quota admission is performed for every ingest attempt. If the HTTP response is lost after the event was accepted and you retry with the same idempotency_key, the raw event is deduplicated downstream, but the retry still counts as a new ingest attempt for the realtime quota. Do not use retries to probe quota availability.

Terminal window
curl 'http://127.0.0.1:8080/v1/projects/proj_gateway/extractor-rule-sets/ers_openai/events/ingest' \
-X POST \
-H 'Authorization: Bearer <api-key>' \
-H 'Content-Type: application/json' \
-d '{
"source": "openai-gateway",
"external_event_id": "chatcmpl_123",
"idempotency_key": "openai-gateway:chatcmpl_123",
"subject_type": "organization",
"subject_id": "org_001",
"occurred_at": 1781222400,
"raw_json": {
"type": "response",
"provider": "openai",
"usage": {
"model": "gpt-4.1-mini",
"prompt_tokens": 1200,
"output_tokens": 300
},
"meta": {
"org_id": "org_001",
"user_id": "user_123"
}
}
}'

The API returns HTTP 202 Accepted when the event is accepted for asynchronous processing.

{
"raw_event_id": "raw_01hxy7f9q8m2",
"status": "received"
}

Use top-level x-billing.items for sparse, request-level charge items that are only known at runtime, such as optional tool calls or add-ons. pricing_hints and discount_hints are keyed by metric.

When the same metric appears in both extractor-generated charge items and x-billing.items, the reported x-billing item is treated as the source of truth for that metric.

When both top-level subject_type and subject_id are present, Meterry uses that pair as the event subject and does not execute the extractor’s subject paths. Otherwise, it resolves each subject field from type_path or id_path, then falls back to default_type or default_id. Supplying only one top-level subject field is invalid.

Before enqueueing, omitted subjects are resolved using the published extractor. A complete, valid subject_type and subject_id pair is copied into the queued request. If extraction cannot resolve the pair, the fields remain empty and enqueueing continues normally. The queue partition key combines tenant, project, subject type, and subject ID; unresolved subjects use the empty subject key within that tenant/project.

  • The extractor rule set must belong to the same tenant and project, and it must be published.
  • Use a stable idempotency_key for retryable gateway requests.
  • Use List Usage Event Details with raw_event_id to inspect the processed usage event and charge items.

Malformed requests return 400, invalid credentials return 401, insufficient access returns 403, and unavailable authentication dependencies return 503. See HTTP entry errors for the common response format.

Request rate limiting returns 429:

{"error":"rate_limit_exceeded","scope":"project","operation":"ingest_request"}

error is a string; scope is tenant or project; operation identifies this endpoint’s rate-limit operation.

Oversized bodies return 413. A rejection based on the known Content-Length also includes max_body_bytes (an integer in bytes). An overflow detected while reading a streamed body returns only error. The size below is an example:

{"error":"request_body_too_large","max_body_bytes":1048576}