Reserve
Claim one or more named resources for a requester.
Endpoint
POST /api/v1/Reservation/reserve X-Api-Key: YOUR_API_KEY Content-Type: application/json
The body is an array of reservation requests. Each is processed independently — use the transactions endpoint if you need atomic all-or-nothing semantics across multiple resources.
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
requester |
string | Yes | Identifier for the entity claiming the resource (e.g. a worker ID, user ID, or service name). |
resource |
string | Yes | Name of the resource to reserve. Any string. Does not need to be pre-registered. |
ttlSeconds |
integer | No | Auto-release after this many seconds. Must be > 0. Omit for no expiry. |
resourceType |
string | No | Optional tag for your own categorisation. Defaults to "UserDefined". Appears in event payloads. |
Example request
[
{
"requester": "worker-1",
"resource": "job-42",
"ttlSeconds": 60
}
]
Response fields
| Field | Type | Description |
|---|---|---|
resource |
string | The resource that was operated on. |
requester |
string | The requester from your request. |
status |
string | Outcome — see statuses below. |
explanation |
string | Human-readable detail about the outcome. |
releaseToken |
string or null | Present on ACQUIRED. Pass this to unreserve. |
expiresAt |
ISO 8601 datetime or null | UTC expiry time when ttlSeconds was set. |
acquiredSlots |
object or null | For capacity resources: maps logical name → physical slot, e.g. {"Printer": "Printer_0"}. |
acquiredBlockers |
array or null | Blocker resources co-acquired with this reservation. |
reservedAtPath |
string or null | Full ancestor path for tree resources, e.g. "Equipment>IT>Printers". |
Example response
[
{
"resource": "job-42",
"requester": "worker-1",
"status": "ACQUIRED",
"explanation": "ACQUIRED",
"releaseToken": "a3f8c2d1-4b5e-...",
"expiresAt": "2026-05-13T12:01:00Z",
"acquiredSlots": null,
"acquiredBlockers": null,
"reservedAtPath": null
}
]
Statuses
| Status | Meaning |
|---|---|
ACQUIRED | Successfully reserved. releaseToken is set. |
ALREADY_HAD | The requester already holds this resource. Idempotent — releaseToken is the existing token. |
ALREADY_HELD | Resource is held by a different requester. Try again later. |
NOT_AVAILABLE | A capacity resource has no free slots. |
NOT_VALID | The resource name is not registered (only applies to resources that require registration). |
Re-reserving a resource you already hold
If requester already holds resource, the call returns
ALREADY_HAD with the existing releaseToken.
This is safe to call without checking first — use it as an idempotent acquire.
Auto-expiry with TTL
Set ttlSeconds to have the reservation auto-release after that duration.
The sweep runs server-side so expiry happens even if your process crashes.
expiresAt in the response gives the UTC timestamp of expiry.
[{"requester":"svc-a","resource":"deploy-lock","ttlSeconds":30}]
Atomic multi-resource transactions
The simple /reserve endpoint processes each resource independently.
If you need to reserve a group of resources atomically — all succeed or none are taken —
use POST /api/v1/Reservation/transactions with a conflict method.
| Method | Behaviour |
|---|---|
ALL_OR_NONE |
Every resource in the transaction must be available. If any one is held, the entire transaction rolls back and nothing is reserved. |
AS_MANY_AS_POSSIBLE |
Reserve as many resources as are available. Skip (not roll back) any that are already held. |
FIRST |
Reserve only the first available resource from the list and stop. |
Transaction request example
POST /api/v1/Reservation/transactions
X-Api-Key: YOUR_API_KEY
Content-Type: application/json
[
{
"method": "ALL_OR_NONE",
"requestType": "RESERVE",
"requests": [
{"requester": "svc-a", "resource": "lock-1"},
{"requester": "svc-a", "resource": "lock-2"}
]
}
]
Check without reserving
Check whether a resource is currently held without attempting to reserve it:
GET /api/v1/Reservation/check/{resource}
X-Api-Key: YOUR_API_KEY
{"resource":"job-42","isReserved":true,"holder":"worker-1"}
Or check multiple resources at once:
POST /api/v1/Reservation/check
X-Api-Key: YOUR_API_KEY
Content-Type: application/json
{"resources":["job-42","job-43","job-44"]}
Error codes
| HTTP status | When |
|---|---|
400 | Empty request body, missing requester or resource, or ttlSeconds ≤ 0. |
401 | Missing or invalid X-Api-Key. |
429 | Rate limit or concurrent reservation limit reached. Check Retry-After header and the upgradeRequired field in the response body. |