Skip to main content

Events

An event is one unit of usage. The minimal shape:
  • Idempotency is enforced, not advisory. A repeated eventIdempotencyKey returns 409 Conflict. Generate one unique key per logical event (a UUID is fine) and you can retry sends safely.
  • Customers resolve by your ID. customerReferenceId is the same ID you used when creating the customer.
  • Cost fields are optional but are the margin story. Send costAmount at the top level. Put model-aware fields inside costInput: model, modelProvider, inputTokens, and outputTokens. The older costInput.costUnits field is deprecated. Stamp what the usage cost you, and per-customer margin falls out of the same ledger that bills.
  • Revenue is computed at ingestion from the plan’s pricing rule and stored on the event, not reconstructed at invoice time.
  • Credit-backed features return a deduction receipt. When the feature is backed by a credit model, the ingestion response includes creditsDeducted, weightApplied, weightId, weightMatch, and remainingBalance — see Credit weights. The fields are absent otherwise.
For AI usage, the cost portion of an event looks like this:

Entitlements

The response is a direct allow/deny with context:
When denied, meta.reason says why (no subscription, entitlement revoked, limit reached). Checks fail closed: no subscription, unpaid first invoice, or missing configuration all mean allowed: false. The intended pattern in your request path:

Quoting the credit cost of a proposed action

POST /entitlements (evaluate) accepts usage.model and returns a creditQuote — the weight that would apply and the credits the proposed usage would burn. The model string must exactly match the costInput.model you’ll send on the event. The quote resolves at request time; the charge resolves at the event’s occurredAt — a quote, not a promise. Details in Credit weights.

Correlating a check to the event it authorized

POST /entitlements (the evaluate variant, for simulating proposed usage) accepts an optional context.flowId. Pass one, or omit it and Tanso generates one and returns it in the response:
Reuse that same value on the POST /events call that reports the resulting usage, and the event carries it too. flowId is a plain, indexed string column on events — not a foreign key, just a shared value you control (or let Tanso mint) so you can later query “what usage came out of this check.” It’s optional, and there’s no automatic join between a check and an event without it. The plain GET /entitlements/{customerReferenceId}/{featureKey} check doesn’t take a flowId — only the evaluate/simulate endpoint does.

Credit hard limits

Credit deduction and depletion detection happen synchronously during event ingestion. When linked hard-limit pools cannot cover the event, the Client API returns 409 Conflict with success: false and a Credit pool depleted error. The event is not stored and no credits are deducted. An entitlement check before serving is still the preferred request pattern. The ingestion rejection protects against stale checks and direct event sends. See Credits for the deduction model.