Skip to content

Lease wallet funds

The leases.create operation: POST /api/v1/wallets/{id}/leases in the REST API and the leases_create tool in the MCP server.

POST/api/v1/wallets/{id}/leases

Reserves a block of a wallet's funds (amount, e.g. 5.00) for ttlSeconds (at most 900), so your service can meter high-frequency usage locally against it and settle in batches instead of calling Ownrecord for every charge. Never consume more than the lease's remaining.

The funds are taken from the wallet's credits in spend order (promo credits first, then subscription credits, then purchased credits, each soonest expiry first) and held until you settle the lease (leases.settle). If the lease reaches its TTL without a closing settle, its expiryPolicy applies:

  • release (default): the rest goes back to the wallet; a later settle gets lease_expired;
  • capture: all of the rest is charged as consumed;
  • hold_for_late_settle: the funds stay reserved for lateSettleWindowSeconds (default one day), and one settle in that window charges what was consumed and releases the rest. If none comes, lateSettleFallback applies (release by default, or capture). For services that keep a durable local consumption log and can recover after a crash.

The response includes suggestedNextAmount, sizing guidance from recent consumption. Retrying with the same Idempotency-Key returns the same lease.

At a glance

  • REST: POST /api/v1/wallets/{id}/leases
  • MCP tool: leases_create
  • Operation ID: leases.create
  • Scope: Brand
  • Permission: wallets:write
  • Risk tier: write
  • Dry run: Supported (send Ownrecord-Dry-Run: true)
  • Idempotency: Idempotency-Key required
  • Modes: Test and live
  • Plan feature: usage_billing.enabled
  • Authentication: API key with the wallets:write scope
  • Since: M6

Request

Path parameters

FieldTypeDescription
idwal_… IDThe wallet to lease from (wal_…).

Body

A JSON object (Content-Type: application/json):

FieldTypeRequiredDescription
amountobjectYesHow much to lease, in the wallet's currency.
amount.amountstringYesInteger micros as a string (5000000 = 5.00); finer than a cent is fine.
amount.currencystringYesISO 4217 currency code: the wallet's currency.
ttlSecondsinteger (1 to 900)YesHow long the lease lives, 1 to 900 seconds.
expiryPolicyone of release, capture, hold_for_late_settleNoWhat happens if the lease reaches its TTL without a closing settle: release (default) returns the rest to the wallet, capture charges all of it, hold_for_late_settle keeps it reserved for a late settle.
lateSettleWindowSecondsinteger (1 to 604800)Nohold_for_late_settle only: how long after the TTL a settle is still accepted (default 86400, at most 7 days).
lateSettleFallbackone of release, captureNohold_for_late_settle only: what happens when the window ends without a settle, release (default) or capture.
metadataobjectNoYour own key-value pairs (at most 50 keys). On updates the whole set is replaced.

Response

201 Created with a JSON object:

FieldTypeDescription
idlease_… ID
object"lease"
brandIdbrd_… ID
livemodeboolean
walletIdwal_… ID
statusone of active, expired_pending, captured, released, expiredactive: settle against it. expired_pending: past its TTL, held for a late settle until lateSettleUntil. captured: closed by a settle (final or a renewal). expired: closed by its expiry policy.
amountmoneyHow much was leased.
capturedmoneyHow much the settles (and an expiry capture) have charged so far. The ledger books it as usage revenue when the lease closes, is renewed or expires.
remainingmoneyWhat can still be consumed: amount − captured while the lease is open, 0 once it is closed.
ttlSecondsinteger (1 to 900)
expiresAtdate-timeSettle before this instant; then the expiry policy applies.
expiryPolicyone of release, capture, hold_for_late_settle
lateSettleWindowSecondsinteger or nullhold_for_late_settle only.
lateSettleFallbackone of release, capture or nullhold_for_late_settle only.
lateSettleUntildate-time or nullhold_for_late_settle only: a settle is accepted until this instant (expiresAt + window).
settleSeqinteger (at least 0)How many settles the lease has had.
renewedFromlease_… ID or nullThe lease this one renewed.
renewedTolease_… ID or nullThe lease a renewing settle replaced this one with.
suggestedNextAmountmoneySizing guidance for the next lease (or renewal) of this TTL, from the recent consumption rate with 25% headroom.
metadataobject
createdAtdate-time
closedAtdate-time or null

Examples

Lease 5.00 for five minutes

HTTP
POST /api/v1/wallets/wal_01m4ty3c2b5qx7mhd9k8w6s0b1/leases
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json

{
  "amount": {
    "amount": "5000000",
    "currency": "EUR"
  },
  "ttlSeconds": 300,
  "metadata": {
    "worker": "api-eu-1"
  }
}

Response (201):

