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.
TIME-RANGE FILTERS (NORMATIVE, ADDITIVE): Three independent inclusive time-window filters are available, each bound to a specific timestamp field on the reservation entity. All bounds are ISO 8601 date-time strings. All are additive parameters: servers that don't recognize them MUST ignore without error.
-
from / to (revision 2026-05-21) — bound on created_at_ms. The original window filter; matches the family-wide convention on listAuditLogs, listEvents, and listWebhookDeliveries. Always binds to created_at_ms regardless of sort_by.
-
expires_from / expires_to — bound on expires_at_ms. Primary use case: locate reservations that have expired (or will expire) within a window — e.g. cleanup sweepers that need to discover abandoned ACTIVE reservations. The field is required on every ReservationSummary / ReservationDetail, so this filter applies to every row regardless of status.
-
finalized_from / finalized_to — bound on finalized_at_ms. The finalized_at_ms field is OPTIONAL on ReservationSummary / ReservationDetail and is populated ONLY on COMMITTED and RELEASED rows (absent on ACTIVE and EXPIRED). Rows where the field is absent MUST be excluded from results when either finalized_from or finalized_to is supplied — the predicate naturally fails on field-absent rows. Callers who want a window over EXPIRED rows should use expires_from / expires_to against expires_at_ms, which is required on every row.
Validation (applies to all three pairs):
- For each pair, from > to MUST return 400 INVALID_REQUEST (e.g., expires_from > expires_to).
- Either side may be supplied alone (open interval).
- Blank-string values for any window bound MUST be treated as unset (NORMATIVE). A client sending ?expires_from=&finalized_to= MUST be handled identically to one omitting both parameters entirely. This applies to all six bounds (from, to, expires_from, expires_to, finalized_from, finalized_to). Servers MUST NOT 400 on empty-string values despite the format: date-time declaration. Rationale: clients commonly emit unconditional query strings whose values come from possibly-unset variables (e.g., ?from=${maybeUnset}&to=${maybeUnset}), and an unset variable rendering as "" is the common failure mode; rejecting these as malformed surfaces a cryptic 400 that adds nothing over treating them as unset.
- The three pairs combine with AND semantics: a row must satisfy every supplied window predicate to be returned.
Cursor invalidation: sorted-path cursors fold the supplied window bounds into the canonical filter hash. Reusing a sorted cursor under a different (from, to, expires_from, expires_to, finalized_from, finalized_to) tuple returns 400 INVALID_REQUEST. Legacy SCAN cursors do not carry filter state; callers paginating without sort_by must keep all window bounds stable across pages, matching the legacy path's treatment of every other filter.
TENANCY (NORMATIVE):
- Under ApiKeyAuth: 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.
- Under AdminKeyAuth (added 2026-04-13): the admin caller has no effective tenant, so the tenant query parameter is REQUIRED and used as a FILTER (not validation). Omitting it MUST return 400 INVALID_REQUEST with message "tenant query parameter is required when using admin key authentication". This matches the existing dual-auth pattern on listBudgets / listPolicies in the governance-admin spec — same single param, semantics keyed on auth type.
Request
- Base URL: https://api.cycles.local
- URL: https://api.cycles.local/v1/reservations
- Auth: one of:
- API key in header X-Cycles-API-Key
- API key in header X-Admin-API-Key
Query parameters
Lookup handle to recover the reservation_id from a prior createReservation call.
Reservation lifecycle state (v1+ may add additional terminal states).
Filter by reservation status (e.g., ACTIVE).
Inclusive lower bound on reservation creation time. ISO 8601 date-time. When set, the server MUST return only reservations whose created_at_ms is greater than or equal to this timestamp. The filter ALWAYS binds to created_at_ms, independent of sort_by. May be supplied alone (no upper bound) or paired with to. Servers MUST reject from > to with HTTP 400 INVALID_REQUEST. Additive parameter — servers that don't recognize it MUST ignore without error (additive-parameter guarantee). Matches the from / to convention on listAuditLogs, listEvents, and listWebhookDeliveries in the governance spec family.
Inclusive upper bound on reservation creation time. ISO 8601 date-time. When set, the server MUST return only reservations whose created_at_ms is less than or equal to this timestamp. The filter ALWAYS binds to created_at_ms, independent of sort_by. May be supplied alone (no lower bound) or paired with from. Servers MUST reject from > to with HTTP 400 INVALID_REQUEST. Additive parameter — servers that don't recognize it MUST ignore without error.
Inclusive lower bound on reservation expiry time. ISO 8601 date-time. When set, the server MUST return only reservations whose expires_at_ms is greater than or equal to this timestamp. The filter ALWAYS binds to expires_at_ms, independent of sort_by and independent of the from/to window on created_at_ms. May be supplied alone (no upper bound) or paired with expires_to. Servers MUST reject expires_from > expires_to with HTTP 400 INVALID_REQUEST. Use case: cleanup sweepers locating reservations that have expired or will expire within a window. Applies to all rows regardless of status since expires_at_ms is required. Additive parameter — servers that don't recognize it MUST ignore without error.
Inclusive upper bound on reservation expiry time. ISO 8601 date-time. When set, the server MUST return only reservations whose expires_at_ms is less than or equal to this timestamp. Same binding and open-interval rules as expires_from. Servers MUST reject expires_from > expires_to with HTTP 400 INVALID_REQUEST. Additive parameter — servers that don't recognize it MUST ignore without error.
Inclusive lower bound on reservation finalization time. ISO 8601 date-time. When set, the server MUST return only reservations whose finalized_at_ms is greater than or equal to this timestamp. The filter ALWAYS binds to finalized_at_ms, independent of sort_by. May be supplied alone (no upper bound) or paired with finalized_to. Servers MUST reject finalized_from > finalized_to with HTTP 400 INVALID_REQUEST. Behavior on rows without finalized_at_ms (NORMATIVE): the field is OPTIONAL on reservation responses and is populated ONLY on COMMITTED and RELEASED rows (absent on ACTIVE and EXPIRED). Rows where the field is absent MUST be excluded from results when either finalized_from or finalized_to is supplied. Callers who want a window over EXPIRED rows should use expires_from / expires_to against expires_at_ms. Additive parameter — servers that don't recognize it MUST ignore without error.
Inclusive upper bound on reservation finalization time. ISO 8601 date-time. When set, the server MUST return only reservations whose finalized_at_ms is less than or equal to this timestamp. Same binding, open-interval, and ACTIVE-row-exclusion rules as finalized_from. Servers MUST reject finalized_from > finalized_to with HTTP 400 INVALID_REQUEST. Additive parameter — servers that don't recognize it MUST ignore without error.
Sort key. When provided, results are returned in the requested order and the returned cursor encodes the sort key so subsequent pages continue in sort order. When omitted, servers use their default ordering (unchanged pre-revision behavior). The reserved key sorts by the integer amount within each row; the single-unit-per- reservation invariant makes this comparison well-defined. The scope_path key sorts lexicographically over the server-derived canonical scope path string (e.g. "tenant:acme/workspace:prod/agent:x"); the tenant key sorts over the Subject.tenant field. Servers that don't recognize the parameter MUST ignore it without error.
Sort direction for list endpoints. Used with per-endpoint sort_by parameters (introduced in revision 2026-04-16 on listReservations). When sort_by is provided but sort_dir is omitted, servers MUST default to "desc". Servers that do not recognize the parameter MUST ignore it without error (additive-parameter guarantee). This schema is carried inline in each spec of the family (protocol + governance); the companion-specs publication model does not use cross-spec $ref.
Sort direction. Default descending.
FIELD PROJECTION (NORMATIVE, ADDITIVE). Comma-separated list of OPTIONAL heavy fields to project onto each ReservationSummary in the response. By default the list projection omits the arbitrary-size, possibly-PII metadata maps to keep list payloads lean; a caller that needs them (e.g. an aggregate audit / export view) opts in explicitly. Added in revision 2026-06-19. Recognized tokens:
- metadata — populate ReservationSummary.metadata (RESERVE-time metadata) on rows that carry it.
- committed_metadata — populate ReservationSummary.committed_metadata (COMMIT-time metadata) on COMMITTED rows whose commit carried metadata.
- evidence — populate ReservationSummary.evidence (the CyclesEvidence references emitted for this reservation's reserve / commit / release operations) on rows that have recorded evidence. Added in revision 2026-06-22.
Semantics (NORMATIVE):
- Unrecognized tokens MUST be ignored without error (forward/backward compatible — a client MAY request a field a given server version does not know, and an additive-parameter-unaware server simply never populates these maps).
- Tokens are comma-separated; surrounding whitespace and empty tokens (e.g. a trailing comma, or include=) MUST be ignored, never 400.
- PROJECTION-ONLY: include selects which fields are serialized; it does NOT affect which rows match, their ordering, or pagination. It therefore MUST NOT participate in cursor / canonical-filter-hash binding — a cursor minted under one include value remains valid when the next page is fetched under a different (or absent) include, and changing include mid-pagination MUST NOT return 400. (Contrast the window filters above, which DO bind the cursor.)
- committed is NOT gated by include; it is always projected (see ReservationSummary.committed).
Maximum number of results to return
Opaque cursor from previous response
Response
Reservations list
Example response
{
"reservations": [
{
"reserved": {
"unit": "USD_MICROCENTS",
"amount": 500000
},
"committed": {
"unit": "USD_MICROCENTS",
"amount": 500000
}
}
]
}Changes
No recorded changes to this operation across all 1 revision of this API.