Wallets & payments
Live purchases are paid from the payer's own wallet at Perflo. The payer is whoever the intent buys for: your account buying for itself, or one of your customers. Perflo holds their money, not OpenIntents, and the payer decides how much their agents may spend.
Test keys never pay, so they never need a wallet: a test intent stops at the payment step with a receipt preview.
How a purchase is paid
- The agent reaches checkout. The cart is filled and the shop shows its total.
- OpenIntents asks the payer's wallet for that total. Perflo checks it against the payer's limits and balance, then loads the payer's virtual Mastercard with exactly that amount.
- The card is typed into the shop's form. OpenIntents reveals the card once and types it in itself. It is kept in memory for that run only, never stored, and never shown to the AI model, your code or your agent.
- The order is placed and the intent completes with a receipt.
A run that ends without placing the order cancels the request at Perflo, so
the money goes back to the wallet. Wallets pay in US dollars. A shop that
charges in another currency fails the intent with currency_unsupported,
and nothing is charged, unless the service converts for that currency: the
wallet is then asked for the dollars the shop's total comes to, with some
headroom for the card network's rate, and what the shop does not take goes
back to the wallet when the order settles. The intent's max_amount and
receipt stay in the shop's currency.
Setting up a wallet
A payer sets up their wallet once, on our page at
https://openintents.io/wallet/…, with Perflo's own sign-in framed in it.
They:
- connect their Perflo account, signing in or making one (the email and name you gave the customer are filled in already),
- set limits: the most one purchase, and one month, may cost,
- add money, or turn on Perflo's automatic top-ups from a saved card,
- confirm who they are, only when Perflo asks for it (for larger purchases, say).
The page runs only the steps left and skips the ones done. You don't need to send anyone there ahead of time: the first live purchase that needs the wallet pauses and asks for it (see below). To have a customer ready before their first purchase, send them a wallet link during your own sign-up.
Your account's own wallet is on the Wallets page of the dashboard, with each customer's status beside it. Customers also see theirs in the payer portal.
When a purchase waits
When the payer has a step to take first, the intent pauses with
"status": "requires_payment" and a payment object:
{
"id": "int_7f11b0",
"status": "requires_payment",
"payment": {
"reason": "add_money",
"payment_url": "https://openintents.io/api/pay/pay_7f11b0",
"merchant": "TAP Air Portugal",
"total": 38900,
"fee": 0,
"currency": "usd",
"expires_at": "2026-10-01T09:00:00Z"
}
}Send payment_url to the payer: show it in your app, post it in a chat, or
let the MCP tools hand it to the user. It opens the wallet page
with only the step this purchase needs. Once it's done the intent goes back to
queued by itself, with the same cart, and on to completed.
reason | What the payer does |
|---|---|
connect_wallet | Connects their wallet, or signs in to it again. |
add_money | Adds money: the wallet is short of total. |
identity_check | Confirms who they are: the wallet needs it first. |
wallet_processing | Nothing yet: the wallet is still taking the money (from a card they saved, say). The link shows how it is going. |
over_limit | Raises the limit they set: the purchase is over it. |
requested | Approves the purchase: you asked for a link first (require_payment_link). |
Other values may appear, so treat an unknown reason like any other: send
the link. Links expire after 30 minutes; an expired or
cancelled intent is free. If Perflo declines the
payment, the intent fails with payment_declined and Perflo's own message,
and nothing is charged.
Wallet links
Onboard a customer ahead of their first purchase:
| Parameter | Type | |
|---|---|---|
amount | integer | A purchase, in cents, to have the wallet ready for: the page then also asks for any money or limits it would need. |
return_url | string | A public https URL the page offers a way back to. |
ttl_seconds | integer | How long the link works, 60 to 604800. Default 86400 (a day). |
{
"id": "wl_7c2e91d04b8a3f56",
"object": "wallet_link",
"customer": "cus_8f2a",
"amount": 5000,
"url": "https://openintents.io/wallet/wl_7c2e91d04b8a3f56",
"status": "open",
"expires_at": "2026-10-02T08:12:00Z"
}url is for the customer alone. status is open until the wallet needs
nothing more (completed) or the link runs out (expired). Once they
connect, endpoints that name wallet.connected get a
webhook.
{
"object": "wallet",
"customer": "cus_8f2a",
"status": "funded",
"next_step": { "kind": "identity_check", "amount": null }
}Where the customer stands, and the one step left before a purchase of
amount could be paid (without amount: before they are set up at all).
status goes not_connected, pending, connected, limits_set, funded,
identity_checked, or is not_needed for test keys. next_step.kind is
connect, set_limits, add_money or identity_check, and next_step is
null when nothing is left. It never shows their balance or limits: those
are the customer's own.
Keeping spend in check
| Control | Where | Effect |
|---|---|---|
| Wallet limits | set by the payer, in their wallet | A purchase over them waits with reason: over_limit until the payer raises them. |
max_amount | per intent | The intent fails with over_max_amount rather than spend more. |
require_payment_link | per intent | The purchase always waits for the payer's approval link. |
Customer limits | set by you, per customer | per_intent caps one purchase and monthly what the payer's wallet purchases commit in a UTC calendar month (each counts its cart's total). They are checked before anything is paid: a purchase over one fails with limit_exceeded, and nothing the payer does in their wallet lifts them. |
The fee
For now, purchases paid from a wallet carry no fee: fee is 0 on the
payment request and on the receipt. See Pricing.
What Perflo gets
To pay, Perflo receives the payer's ID with us, their email and name when we have them, and for each purchase the shop's name and domain, the amount and a short description. We never store the card's details, and Perflo never sees what is in the cart beyond that description.
Card details never reach your code, your agent or the model. The card is revealed once, typed into the shop's payment form by OpenIntents, and kept in memory for that run only.