JSON
{
  "id": "lease_01m4v0d9k2j7r5t3w8x6y4z2b0",
  "object": "lease",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "livemode": false,
  "walletId": "wal_01m4ty3c2b5qx7mhd9k8w6s0b1",
  "status": "active",
  "amount": {
    "amount": "5000000",
    "currency": "EUR"
  },
  "captured": {
    "amount": "0",
    "currency": "EUR"
  },
  "remaining": {
    "amount": "5000000",
    "currency": "EUR"
  },
  "ttlSeconds": 300,
  "expiresAt": "2026-10-13T12:05:00.000Z",
  "expiryPolicy": "release",
  "lateSettleWindowSeconds": null,
  "lateSettleFallback": null,
  "lateSettleUntil": null,
  "settleSeq": 0,
  "renewedFrom": null,
  "renewedTo": null,
  "suggestedNextAmount": {
    "amount": "5000000",
    "currency": "EUR"
  },
  "metadata": {
    "worker": "api-eu-1"
  },
  "createdAt": "2026-10-13T12:00:00.000Z",
  "closedAt": null
}

A lease that survives a crash

The funds stay reserved for an hour after the TTL, for one late settle; then they're released.

HTTP
POST /api/v1/wallets/wal_01m4ty3c2b5qx7mhd9k8w6s0b1/leases
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json

{
  "amount": {
    "amount": "5000000",
    "currency": "EUR"
  },
  "ttlSeconds": 300,
  "expiryPolicy": "hold_for_late_settle",
  "lateSettleWindowSeconds": 3600,
  "metadata": {
    "worker": "api-eu-1"
  }
}

Response (201):

JSON
{
  "id": "lease_01m4v0d9k2j7r5t3w8x6y4z2b0",
  "object": "lease",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "livemode": false,
  "walletId": "wal_01m4ty3c2b5qx7mhd9k8w6s0b1",
  "status": "active",
  "amount": {
    "amount": "5000000",
    "currency": "EUR"
  },
  "captured": {
    "amount": "0",
    "currency": "EUR"
  },
  "remaining": {
    "amount": "5000000",
    "currency": "EUR"
  },
  "ttlSeconds": 300,
  "expiresAt": "2026-10-13T12:05:00.000Z",
  "expiryPolicy": "hold_for_late_settle",
  "lateSettleWindowSeconds": 3600,
  "lateSettleFallback": "release",
  "lateSettleUntil": "2026-10-13T13:05:00.000Z",
  "settleSeq": 0,
  "renewedFrom": null,
  "renewedTo": null,
  "suggestedNextAmount": {
    "amount": "5000000",
    "currency": "EUR"
  },
  "metadata": {
    "worker": "api-eu-1"
  },
  "createdAt": "2026-10-13T12:00:00.000Z",
  "closedAt": null
}

Errors

CodeHTTP statusWhenWhat to do
entitlement_required403The organization's plan doesn't include usage billing (usage_billing.enabled).Upgrade the organization's plan, or remove something to get back under the limit.
validation_failed400A field is malformed: an amount that isn't positive micros, a TTL outside 1–900 seconds, late-settle terms without hold_for_late_settle.Fix the fields listed in details.issues and send the request again.
resource_not_found404No wallet with this ID in the brand and mode.Check the ID and that your credentials are scoped to the right organization, entity, brand and mode.
insufficient_funds402The wallet's spendable credits are less than amount (402).Reserve or capture a smaller amount, or top up the wallet first. details.requested and details.available are in integer micros.
wallet_fresh_balance_capped402The brand caps how much of recently bought credits can be used (402; §20 usage caps for fresh balances).Ask for less now, or wait until details.freeAt: the brand caps how much of a fresh balance can be used (details.cap, micros) until the credits are details.untilAgeHours hours old.
wallet_currency_mismatch400amount isn't in the wallet's currency.Use the wallet for this currency. A customer has one wallet per currency (details.expected, details.actual).
wallet_frozen409The wallet is frozen.Unfreeze the wallet (wallets.update with status: active) once the review is done. Reservations made before the freeze can still be captured or released.
wallet_closed409The wallet is closed.Reopen the wallet with wallets.update (status: active). A purchase of credits in its currency reopens it, too.
idempotency_key_reused409The Idempotency-Key was already used for another lease or reservation of this wallet.Use a new Idempotency-Key for a different request, or resend the original request unchanged.
permission_denied403The credentials can't use this brand's wallets (wallets:write).Ask an organization owner or admin for a role or scope that allows this action.

It can also return these common errors: api_version_unsupported, invalid_json, unsupported_media_type, authentication_required, api_key_invalid, scope_required, idempotency_key_required, idempotency_in_progress, approval_required, approval_rejected, approval_expired, stale_approval, approval_policy_denied, rate_limited, internal_error.