Skip to content

Reservations tools

MCP tools for Reservations: reservations_capture, reservations_create, reservations_get, reservations_list, reservations_release.

  • reservations_capture: Capture a reservation
  • reservations_create: Reserve funds on a wallet
  • reservations_get: Get a reservation
  • reservations_list: List a wallet's reservations
  • reservations_release: Release a reservation

reservations_capture

Capture a reservation. Operation reservations.capture · Brand level · permission wallets:write · risk tier write · since M6

Charges what was used from a reservation: at most what is still reserved (capture_exceeds_reservation), and before expiresAt (reservation_expired). By default the rest is released and the reservation closes as captured; with final: false it stays open for further captures until it expires.

usage says what the amount was spent on, one line per meter (meter, quantity, amount, attributes); the line amounts add up to amount. The revenue is booked per meter, and each line is stored as a usage event (with its wallet, the reservation and its amount) and counted in the daily usage aggregates. A meter that a metered price bills in arrears can't be used (usage_meter_billed_in_arrears). Without usage the whole amount is usage of the meter default, and no usage event is stored.

The captured credits leave the wallet: it may emit wallet.low_balance or wallet.depleted, and start an auto-reload. The same idempotencyKey (or Idempotency-Key) and request returns the same capture.

Annotations: readOnlyHint: false · destructiveHint: false · idempotentHint: true · openWorldHint: false

Arguments

