Events

Optional post-only accounting when pre-estimation is not available

Records an accounting event without a reservation. This endpoint is optional in v0 deployments. The event MUST be applied atomically across all derived scopes before the server returns 201. IDEMPOTENCY (NORMATIVE): - On replay with the same idempotency_key, the server MUST return the original successful response payload. TENANCY (NORMATIVE): - subject.tenant MUST match the effective tenant derived from auth; otherwise the server MUST return 403 FORBIDDEN.

post/v1/events

Request

  • Base URL: https://api.cycles.local
  • URL: https://api.cycles.local/v1/events
  • Auth: API key in header X-Cycles-API-Key

Headers

X-Idempotency-Keystring

Optional idempotency key header. If both header and body idempotency_key are provided, they MUST match. Server MUST enforce idempotency per endpoint by (effective tenant, endpoint, idempotency_key). On replay of an idempotent request that previously succeeded, server MUST return the original successful response outcome and payload (including any server-generated identifiers such as reservation_id), except that remaining_ttl_ms on a replayed createReservation or extendReservation success is a volatile response observation and is not part of the attested CyclesEvidence payload. When emitted, it MUST be recomputed from the original expires_at_ms and the current authoritative server time, never copied from the stored response; it MUST be 0 if the reservation is no longer ACTIVE. All other fields replay verbatim.

Request body

idempotency_keystring required
overage_policy'REJECT' | 'ALLOW_IF_AVAILABLE' | 'ALLOW_WITH_OVERDRAFT'

How server handles commits where actual > reserved. REJECT: reject commit (client must reserve a buffer or over-estimate). Recommended: add 10-20% buffer to estimates when using REJECT. If the action already happened externally, this creates an unaccounted gap.

ALLOW_IF_AVAILABLE (default): commit always succeeds when actual > reserved. If remaining budget supports the full delta, charge it atomically across all derived scopes. Otherwise, cap the delta to available remaining (minimum across all affected scopes, floor 0), charge estimate + capped_delta, and set is_over_limit=true on scopes where the full delta could not be covered. This blocks future reservations until reconciled. Never creates debt. Never rejects a commit — the action already happened. ALLOW_WITH_OVERDRAFT: if remaining budget supports the delta, commit normally. Otherwise, check if (debt + delta) <= overdraft_limit across all affected scopes. If yes: commit succeeds, add delta to debt, remaining can go negative. If no: commit fails with 409 OVERDRAFT_LIMIT_EXCEEDED. This policy ensures the ledger always reflects reality even when budget is exhausted at commit time. CONCURRENCY (NORMATIVE):

  • The overdraft_limit check is performed per-commit and is NOT atomic across concurrent commits to the same scope.
  • Multiple commits may each individually pass the (debt + delta) <= limit check but collectively cause debt to exceed overdraft_limit.
  • This is acceptable: all commits succeed (actions already happened), and the scope enters "over-limit" state, blocking future reservations until reconciled.
  • Servers SHOULD use optimistic locking or compare-and-swap on debt updates to ensure individual commit atomicity, but cross-commit atomicity is not required.
client_time_msinteger

Optional client-observed timestamp (ms). This value is advisory only and MUST NOT be used for authorization, expiry, idempotency or budget enforcement decisions. The server's receipt time governs all budget comparisons and invariants.

metadataobject

Example request

{
  "actual": {
    "unit": "USD_MICROCENTS",
    "amount": 500000
  }
}

Response

Event created and atomically applied to balances

status'APPLIED' required
event_idstring required

Example response

{
  "charged": {
    "unit": "USD_MICROCENTS",
    "amount": 500000
  },
  "balances": [
    {
      "scope": "tenant:acme-corp/workspace:prod",
      "scope_path": "tenant:acme-corp/workspace:prod",
      "remaining": {
        "unit": "USD_MICROCENTS",
        "amount": 4577000
      },
      "reserved": {
        "unit": "USD_MICROCENTS",
        "amount": 500000
      },
      "spent": {
        "unit": "USD_MICROCENTS",
        "amount": 423000
      },
      "allocated": {
        "unit": "USD_MICROCENTS",
        "amount": 5500000
      }
    }
  ]
}

Changes

No recorded changes to this operation across all 1 revision of this API.