API

Reward Vaults

A Reward Vault lets your code pay out without FomoPay ever holding your money. The funds stay in a wallet you control; FomoPay may only spend the allowance you approved.

The allowance model

Your vault
Project wallet / Safe

Holds the USDG. You keep custody.

approve(cap)
once, revocable
FomoPay
Payout signer

Can spend at most the remaining allowance — nothing else.

transferFrom
per payout
Recipient
L@lyra

verified payout wallet

approve(signer, 0)from the vault wallet revokes access instantly — on-chain, no permission needed.
  • Your wallet holds the funds. The vault is a project wallet — ideally a dedicated wallet or a Safe — holding USDG on Robinhood Chain. FomoPay never takes custody.
  • You approve a cap. The vault wallet calls the standard ERC-20 approve(payoutSigner, cap), with the vault's tag appended to the calldata (the dashboard and approve.data include it) — only an approval carrying it activates the vault. That cap is enforced by the USDG contract itself: the payout signer can never move more than what remains of it, and never anything other than USDG from that wallet.
  • FomoPay pays verified handles only. Each payout is transferFrom(vault, recipient, amount) to the wallet FomoPay verified for that identity, after the same resolution, snapshot and risk checks as every payment. The signer pays the gas (ETH).
  • You can stop it any time. approve(payoutSigner, 0) from the vault wallet revokes FomoPay's access on-chain, immediately, without asking anyone. You can also pause a vault in the dashboard, and withdraw is simply moving USDG out of your own wallet.
on-chain, in plain terms
// What the vault wallet signs (the dashboard prepares it for you):
USDG.approve(payoutSigner, 10_000_000000) ‖ vaultTag   // a 10,000 USDG spending cap
// (vaultTag: 32 bytes naming this vault — proof that this wallet backs it; USDG ignores trailing bytes)

// What FomoPay's payout signer sends for each reward:
USDG.transferFrom(vaultWallet, lyraVerifiedWallet, 500_000000) ‖ paymentReference

// Revoke at any time, from the vault wallet:
USDG.approve(payoutSigner, 0)
USDG only

Vaults pay USDG (Global Dollar, 6 decimals, 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168). ETH has no ERC-20 allowance, so it can't be delegated this way — ETH payments go through a wallet payment that a person signs. There is no official USDC on Robinhood Chain.

Limits on top of the allowance

ControlWhere it's enforcedWhat happens
Allowance capOn-chain (USDG contract)VAULT_ALLOWANCE_EXCEEDED before a payment is created; the chain would refuse it anyway.
BalanceOn-chainVAULT_INSUFFICIENT when the wallet's balance, minus payouts in flight, can't cover it.
Per-payout limitFomoPayPAYMENT_LIMIT_EXCEEDED (details.limit: "PER_PAYOUT").
Daily limitFomoPay (UTC day)PAYMENT_LIMIT_EXCEEDED (details.limit: "DAILY").
Approval thresholdFomoPayLarger payouts wait in PENDING_APPROVAL until a project Finance/Admin approves.
Low-balance alertFomoPayvault.low_balance webhook when available funds drop below your threshold.

Set up a vault

  1. Open Dashboard → Vaults and create a vault: pick the wallet or Safe that will hold the funds, a spending cap and optional limits.
  2. Approve the payout signer from that wallet (one transaction the dashboard prepares; nothing else is ever requested from it — never a private key or seed phrase).
  3. Send USDG to the vault wallet. Deposits are detected on-chain; the vault becomes ACTIVE once the allowance is confirmed.
  4. Pay from code with rewards.create or fomopay.send.

List vaults

GET/v1/vaultsscope vaults:read
const { data: vaults } = await fp.vaults.list();

for (const v of vaults) {
  console.log(v.name, v.status, `${v.available} USDG available`);
}

Retrieve a vault

GET/v1/vaults/{vault_id}scope vaults:read
TypeScript
const vault = await fp.vaults.get("vault_3n5p7r9t1v3x5z7b9d1f");

// Pay only what the vault can actually cover right now
if (Number(vault.available) < 500) await notifyFinance(vault);
200 OK
{
  "vault_id": "vault_3n5p7r9t1v3x5z7b9d1f",
  "object": "vault",
  "name": "FomoRun prizes",
  "status": "ACTIVE",
  "kind": "safe",
  "asset": "USDG",
  "network": "robinhood",
  "chain_id": 4663,
  "vault_address": "0x80DA3A104f069fABfC294923047971551B0c2725",
  "spender": "0xBBD5D6bD1c1eE79B500040995067d0Ca65998953",
  "balance": "8420.5",
  "allowance": "6000",
  "allowance_cap": "10000",
  "reserved": "500",
  "available": "5500",
  "paid_this_month": "4000",
  "payouts_this_month": 9,
  "limits": { "per_payout": "1000", "daily": "5000", "approval_threshold": "750", "low_balance_threshold": "1000" },
  "low_balance": false,
  "synced_at": "2026-10-12T18:03:58.000Z",
  "stale": false,
  "created_at": "2026-10-01T09:12:00.000Z",
  "activated_at": "2026-10-01T09:14:31.000Z"
}
FieldMeaning
balanceUSDG held by the vault wallet.
allowanceWhat remains of the approved spending cap — the most FomoPay can ever move.
allowance_capThe cap you last approved.
reservedPayouts created but not yet confirmed.
availablemin(balance, allowance) − reserved, never below 0. What new payouts can draw on.
limitsper_payout, daily, approval_threshold and low_balance_threshold (null = not set).
statusPENDING_SETUP (approval not confirmed yet) · ACTIVE · PAUSED · REVOKED (allowance set to 0).
staletrue when Robinhood Chain couldn't be read and the numbers are from the last sync (synced_at).

Amounts are decimal strings in USDG. Balances are read from Robinhood Chain; if the chain can't be read, payouts are refused with NETWORK_UNAVAILABLE rather than guessed.