Fund your test programme
Create test money in your programme float and put it on cards.
Cards can't spend money that isn't there. This guide covers the three steps between "I have a test programme" and "I have a card that can authorize": put test money into the programme's float, move it onto a card's funding account, and know what happens when you hit the ceiling.
How money flows
Money in test mode moves in one direction, through three stops:
Issuance → programme float → card funding account → spend. A simulated deposit mints test money straight onto the programme's float — there's no external payment rail behind it, so nothing needs to be "cleared" before the balance is usable. From the float, a card top-up moves a slice of it onto one card's own funding account, and only then can that card authorize a simulated spend against it.
Test money is synthetic and mode-isolated: it exists only in test mode, on a programme's test-mode float, and it never touches or offsets anything in live mode. Switching a programme to live mode does not carry a test float's balance with it — live funding is a separate, operator-arranged process.
Deposit to your float
POST /v1/simulated-deposits mints test money onto a programme's float. It
requires an Idempotency-Key header — an identical retry replays the stored
result, and reusing the key with a different body returns 409:
curl -X POST https://api.rigid.fi/v1/simulated-deposits \
-H "Authorization: Bearer $RIGID_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"program_id": "<programme-id>",
"amount": { "amount": "250.00", "currency": "GBP" },
"reference": "Sandbox top-up"
}'{
"id": "0198e2c1-9f3a-7c42-b8d1-6a2f5c0e77b3",
"program_id": "<programme-id>",
"amount": { "amount": "250.00", "currency": "GBP" },
"actor_user_id": "018f2b3c-9f3a-7e5f-8a9b-0c1d2e3f4a5b",
"transfer_id": "tr_018f2b3c-9f3a-7e5f-8a9b-0c1d2e3f4a5c",
"float_account_id": "acct_018f2b3c-9f3a-7e5f-8a9b-0c1d2e3f4a5d",
"reference": "Sandbox top-up",
"created_at": "2026-08-25T12:00:00Z",
"float_available": { "amount": "250.00", "currency": "GBP" },
"replayed": false
}This route is test mode only, and the caller must be a programme admin
for the programme named in program_id. There's nothing to provision ahead
of time — the first deposit against a programme lazily creates its float, so
a brand-new test-mode programme can go straight from zero to a funded float
in one call. reference is optional and echoed back on the record.
To see what's landed so far, list the programme's deposit history. Passing
currency also returns that float's current balance as float_available:
curl "https://api.rigid.fi/v1/simulated-deposits?program_id=<programme-id>¤cy=GBP" \
-H "Authorization: Bearer $RIGID_API_TOKEN"float_available is null when currency is omitted, or when the float
has never been minted into. Reference:
Simulate a deposit and
List simulated deposits.
The console's Sandbox tab carries the same flow as a "Programme float" deposit panel, if you'd rather fund a programme by hand than script it.
Top up a card
A float balance doesn't authorize anything by itself — a card can only spend
from its own funding account. POST /v1/cards/{id}/topup moves money from
the card's programme float onto that card:
curl -X POST https://api.rigid.fi/v1/cards/<card-id>/topup \
-H "Authorization: Bearer $RIGID_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"amount": { "amount": "50.00", "currency": "GBP" }
}'There's no program_id in the body — the card's own programme and currency
are resolved from card_id, and a currency mismatch between the request and
the funding account is rejected. If the float doesn't have enough available
to cover the top-up, fund it first with a simulated
deposit. See the full flow in the
quickstart and the reference for
Top up a card.
Withdraw and reset
POST /v1/simulated-withdrawals is the deposit's mirror: it burns test money
back out of a programme's float rather than minting it in. Same
Idempotency-Key requirement, same programme admin requirement for the
programme named in program_id. Pass an amount to withdraw a fixed sum:
curl -X POST https://api.rigid.fi/v1/simulated-withdrawals \
-H "Authorization: Bearer $RIGID_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"program_id": "<programme-id>",
"amount": { "amount": "50.00", "currency": "GBP" }
}'Or reset the float in one call: pass drain: true and a currency instead
of an amount, and the whole current balance in that currency burns —
whatever it happens to be, without you having to look it up first. drain
requires currency because the float is per-currency; there's no single
balance to drain without saying which one:
curl -X POST https://api.rigid.fi/v1/simulated-withdrawals \
-H "Authorization: Bearer $RIGID_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"program_id": "<programme-id>",
"drain": true,
"currency": "GBP"
}'A withdrawal can only burn what's still sitting in the float, checked
against its posted balance — today identical to the float_available
figure the panel reports.
Money is not withdrawable once it has left the float: a top-up
moves it onto a card's own funding account, and a withdrawal or a drain
afterwards can't reach it and won't unwind the top-up. A request for more
than the float currently holds fails 400 with code float_insufficient; a
withdrawal against a currency the float has never been minted into fails
400 with no_float; and draining a float that's already at zero fails
400 with nothing_to_drain.
Burning is real drawdown, not a cosmetic reset: it restores the same test issuance ceiling headroom the original deposit consumed, so a tenant that's hit its ceiling can withdraw — or drain a float it no longer needs — to buy back room to mint again. Reference: Simulate a withdrawal and List simulated withdrawals.
Both directions are webhook events, if you'd rather react to them than poll:
a deposit fires
ledger.funds.issued, a
withdrawal or drain fires
ledger.funds.retired — see
the webhooks guide for delivery, verification and
retries.
The console's Sandbox tab carries Withdraw and Drain float actions right next to Deposit, on the same programme float panel.
Limits
Simulated deposits draw down a per-tenant test issuance ceiling — a cap
on how much test money your tenant can mint in total, across every test-mode
programme and float. Once it's exhausted, further deposits fail 400 with
code issuance_ceiling_reached. This isn't a per-request throttle you can
retry your way past — if you hit it, contact support to have the ceiling
raised.
Live mode
Live funding is real money, and it isn't self-serve. It's arranged with your
operator during onboarding, outside the API. POST /v1/simulated-deposits reflects that boundary directly: called against a
live-mode programme it returns 403 with code live_funding_operator_only,
regardless of the caller's role — there is no permission that unlocks it.
Test-mode self-serve funding and live-mode operator funding are deliberately
two different paths, not one gated behind a role check.