Wallet
Wallet balance, transactions and top-ups.
Quote a wallet top-up
Section titled “Quote a wallet top-up”POST /billing/topup-quote
Prices a wallet top-up before checkout: the exact subtotal, processing fee, tax and total the card will be charged, and the USD that will land in the wallet. Returns a signed quote valid for 10 minutes that POST /payments/create-order requires. The key’s owner must be OWNER or ADMIN, and a billing country must be set first. Pay-as-you-go organisations only: an organisation on plan billing (billingModel WALLET) gets 409 and adds money with POST /billing/topups instead.
Scope: wallet:write
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the key’s own organisation. |
amount |
number | yes | Wallet credit wanted, in major units of the organisation’s checkout currency (for example 5000 means INR 5,000 for an India-billed organisation, 100 means USD 100 for a US one). |
currency |
string | no | Optional. If sent it must equal the checkout currency for the billing country, otherwise 400. |
Example request
curl -X POST "https://api.foan.ai/api/v1/billing/topup-quote" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"organisationId":"cmf3k2j9x0001abcd1234efgh","amount":5000,"currency":"INR"}'Response 200
{ "success": true, "data": { "gateway": "razorpay", "walletCredit": { "amount": 5000, "currency": "INR" }, "walletCreditUsd": 56.74, "subtotal": { "amount": 5000, "currency": "INR" }, "platformFee": { "amount": 100, "currency": "INR" }, "platformFeeLabel": "Payment processing fee", "tax": { "amount": 918, "currency": "INR" }, "taxLabel": "GST 18%", "total": { "amount": 6018, "currency": "INR" }, "chargeCurrency": "INR", "fx": null, "rate": 1, "quoteId": "<opaque signed quote token>", "expiresAt": "2026-09-20T10:25:30.000Z", "minimumTopUp": { "amountUsd": 50, "meetsMinimum": true } }}Errors
| Code | When |
|---|---|
| 400 | organisationId missing, amount not a positive number, billing country not set, or currency does not match the billing country |
| 401 | Missing, invalid or revoked API key, or the key’s owner is deactivated or no longer in the organisation |
| 403 | Missing wallet:write scope, foreign organisation, or the key’s owner is not OWNER/ADMIN |
| 404 | Organisation not found |
| 409 | The organisation is on plan billing; use POST /billing/topups |
| 429 | Rate limited |
| 502 | A live exchange rate or processor price preview could not be obtained |
| 503 | Card payments are unavailable or misconfigured for this organisation |
Get wallet balance
Section titled “Get wallet balance”GET /wallet/{organisationId}
Returns the organisation’s pay-as-you-go wallet balance in USD. For local-currency display use displayCurrency and fxRate from GET /billing/status. For an organisation on plan billing (billingModel WALLET) this balance is not the money it spends and currency reports the plan wallet’s currency; read GET /billing/balance instead. An organisation whose AI calls are paid from a Muncho restaurant wallet gets that wallet (rupees, walletProvider muncho). Any member role may read it.
Scope: wallet:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organisationId |
path | string | yes | Your organisation ID. Must be the API key’s own organisation. |
Example request
curl -X GET "https://api.foan.ai/api/v1/wallet/{organisationId}" \ -H "Authorization: Bearer $FOAN_API_KEY"Response 200
{ "success": true, "data": { "balance": 42.5, "currency": "USD", "hasPaid": true }}Errors
| Code | When |
|---|---|
| 401 | Missing, invalid or revoked API key, or the key’s owner is deactivated or no longer in the organisation |
| 403 | Missing wallet:read scope, foreign organisation, or the key’s owner is not a member |
| 404 | No wallet exists for this organisation yet |
| 429 | Rate limited |
| 503 | Only for an organisation whose AI calls are paid from a Muncho restaurant wallet: that wallet could not be reached, or is being switched over; retry in a minute |
List wallet transactions
Section titled “List wallet transactions”GET /wallet/{organisationId}/transactions
Lists wallet ledger entries (top-ups, call and message charges, number rental, plan allowances, adjustments), newest first, with filters and pagination. Pay-as-you-go entries are in USD (amount, balanceAfter). Plan-billing entries carry unit MINOR with the real money in amountMinor and balanceMinorAfter (minor units of currency: paise for INR, cents for USD) and show 0 in the USD fields. For an organisation whose AI calls are paid from a Muncho restaurant wallet, the last 50 lines of that wallet are listed instead, in rupees. Any member role may read it.
Scope: wallet:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organisationId |
path | string | yes | Your organisation ID. Must be the API key’s own organisation. |
type |
query | string: CREDIT, DEBIT, REFUND, ADJUSTMENT | no | Filter by transaction type. |
startDate |
query | string | no | Only entries created at or after this time. |
endDate |
query | string | no | Only entries created at or before this time. |
page |
query | integer | no | Page number. |
limit |
query | integer | no | Page size (clamped to 1..100). |
Example request
curl -X GET "https://api.foan.ai/api/v1/wallet/{organisationId}/transactions" \ -H "Authorization: Bearer $FOAN_API_KEY"Response 200
{ "success": true, "data": { "transactions": [ { "id": "cmf3k2j9x0009abcd1234efgh", "type": "DEBIT", "amount": 0.095, "balanceAfter": 42.5, "referenceType": "VOICE_CALL", "referenceId": "cmf3k2j9x0010abcd1234efgh", "description": "Call cost for call-7f3a9c2e", "createdAt": "2026-09-20T09:01:36.000Z" }, { "id": "cmf3k2j9x0012abcd1234efgh", "type": "CREDIT", "amount": 56.74, "balanceAfter": 42.595, "referenceType": "TOP_UP", "referenceId": "cmf3k2j9x0008abcd1234efgh", "description": "Wallet top-up", "createdAt": "2026-09-18T12:00:00.000Z" }, { "id": "cmg2txn0000a1b2c3d4e5f6g7", "type": "DEBIT", "amount": 0, "balanceAfter": 0, "unit": "MINOR", "currency": "INR", "amountMinor": -750, "balanceMinorAfter": 411600.5, "referenceType": "VOICE_CALL", "referenceId": "cmg2use0000a1b2c3d4e5f6g7", "description": "Call call-9b1d4f7a", "createdAt": "2026-10-04T11:20:05.000Z" } ], "pagination": { "page": 1, "limit": 20, "total": 3, "totalPages": 1 } }}Errors
| Code | When |
|---|---|
| 401 | Missing, invalid or revoked API key, or the key’s owner is deactivated or no longer in the organisation |
| 403 | Missing wallet:read scope, foreign organisation, or the key’s owner is not a member |
| 429 | Rate limited |
| 500 | An invalid type value or unparseable date is not validated and fails as a server error |
| 503 | Only for an organisation whose AI calls are paid from a Muncho restaurant wallet: that wallet could not be reached, or is being switched over; retry in a minute |
Check balance covers an amount
Section titled “Check balance covers an amount”GET /wallet/{organisationId}/check-balance
Answers whether the pay-as-you-go USD wallet balance is at least the given amount. It does not look at the plan wallet: for an organisation on plan billing use GET /billing/balance. For an organisation whose AI calls are paid from a Muncho restaurant wallet the amount is compared with that wallet in rupees. Returns hasEnoughBalance false when no wallet exists. Any member role may call it.
Scope: wallet:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organisationId |
path | string | yes | Your organisation ID. Must be the API key’s own organisation. |
amount |
query | number | yes | Amount to compare against the balance, in USD (rupees for a Muncho wallet). |
Example request
curl -X GET "https://api.foan.ai/api/v1/wallet/{organisationId}/check-balance" \ -H "Authorization: Bearer $FOAN_API_KEY"Response 200
{ "success": true, "data": { "hasEnoughBalance": true, "requiredAmount": 10 }}Errors
| Code | When |
|---|---|
| 400 | amount missing |
| 401 | Missing, invalid or revoked API key, or the key’s owner is deactivated or no longer in the organisation |
| 403 | Missing wallet:read scope, foreign organisation, or the key’s owner is not a member |
| 429 | Rate limited |
| 503 | Only for an organisation whose AI calls are paid from a Muncho restaurant wallet: that wallet could not be reached, or is being switched over; retry in a minute |
Start a wallet top-up checkout
Section titled “Start a wallet top-up checkout”POST /payments/create-order
Creates a pending top-up payment from a quote obtained from POST /billing/topup-quote and returns a hosted checkout URL to hand to a person to pay. The wallet is credited in USD only after our payment processor confirms the payment. The key’s owner must be OWNER or ADMIN. Each quote can be used once. Rate limited (30 requests per minute by default). Pay-as-you-go organisations only: an organisation on plan billing (billingModel WALLET) gets 409 and adds money with POST /billing/topups instead.
Scope: wallet:write
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the key’s own organisation and the organisation the quote was issued for. |
amount |
number | yes | Must equal walletCredit.amount from the quote exactly. |
currency |
string | no | Optional. If sent must equal the organisation’s checkout currency. |
expectedTotal |
object | yes | Echo of the quote the payer was shown. Every value must match the signed quote. |
Example request
curl -X POST "https://api.foan.ai/api/v1/payments/create-order" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"organisationId":"cmf3k2j9x0001abcd1234efgh","amount":5000,"currency":"INR","expectedTotal":{"amount":6018,"currency":"INR","walletCreditUsd":56.74,"quoteId":"<opaque signed quote token>"}}'Response 201
{ "success": true, "message": "Order created successfully", "data": { "paymentId": "cmf3k2j9x0008abcd1234efgh", "gateway": "razorpay", "checkoutUrl": "https://api.foan.ai/pay/cmf3k2j9x0008abcd1234efgh?t=<signed pay token>", "razorpayOrderId": "<processor order id>", "razorpayKeyId": "<publishable checkout key>", "razorpayAmount": 601800, "razorpayCurrency": "INR", "razorpayCustomerId": "<processor customer id>", "razorpayPrefillContact": "+919876543210" }}Errors
| Code | When |
|---|---|
| 400 | organisationId or amount missing, missing or malformed expectedTotal, quote invalid/expired/edited or issued to another organisation, amount below the minimum top-up, currency mismatch, billing country not set, or invalid GSTIN on file |
| 401 | Missing, invalid or revoked API key, or the key’s owner is deactivated or no longer in the organisation |
| 403 | Missing wallet:write scope, foreign organisation, or the key’s owner is not OWNER/ADMIN |
| 404 | Organisation or user not found |
| 409 | The organisation is on plan billing (use POST /billing/topups), the total changed since the quote (re-quote), the quote was already used, or the billing country changed during pricing |
| 429 | Rate limited |
| 502 | Could not price the checkout (exchange rate or processor preview unavailable) |
| 503 | Card payments unavailable or misconfigured for this organisation |
Buy a plan wallet top-up
Section titled “Buy a plan wallet top-up”POST /billing/topups
Creates a one-off payment link that adds money to the plan wallet (organisations on plan billing, billingModel WALLET; pay-as-you-go organisations use POST /billing/topup-quote and POST /payments/create-order instead). amountMinor is the money to add, in minor units of the wallet currency: paise in India, US cents elsewhere. India: the payer is charged amountMinor plus 18% GST (chargeMinor), and the organisation’s invoice details must be on file (set on the dashboard Billing page). Elsewhere: chargeMinor equals amountMinor in USD cents and the processor adds any local tax at checkout, possibly in the payer’s local currency. Nothing is charged by this call; the money is added when the payment reaches us and stays spendable for 365 days. With a paid plan it is spent at the plan’s prices; without one at Starter prices. The minimum is the smallest of the plan’s topUpPresets (Rs 1,000 or USD 10 on the current plans). The key’s owner must be OWNER or ADMIN.
Scope: wallet:write
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | no | Optional. Defaults to the API key’s own organisation; if sent it must be that organisation, otherwise 403. |
amountMinor |
integer | yes | Money to add before tax, whole minor units (paise or cents). |
returnUrl |
string | no | Outside India only. Where the payer is sent after paying. Must be on the Foan dashboard; any other address is replaced with the dashboard Billing page. |
Example request
curl -X POST "https://api.foan.ai/api/v1/billing/topups" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"amountMinor":500000}'Response 201
{ "success": true, "data": { "rail": "RAZORPAY", "amountMinor": 500000, "currency": "INR", "chargeMinor": 590000, "checkoutUrl": "https://rzp.io/rzp/YzAbCd90", "reference": "plink_Qw2eR3tY4uI5o6" }}Errors
| Code | When |
|---|---|
| 400 | amountMinor missing, not a positive whole number, or below the minimum top-up; no billing country; the billing country is not one we can bill in; or the processor rejected the details (outside India) |
| 401 | Missing, invalid or revoked API key, or the key’s owner is deactivated or no longer in the organisation |
| 403 | Missing wallet:write scope, organisationId is not the key’s own organisation, or the key’s owner is not OWNER/ADMIN |
| 404 | Organisation not found |
| 409 | The organisation is on the earlier pay-as-you-go billing, top-ups are not on sale in its currency, or (India) invoice details are missing or invalid |
| 429 | Rate limited |
| 500 | Top-ups are not configured on this server |
| 502 | Our payment processor refused the request or did not respond. Nothing was charged. |

