Skip to content

Reservations

An amount is reserved for one order at a time. This page is when that starts, when it ends, and what it costs you when a lot of people are paying at once.

open ──(customer taps)──▶ reserved ──(credit lands)──▶ hold ──▶ released, confirmed
reserved ──(nobody paid)──▶ released, expired ──▶ rests, out of circulation
any ──(you cancel)────────▶ released, cancelled ──▶ rests, out of circulation

Reserved at the tap, not at creation. POST /v1/intents creates an order and reserves nothing. Nothing is held until the customer taps a pay button, the web SDK reports a tap, or a QR is rendered. Browsing costs nothing and an abandoned cart costs nothing, which is the whole reason the two calls are separate.

If you genuinely cannot call at tap time — a backend that mints a link and hands it off with no further contact — pass reservation: "immediate" on creation and accept that the amount is held for the whole open window instead of for the couple of minutes somebody is actually paying.

default range what it is
reserve 300 s 60–900 s How long a reservation lives without a heartbeat.
extension cap 900 s A reservation can never outlive 15 minutes from when the amount was first taken, however many heartbeats arrive.
hold 180 s 60–900 s After the credit lands. Absorbs the second alert of a double payment, and the second feed’s copy of the same credit.
rest 3600 s 3600–7200 s How long an amount released unpaid — expired or cancelled — is passed over, while any other free amount exists. Never configurable below the default; that floor is enforced, not a suggestion.
order ttl 86400 s 1–604800 s The order’s own lifetime, set per order with ttl.

The first four are per-tenant, in Settings. The last is per order.

Rest is the setting that makes small sellers work. Without it, an amount abandoned by a slow payer goes straight back into circulation and the next customer gets it — so when the slow payer finally pays, two orders are holding the same amount and the credit is ambiguous. Resting it for an hour by default means the late payment almost always still belongs to exactly one order, which is what makes the late evidence label trustworthy. See Evidence.

A visible checkout should heartbeat:

Terminal window
curl -sS -X POST https://moneylanded.com/v1/intents/int_9f2c1a77b40e6d3a5c81/extend \
-H "Authorization: Bearer $ML_KEY"
{ "reserve_expires_at": 1758211620 }

A 409 with {"error":"no open reservation"} means it already lapsed. Call reserve again — and re-render your buttons from the new response, because the amount may have changed.

Our own web SDK polls status every 2 seconds and heartbeats every 60, which is 31 requests a minute against a 60-a-minute limit on the page token. If you are writing your own front end against /p/{token}/*, stay inside that.

One price point, Poisson arrivals, reservations at the five-minute default (reserve, above). “Open at once” is roughly orders-per-hour × the reserve window; “max discount” is what the last concurrent customer sees — the average is far lower, because the walk starts at the list price and most customers take the top of it.

orders per hour open at once max discount max discount, per-app pools a 2% gateway on ₹499
100 ~8 ₹0.08 ₹0.03 ₹9.98
1,000 ~83 ₹0.83 ₹0.28 ₹9.98
3,000 ~250 ₹2.50 ₹0.83 ₹9.98
10,000 ~833 ₹8.33 ₹2.78 ₹9.98

This counts open and held reservations only. It does not add amounts that are currently resting (above), which also shrink the free pool for as long as they rest. Treat it as a floor, not a ceiling: the band you actually need to stay clear of pool_exhausted can run deeper than these numbers, especially once real-world abandonment is in the mix. Your band has a hard ceiling either way — ₹9.99 (999 paise), tenant-configurable up to that and no further — and the 10,000-an-hour row is close enough to it, and understated enough by the gap above, that per-app pools or a bank merchant VPA stop being optional at that volume.

The right-hand column is what a payment gateway charges to do the correlation for you, on the same order, at every volume. It does not move.

If your volume needs a deeper band than you are willing to give away, widening the band is the wrong answer. Get a merchant VPA from your own bank: that channel returns your order reference directly and removes amount variation completely. Money still never touches us. It is not built yet.

{ "error": "pool_exhausted", "retry_after_seconds": 30 }

A 503. Every amount from your list price down to the bottom of your band is held right now. We refuse rather than hand out an amount that cannot be told apart from another order — an ambiguous amount is worse than a retry. We alert our operators at the same time, and the hosted pay page says “busy, try again in a moment”.

Retry after retry_after_seconds. Do not widen the amount yourself.

Off by default, and only worth turning on at thousands of orders an hour.

The payer’s UPI handle suffix names the app they paid from, exactly: @ybl, @ibl, @axl is PhonePe; @okaxis, @okhdfcbank, @okicici, @oksbi is Google Pay; @paytm and its siblings are Paytm; @upi is BHIM.

With pools on, a tap on your PhonePe button reserves in the PhonePe pool, so the same amount can be live once per app and your effective capacity roughly triples — the fourth column of the table above.

The catch, and the reason it is opt-in: a customer taps PhonePe and then pays from Google Pay. Now the credit’s handle says Google Pay, and a different order is holding that amount in the Google Pay pool. Both are candidates, so the credit is parked for you. At low volume that trade is not worth making.

A QR render and the generic “other UPI app” button reserve in the any pool, which blocks every pool.

Terminal window
curl -sS -X POST https://moneylanded.com/v1/intents/int_9f2c1a77b40e6d3a5c81/cancel \
-H "Authorization: Bearer $ML_KEY"

Releases the reservation immediately — nothing is held for this order from that moment on — but the amount itself rests, exactly like an unpaid expiry. A customer can still pay a cancelled order late, from a stale screen, so the amount is not handed to the next customer until rest has run its course.

That also means a late payment against a cancelled order never auto-confirms it: the late evidence label only reaches an expired reservation, never a cancelled one. The credit parks instead, with the cancelled order listed among its candidates, for you to place by hand if you decide to honour it.

Cancelling a cancelled order is a 200. Cancelling a confirmed order is a 409 — the money is already in your account and there is nothing here to undo. Refunding it is between you and your customer’s bank, and no part of it involves us.