ArgumentTypeRequiredDescription
idID (res_…)yesThe reservation (res_…).
amountobjectyesWhat was used: at most what is still reserved.
usagearray of objectnoWhat the amount was spent on, one line per meter. Without it the whole amount is usage of the meter default. (1–100 items)
finalbooleannoRelease the rest and close the reservation (default true). false keeps it open for more captures until it expires.
idempotencyKeystringnoUnique per reservation: the same key and request returns the same capture (default: the request's Idempotency-Key).
entity_idID (ent_…)noOptional: the brand's legal entity (ent_…); brand_id alone is enough.
brand_idID (brd_…)noThe brand to act on (brd_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand.
idempotency_keystringnoA unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's idempotency_key. (1–255 characters)
dry_runbooleannotrue: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default false)
  • Brand: pass brand_id with an organization or entity key; a brand key acts on its own brand.
  • Idempotency: the result returns the idempotency_key used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
  • Dry run: supported. dry_run: true checks permissions and rules and computes the result, then rolls everything back.

Errors

Example

Capture €1.25 of €3 for 250 API calls and release the rest

Arguments:

JSON
{
  "id": "res_01m4ty7w2a5qx7mhd9k8w6s0b4",
  "amount": {
    "amount": "1250000",
    "currency": "EUR"
  },
  "usage": [
    {
      "meter": "api.calls",
      "quantity": "250",
      "amount": {
        "amount": "1250000",
        "currency": "EUR"
      }
    }
  ],
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}

Result output:

JSON
{
  "id": "res_01m4ty7w2a5qx7mhd9k8w6s0b4",
  "object": "reservation",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "livemode": false,
  "walletId": "wal_01m4ty7w2a5qx7mhd9k8w6s0b1",
  "kind": "single",
  "status": "captured",
  "amount": {
    "amount": "3000000",
    "currency": "EUR"
  },
  "captured": {
    "amount": "1250000",
    "currency": "EUR"
  },
  "outstanding": {
    "amount": "0",
    "currency": "EUR"
  },
  "expiresAt": "2026-10-16T10:20:00.000Z",
  "closedAt": "2026-10-16T10:16:00.000Z",
  "idempotencyKey": "job-8841",
  "metadata": {
    "job": "8841"
  },
  "createdAt": "2026-10-16T10:15:00.000Z",
  "updatedAt": "2026-10-16T10:16:00.000Z"
}

reservations_create

Reserve funds on a wallet. Operation reservations.create · Brand level · permission wallets:write · risk tier write · since M6

Holds an amount of a wallet's credits for usage that is about to happen (spec §17.2): taken in spend order (promo credits first, soonest expiry first) and moved to reserved, so nothing else can spend them. Capture what was used (reservations.capture) or release the hold (reservations.release) before expiresAt (ttlSeconds, at most an hour); after that the funds go back to the wallet by themselves (reservation.expired).

Answers 402 insufficient_funds when the wallet's spendable credits are less than the amount. A frozen or closed wallet takes no reservations. The same idempotencyKey (or Idempotency-Key) and request returns the same reservation. For thousands of small charges a minute, use leases instead (§17.3).

Annotations: readOnlyHint: false · destructiveHint: false · idempotentHint: true · openWorldHint: false

Arguments

ArgumentTypeRequiredDescription
idID (wal_…)yesThe wallet (wal_…).
amountobjectyesThe amount to hold, in the wallet's currency.
ttlSecondsintegeryesHow long the hold lasts, 1 to 3600 seconds. (1–3600)
metadataobjectnoYour own key-value pairs (at most 50 keys, values of at most 500 characters).
idempotencyKeystringnoUnique per wallet: the same key and request returns the same reservation (default: the request's Idempotency-Key).
entity_idID (ent_…)noOptional: the brand's legal entity (ent_…); brand_id alone is enough.
brand_idID (brd_…)noThe brand to act on (brd_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand.
idempotency_keystringnoA unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's idempotency_key. (1–255 characters)
dry_runbooleannotrue: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default false)
  • Brand: pass brand_id with an organization or entity key; a brand key acts on its own brand.
  • Idempotency: the result returns the idempotency_key used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
  • Dry run: supported. dry_run: true checks permissions and rules and computes the result, then rolls everything back.
  • Plan feature: needs usage_billing.enabled.

Errors

  • validation_failed: The amount isn't positive, ttlSeconds isn't 1 to 3600, or the metadata or idempotency key is malformed.
  • resource_not_found: This brand has no such wallet in this mode (wallets belong to one brand and one mode).
  • insufficient_funds: The wallet's spendable credits (unexpired, not already reserved) are less than the amount (details.requested, details.available). HTTP 402.
  • wallet_fresh_balance_capped: The wallet has the credits, but the brand caps how much of recently bought credits can be used (details.freeAt; §20, WP-M9-risk). HTTP 402.
  • wallet_currency_mismatch: The amount isn't in the wallet's currency.
  • wallet_frozen: The wallet is frozen: it takes no new reservations.
  • wallet_closed: The wallet is closed.
  • idempotency_key_reused: The idempotency key was used before for another request.
  • version_conflict: The wallet changed at the same moment (another reservation, capture or grant); retry.
  • scope_required: The request names no brand (an org or entity key without Ownrecord-Brand).
  • permission_denied: You can't change this brand's wallets (wallets:write: owners, admins and support).
  • livemode_not_allowed: You have no live-mode access, so you can't use live wallets.
  • entitlement_required: The organization's plan doesn't include usage billing (usage_billing.enabled).

Example

Hold €3 for five minutes

Arguments:

JSON
{
  "id": "wal_01m4ty7w2a5qx7mhd9k8w6s0b1",
  "amount": {
    "amount": "3000000",
    "currency": "EUR"
  },
  "ttlSeconds": 300,
  "idempotencyKey": "job-8841",
  "metadata": {
    "job": "8841"
  },
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}

Result output:

JSON
{
  "id": "res_01m4ty7w2a5qx7mhd9k8w6s0b4",
  "object": "reservation",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "livemode": false,
  "walletId": "wal_01m4ty7w2a5qx7mhd9k8w6s0b1",
  "kind": "single",
  "status": "active",
  "amount": {
    "amount": "3000000",
    "currency": "EUR"
  },
  "captured": {
    "amount": "0",
    "currency": "EUR"
  },
  "outstanding": {
    "amount": "3000000",
    "currency": "EUR"
  },
  "expiresAt": "2026-10-16T10:20:00.000Z",
  "closedAt": null,
  "idempotencyKey": "job-8841",
  "metadata": {
    "job": "8841"
  },
  "createdAt": "2026-10-16T10:15:00.000Z",
  "updatedAt": "2026-10-16T10:15:00.000Z"
}

reservations_get

Get a reservation. Operation reservations.get · Brand level · permission wallets:read · risk tier read · since M6

Returns a single reservation (res_…): what it holds (outstanding), what was captured, its status and when it expires. Leases have their own operation (leases.get).

Annotations: readOnlyHint: true · destructiveHint: false · idempotentHint: true · openWorldHint: false

Arguments

ArgumentTypeRequiredDescription
idID (res_…)yesThe reservation (res_…).
entity_idID (ent_…)noOptional: the brand's legal entity (ent_…); brand_id alone is enough.
brand_idID (brd_…)noThe brand to act on (brd_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand.
  • Brand: pass brand_id with an organization or entity key; a brand key acts on its own brand.
  • Read-only: changes nothing; idempotency_key is ignored.

Errors

Example

An open reservation

Arguments:

JSON
{
  "id": "res_01m4ty7w2a5qx7mhd9k8w6s0b4",
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz"
}

Result output:

JSON
{
  "id": "res_01m4ty7w2a5qx7mhd9k8w6s0b4",
  "object": "reservation",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "livemode": false,
  "walletId": "wal_01m4ty7w2a5qx7mhd9k8w6s0b1",
  "kind": "single",
  "status": "active",
  "amount": {
    "amount": "3000000",
    "currency": "EUR"
  },
  "captured": {
    "amount": "0",
    "currency": "EUR"
  },
  "outstanding": {
    "amount": "3000000",
    "currency": "EUR"
  },
  "expiresAt": "2026-10-16T10:20:00.000Z",
  "closedAt": null,
  "idempotencyKey": "job-8841",
  "metadata": {
    "job": "8841"
  },
  "createdAt": "2026-10-16T10:15:00.000Z",
  "updatedAt": "2026-10-16T10:15:00.000Z"
}

reservations_list

List a wallet's reservations. Operation reservations.list · Brand level · permission wallets:read · risk tier read · since M6

Lists a wallet's reservations, newest first: single reservations and leases (kind), open and closed.

  • status=active: what holds funds now (with expired_pending for leases waiting for a late settle);
  • kind: single or lease.

Paginate with limit (1–100, default 10) and starting_after (a res_… or lease_… ID).

Annotations: readOnlyHint: true · destructiveHint: false · idempotentHint: true · openWorldHint: false

Arguments

ArgumentTypeRequiredDescription
idID (wal_…)yesThe wallet (wal_…).
limitintegerno(1–100)
starting_afterstringno–
statusone of active, expired_pending, captured, released, expirednoOnly reservations with this status.
kindone of single, leasenosingle reservations or leases (spec §17.3); both by default.
entity_idID (ent_…)noOptional: the brand's legal entity (ent_…); brand_id alone is enough.
brand_idID (brd_…)noThe brand to act on (brd_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand.
  • Brand: pass brand_id with an organization or entity key; a brand key acts on its own brand.
  • Read-only: changes nothing; idempotency_key is ignored.

Errors

  • validation_failed: id isn't a wallet ID, or a filter or pagination value is invalid.
  • resource_not_found: This brand has no such wallet in this mode (wallets belong to one brand and one mode).
  • scope_required: The request names no brand (an org or entity key without Ownrecord-Brand).
  • permission_denied: You can't read this brand's wallets (wallets:read).
  • livemode_not_allowed: You have no live-mode access, so you can't use live wallets.

Example

Open reservations

Arguments:

JSON
{
  "id": "wal_01m4ty7w2a5qx7mhd9k8w6s0b1",
  "status": "active",
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz"
}

Result output:

JSON
{
  "object": "list",
  "data": [
    {
      "id": "res_01m4ty7w2a5qx7mhd9k8w6s0b4",
      "object": "reservation",
      "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
      "livemode": false,
      "walletId": "wal_01m4ty7w2a5qx7mhd9k8w6s0b1",
      "kind": "single",
      "status": "active",
      "amount": {
        "amount": "3000000",
        "currency": "EUR"
      },
      "captured": {
        "amount": "0",
        "currency": "EUR"
      },
      "outstanding": {
        "amount": "3000000",
        "currency": "EUR"
      },
      "expiresAt": "2026-10-16T10:20:00.000Z",
      "closedAt": null,
      "idempotencyKey": "job-8841",
      "metadata": {
        "job": "8841"
      },
      "createdAt": "2026-10-16T10:15:00.000Z",
      "updatedAt": "2026-10-16T10:15:00.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}

reservations_release

Release a reservation. Operation reservations.release · Brand level · permission wallets:write · risk tier write · since M6

Gives what a reservation still holds back to the wallet (each part to the credits it came from) and closes it as released. Releasing a released reservation returns it unchanged; a captured or expired one can't be released (reservation_not_open). Releasing changes no balance: the credits only stop being held.

Annotations: readOnlyHint: false · destructiveHint: false · idempotentHint: true · openWorldHint: false

Arguments

ArgumentTypeRequiredDescription
idID (res_…)yesThe reservation (res_…).
entity_idID (ent_…)noOptional: the brand's legal entity (ent_…); brand_id alone is enough.
brand_idID (brd_…)noThe brand to act on (brd_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand.
idempotency_keystringnoA unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's idempotency_key. (1–255 characters)
dry_runbooleannotrue: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default false)
  • Brand: pass brand_id with an organization or entity key; a brand key acts on its own brand.
  • Idempotency: the result returns the idempotency_key used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
  • Dry run: supported. dry_run: true checks permissions and rules and computes the result, then rolls everything back.

Errors

Example

The job was cancelled

Arguments:

JSON
{
  "id": "res_01m4ty7w2a5qx7mhd9k8w6s0b4",
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}

Result output:

JSON
{
  "id": "res_01m4ty7w2a5qx7mhd9k8w6s0b4",
  "object": "reservation",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "livemode": false,
  "walletId": "wal_01m4ty7w2a5qx7mhd9k8w6s0b1",
  "kind": "single",
  "status": "released",
  "amount": {
    "amount": "3000000",
    "currency": "EUR"
  },
  "captured": {
    "amount": "0",
    "currency": "EUR"
  },
  "outstanding": {
    "amount": "0",
    "currency": "EUR"
  },
  "expiresAt": "2026-10-16T10:20:00.000Z",
  "closedAt": "2026-10-16T10:16:00.000Z",
  "idempotencyKey": "job-8841",
  "metadata": {
    "job": "8841"
  },
  "createdAt": "2026-10-16T10:15:00.000Z",
  "updatedAt": "2026-10-16T10:16:00.000Z"
}