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

FieldTypeRequiredDescription
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

FieldTypeDescription
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
  }
]
Save the releaseToken. You need it to call unreserve. If you lose it, you can force-release from the dashboard, or wait for the TTL to expire.

Statuses

StatusMeaning
ACQUIREDSuccessfully reserved. releaseToken is set.
ALREADY_HADThe requester already holds this resource. Idempotent — releaseToken is the existing token.
ALREADY_HELDResource is held by a different requester. Try again later.
NOT_AVAILABLEA capacity resource has no free slots.
NOT_VALIDThe 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.

MethodBehaviour
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 statusWhen
400Empty request body, missing requester or resource, or ttlSeconds ≤ 0.
401Missing or invalid X-Api-Key.
429Rate limit or concurrent reservation limit reached. Check Retry-After header and the upgradeRequired field in the response body.