Skip to content

Reservations

Some work only knows its cost afterwards, like a model call billed by tokens or a job billed by minutes. A reservation holds an estimate while the work runs, then settles at the actual amount.

In TypeScript, the SDK’s hold does all of this for you. See Add Limitry with your agent. The steps below are what it does through the API.

Make a check with mode: "reserve" and your estimate as the cost:

Terminal window
curl -X POST https://api.limitry.com/v1/checks \
-H "Authorization: Bearer $LIMITRY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "subject": { "kind": "user", "id": "u_42" }, "action": "ai-call",
"cost": 2000, "mode": "reserve" }'

It decides like any other check. If it’s allowed, Limitry holds the estimate: other checks for that subject see it as used. The answer includes the reservation:

{
"allowed": true,
"remaining": [{ "limit": "lim_…", "remaining": 8000, "…": "…" }],
"reservation": { "id": "rsv_…", "expiresAt": "2026-10-04T12:05:00Z" }
}

If the answer is no, Limitry holds nothing and there’s no reservation.

When the work is done, commit what it actually used:

Terminal window
curl -X POST https://api.limitry.com/v1/reservations/rsv_…/commit \
-H "Authorization: Bearer $LIMITRY_API_KEY" \
-H "Content-Type: application/json" -d '{ "cost": 1310 }'

If it used less than the estimate, Limitry gives the rest back at once. If it used more, Limitry charges the full amount, even past the limit, because the work did happen. The next check then sees the true total.

If the work didn’t happen, release the hold instead: POST /v1/reservations/{id}/release gives all of it back.

Settling is safe to retry: the same commit or release gets the same answer. Releasing a committed reservation, or committing a released one, is refused with 400.

A hold lasts ttlSeconds, which you set on the check: 300 by default, 3,600 at most. A hold nobody settles gives itself back when it expires, so a crashed worker never keeps a subject blocked. If the work did finish after all, a late commit within 24 hours still charges what it used.

For longer work, extend the hold as a heartbeat: POST /v1/reservations/{id}/extend with { "ttlSeconds": 600 } makes it expire that long from now. A reservation can also hold a slot of a concurrent limit.

  • A subject can have at most 1,000 open reservations. Past that, a reserve is a normal “no” with reason.code: "too_many_reservations".
  • A reserve counts as one check on your plan. Settling is free.
  • Reservations need the same checks:write scope as checks.