PURPOSE (v0):
- Provide a minimal, language-agnostic protocol to enforce deterministic spend exposure for agent runtimes
via concurrency-safe reservations and idempotent commits. - Include optional integration endpoints: /decide (soft landing) and /balances (operator visibility).
NON-GOALS (v0) (NORMATIVE):
- Budget establishment and funding operations are out of scope for v0.
v0 defines the reservation/commit/release enforcement plane and balance reporting only. - v0 provides no API for budget CRUD (create/update/delete), allocation setting, credit/deposit, or debit/withdrawal.
Implementations MAY provide these via an operator/admin plane or a separate API; future versions may standardize them. - A reservation lifecycle is denominated in exactly one unit (single-unit reserve/commit/release).
Multi-unit atomic reservation/settlement is a v1+ concern.
AUTH & TENANCY (NORMATIVE):
- Requests are authenticated via X-Cycles-API-Key.
- Server determines an "effective tenant" from the API key (or other auth context).
- Subject.tenant is a budgeting dimension and MUST be validated against the effective tenant.
If mismatched, server MUST return 403 FORBIDDEN. - Reservation ownership MUST be enforced: every reservation is bound to the effective tenant at creation.
Any subsequent GET/commit/release for a reservation that exists but is owned by a different tenant
MUST return 403 FORBIDDEN. - Balance visibility MUST be tenant-scoped: the server MUST only return balances within the effective tenant.
If a request attempts to query another tenant (e.g., tenant filter mismatches), server MUST return 403 FORBIDDEN.
EVOLUTION CONTRACT:
- This API starts at v0.1.0 with /v1 paths to avoid future client churn.
- v1+ evolution MUST be backward-compatible by default: new fields are additive, existing field meanings MUST NOT change.
- Breaking changes (e.g., new required fields, semantic changes) require a new major API path (e.g., /v2).
CORE INVARIANTS:
- Reserve is atomic across all derived scopes.
- Commit and release are idempotent.
- No double-charge on retries (idempotency key enforced).
ERROR SEMANTICS (NORMATIVE):
- Budget denials MUST return HTTP 409 with error=BUDGET_EXCEEDED.
- Overdraft limit exceeded MUST return HTTP 409 with error=OVERDRAFT_LIMIT_EXCEEDED in two cases:
- During commit: when overage_policy=ALLOW_WITH_OVERDRAFT and (current_debt + delta) > overdraft_limit at commit time
- During reservation: when the scope is in over-limit state (debt > overdraft_limit due to prior concurrent commits)
- Outstanding debt blocking reservation MUST return HTTP 409 with error=DEBT_OUTSTANDING
(when debt > 0 and new reservation is attempted). - Finalized reservations MUST return HTTP 409 with error=RESERVATION_FINALIZED.
- Expired reservations MUST return HTTP 410 with error=RESERVATION_EXPIRED.
(commit/release: beyond expires_at_ms + grace_period_ms; extend: beyond expires_at_ms). - Reservations that never existed MUST return HTTP 404 with error=NOT_FOUND.
- HTTP 429 is reserved for server-side throttling/rate limiting (optional in v0), not deterministic budget exhaustion.
- Unit mismatch on commit (actual.unit ≠ reservation unit) or event (actual.unit not supported for the target scope) MUST return HTTP 400 with error=UNIT_MISMATCH.
- For expiry comparisons, “now” refers to server time (not client-provided time).
- When is_over_limit=true, server MUST return 409 OVERDRAFT_LIMIT_EXCEEDED for new reservations.
This takes precedence over DEBT_OUTSTANDING even when debt > 0.
OVERDRAFT RECONCILIATION (NORMATIVE):
- When concurrent commits cause debt > overdraft_limit on a scope, the server MUST mark that scope as "over-limit" (is_over_limit=true).
- Over-limit scopes MUST reject ALL new reservation attempts with 409 OVERDRAFT_LIMIT_EXCEEDED until debt is reduced below overdraft_limit.
- Operators reconcile over-limit scopes via budget funding operations (out-of-scope for this API).
When debt is repaid below overdraft_limit, is_over_limit automatically returns to false. - Servers SHOULD provide monitoring/alerting when scopes enter over-limit state:
- Log events with scope identifier, current debt, and overdraft_limit
- Optionally emit webhooks or notifications to operators
- Optionally expose metrics endpoint showing over-limit scope count
- Clients SHOULD handle 409 OVERDRAFT_LIMIT_EXCEEDED on reservation as a signal to wait/retry with exponential backoff, or escalate to operators.
IDEMPOTENCY (NORMATIVE):
- If X-Idempotency-Key header is present and body.idempotency_key is present, they MUST match.
- Server MUST enforce idempotency per (effective tenant, endpoint, idempotency_key).
- On replay of an idempotent request that previously succeeded, server MUST return the original successful
response payload (including any server-generated identifiers such as reservation_id). - If the same key is reused with a different request payload, server MUST return 409 IDEMPOTENCY_MISMATCH.
- Servers SHOULD compare idempotency payloads using a canonical JSON representation
(e.g., RFC 8785 JSON Canonicalization Scheme) or an equivalent stable serialization.
SCOPE DERIVATION (NORMATIVE):
- Server derives canonical scope identifiers and a canonical scope_path from Subject fields.
- Canonical ordering is: tenant → workspace → app → workflow → agent → toolset.
- Only explicitly provided subject levels are included in scope paths; intermediate gaps are skipped (not filled with "default").
- Scopes without budgets are skipped during enforcement; at least one derived scope MUST have a budget.
- affected_scopes returned by the server MUST be in that canonical order.
RESERVATION LEASING (GUIDANCE):
- To mitigate "zombie reservations" (client crash after reserve), SDKs SHOULD:
- keep ttl_ms short (typically 10s–30s),
- include modest estimation buffers when using overage_policy=REJECT,
- reserve in small initial leases and increase gradually ("slow start") for long or bursty operations,
- prefer chunked reserve/commit cycles for long-running actions rather than a single large reservation.
OVERDRAFT MONITORING (GUIDANCE):
- Implementations SHOULD provide visibility into over-limit states:
- Dashboard showing scopes with is_over_limit=true
- Alerts when debt exceeds overdraft_limit
- Time-series metrics: debt_utilization = debt / overdraft_limit
- Recommended alerting thresholds:
- Warning at 80% of overdraft_limit
- Critical at 100% (over-limit state)
- Recommended operator runbook:
- Investigate which reservations caused the over-limit state
- Determine if overdraft_limit should be increased (normal variance) or if this represents anomalous consumption (incident)
- Fund the scope to repay debt below limit
- Monitor that is_over_limit returns to false
- Resume operations automatically
CHANGELOG:
v0.1.23 (2026-02-21):
- Renamed Subject.toolGroup → Subject.toolset across all endpoints, schemas,
and normative text for consistency with scope hierarchy naming conventions.
- Added ALLOW_WITH_OVERDRAFT semantics to EventCreateRequest.overage_policy
(finding: behavior was previously unspecified for events).
- Clarified DecisionResponse.decision description: DENY is a valid live-path
outcome on /decide; removed erroneous dry_run framing copied from
ReservationCreateResponse.
- Added ERROR SEMANTICS precedence rule: when is_over_limit=true, server MUST
return 409 OVERDRAFT_LIMIT_EXCEEDED for new reservations, taking precedence
over DEBT_OUTSTANDING even when debt > 0.
- Added DEBT/OVERDRAFT STATE normative block to /decide: server SHOULD return
decision=DENY under debt or over-limit conditions; MUST NOT return 409.
v0.1.22 (2026-02-18):
- Added overdraft/debt model: ALLOW_WITH_OVERDRAFT overage policy, debt and
overdraft_limit fields on Balance, is_over_limit flag, OVERDRAFT_LIMIT_EXCEEDED
and DEBT_OUTSTANDING error codes.
- Added SignedAmount schema to support negative remaining in overdraft state.
- Added OVERDRAFT RECONCILIATION normative section and OVERDRAFT MONITORING
guidance section including recommended alerting thresholds and operator runbook.
- Added OVER-LIMIT BLOCKING normative block to createReservation.
v0.1.0 → v0.1.21:
- Initial protocol definition: reserve/commit/release lifecycle, idempotency,
scope derivation, /decide, /balances, /events, dry_run shadow mode,
reservation extend/list/get endpoints, soft enforcement via Caps.
License
Apache 2.0Servers
Optional preflight policy decision (no reservation created)
Returns ALLOW / DENY, optionally with Caps for soft landing. This endpoint does not reserve budget. Clients that require concurrency safety MUST use /v1/reservations.
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.
DEBT/OVERDRAFT STATE (NORMATIVE): - If the subject scope has debt > 0 or is_over_limit=true, server SHOULD return decision=DENY with reason_code=DEBT_OUTSTANDING or reason_code=OVERDRAFT_LIMIT_EXCEEDED respectively. Server MUST NOT return 409 for these conditions on /decide.
Idempotency on /decide is for request deduplication only. A replayed ALLOW response reflects budget state at the time of the original call; clients MUST NOT treat a replayed decision as current budget authorization.
Authorizations
Parameters
Header Parameters
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 payload (including any server-generated identifiers such as reservation_id).
1256Request Body
Responses
Decision result
List reservations (optional recovery/debug endpoint)
Lists reservations visible to the effective tenant. This endpoint is OPTIONAL in v0 deployments.
RECOVERY (NORMATIVE):
- If a client loses reservation_id, it MAY recover it by querying with idempotency_key and/or subject filters.
- If idempotency_key is provided, the server SHOULD return at most one matching reservation (uniqueness is expected per (effective tenant, endpoint, idempotency_key)).
- Servers SHOULD support filtering by status=ACTIVE to identify "stuck" reservations.
SUBJECT FILTERS (GUIDANCE):
- Query parameters tenant/workspace/app/workflow/agent/toolset filter on the canonical Subject fields.
- Filtering on Subject.dimensions is out of scope for v0 unless explicitly implemented by the server.
TENANCY (NORMATIVE):
- The server MUST scope results to the effective tenant derived from auth.
- If the tenant query parameter is provided, it is validation-only and MUST match the effective tenant; otherwise the server MUST return 403 FORBIDDEN.
- If tenant is omitted, the effective tenant is used.
Authorizations
Parameters
Query Parameters
Lookup handle to recover the reservation_id from a prior createReservation call.
1256Filter by reservation status (e.g., ACTIVE).
"ACTIVE""COMMITTED""RELEASED""EXPIRED"Maximum number of results to return
120050Opaque cursor from previous response
Responses
Reservations list
Reserve budget for a planned action (concurrency-safe)
Atomically reserves the estimated amount across server-derived scopes and returns a reservation_id. Reservations expire at expires_at_ms; commits are accepted through (expires_at_ms + grace_period_ms).
If dry_run=true, server MUST evaluate the full reservation request and return decision/caps/affected_scopes/balances as if the reservation were live, but MUST NOT modify balances, persist a reservation, or require commit/release.
DRY-RUN RESPONSE RULES (NORMATIVE): - reservation_id and expires_at_ms MUST be absent. - affected_scopes MUST be populated regardless of decision outcome (ALLOW / ALLOW_WITH_CAPS / DENY). - If decision=ALLOW_WITH_CAPS, caps MUST be present; otherwise caps MUST be absent. - If decision=DENY, reason_code SHOULD be populated; it is the primary diagnostic signal for why the dry_run was denied. - balances MAY be populated (recommended for operator visibility), but MUST reflect a non-mutating evaluation.
OVER-LIMIT BLOCKING (NORMATIVE): - If ANY affected scope has debt > overdraft_limit (is_over_limit=true), the reservation MUST be rejected
with 409 OVERDRAFT_LIMIT_EXCEEDED, regardless of available remaining budget.
- This blocks new work when overdraft reconciliation is needed.
IDEMPOTENCY (NORMATIVE): - On replay with the same idempotency_key, the server MUST return the original successful response payload, including the original reservation_id (if any).
TENANCY (NORMATIVE): - subject.tenant MUST match the effective tenant derived from auth; otherwise the server MUST return 403 FORBIDDEN.
Authorizations
Parameters
Header Parameters
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 payload (including any server-generated identifiers such as reservation_id).
1256Request Body
Responses
Reservation decision (ALLOW/DENY with optional caps)
Get reservation details (optional, for debugging)
Retrieve current status and details of a reservation by ID. Useful for debugging and monitoring long-running operations.
TENANCY (NORMATIVE): - If the reservation exists but is owned by a different effective tenant, the server MUST return 403 FORBIDDEN.
Authorizations
Parameters
Path Parameters
1128Responses
Reservation details
Commit actual spend for a reservation (auto-releases delta)
Commits actual spend. If actual < reserved, delta is released automatically. If actual > reserved, behavior is controlled by the reservation's overage_policy.
IDEMPOTENCY (NORMATIVE): - On replay with the same idempotency_key, the server MUST return the original successful response payload.
TENANCY (NORMATIVE): - If the reservation exists but is owned by a different effective tenant, the server MUST return 403 FORBIDDEN.
Authorizations
Parameters
Header Parameters
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 payload (including any server-generated identifiers such as reservation_id).
1256Path Parameters
1128Request Body
Responses
Commit succeeded
Release an unused reservation
Releases reserved amount back to remaining budget.
IDEMPOTENCY (NORMATIVE): - On replay with the same idempotency_key, the server MUST return the original successful response payload.
TENANCY (NORMATIVE): - If the reservation exists but is owned by a different effective tenant, the server MUST return 403 FORBIDDEN.
Authorizations
Parameters
Header Parameters
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 payload (including any server-generated identifiers such as reservation_id).
1256Path Parameters
1128Request Body
Responses
Release succeeded
Extend reservation TTL (lease refresh / heartbeat)
Extends the expiry of an ACTIVE reservation to support long-running agent workflows.
SEMANTICS (NORMATIVE): - Extension updates expires_at_ms only; it MUST NOT change reserved amount, unit, subject, action, scope_path, or affected_scopes. - Extensions MUST be applied in a concurrency-safe way. - Server MUST accept extend only when status is ACTIVE and the reservation has not yet expired
(i.e., server time ≤ expires_at_ms). If the reservation is expired, server MUST return 410 with error=RESERVATION_EXPIRED.
IDEMPOTENCY (NORMATIVE): - On replay with the same idempotency_key, the server MUST return the original successful response payload.
TENANCY (NORMATIVE): - If the reservation exists but is owned by a different effective tenant, the server MUST return 403 FORBIDDEN.
ERROR SEMANTICS (NORMATIVE): - If the reservation is COMMITTED or RELEASED, server MUST return 409 with error=RESERVATION_FINALIZED. - If the reservation is expired (server time > expires_at_ms), server MUST return 410 with error=RESERVATION_EXPIRED. - If the reservation never existed, server MUST return 404 with error=NOT_FOUND.
Authorizations
Parameters
Header Parameters
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 payload (including any server-generated identifiers such as reservation_id).
1256Path Parameters
1128Request Body
Responses
Reservation expiry extended
Query current budget balances across scopes (nice-to-have)
Returns balances for scopes matching the provided subject filter. include_children MAY be ignored by v0 implementations.
SUBJECT FILTER REQUIREMENT (NORMATIVE): - At least one of tenant/workspace/app/workflow/agent/toolset MUST be provided. - If all of these filters are omitted, server MUST return 400 with error=INVALID_REQUEST.
TENANCY (NORMATIVE): - The server MUST scope results to the effective tenant derived from auth. - If the tenant query parameter is provided, it is validation-only and MUST match the effective tenant; otherwise the server MUST return 403 FORBIDDEN. - If tenant is omitted, the effective tenant is used.
Authorizations
Parameters
Query Parameters
falseMaximum number of results to return
120050Opaque cursor from previous response
Responses
Balance response
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.
Authorizations
Parameters
Header Parameters
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 payload (including any server-generated identifiers such as reservation_id).
1256Request Body
Responses
Event created and atomically applied to balances
