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):
- Under ApiKeyAuth: if the reservation exists but is owned by a different effective tenant, the server MUST return 403 FORBIDDEN.
- Under AdminKeyAuth (added 2026-04-13): admin operators can release any reservation regardless of owning tenant — the ops use case is "force-expire a hung reservation" during incident response. reservation_id pins the owner so no extra parameter is needed.
AUDIT (NORMATIVE, AdminKeyAuth path):
- The audit-log entry for an admin-driven release MUST record actor_type=admin_on_behalf_of (existing audit field, value already used on createBudget / createPolicy / updatePolicy in the governance-admin spec). This lets security review distinguish admin-driven releases from tenant self-service without joining to the keys table.
- The entry MUST be discoverable via the governance plane's audit-query surface (GET /v1/admin/audit/logs in the governance-admin spec). Writing the entry to a store the governance audit-query endpoint cannot read from does not satisfy this requirement — the operational intent is that admin-driven release actions surface in the same admin- facing audit view that shows governance-plane audit entries. How servers achieve this is an implementation concern and out of scope for the spec.
- Callers SHOULD populate the optional reason body field with a structured tag (e.g. "[INCIDENT_FORCE_RELEASE]") for grep-ability in the audit log.
Request
- Base URL: https://api.cycles.local
- URL: https://api.cycles.local/v1/reservations/{reservation_id}/release
- Auth: one of:
- API key in header X-Cycles-API-Key
- API key in header X-Admin-API-Key
Path parameters
Headers
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
Response
Release succeeded
Example response
{
"released": {
"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.