Guarantees
Errors
Every error has a stable code, an HTTP status, a plain-language message and a request id. Branch on the code — messages may be reworded.
422 Unprocessable Content
code— stable, machine-readable; the tables below are exhaustive.message— safe to show to a person. Never a raw RPC or database error.request_id— also in thex-request-idheader. Quote it to support.details— structured context when there is some (the field that failed, the limit that was hit, the confirmations required).
Payment errors
RECIPIENT_NOT_FOUND404- “We couldn't find that Fomo identity.”
- Check the spelling. Resolve only finds identities FomoPay knows.
RECIPIENT_UNVERIFIED422- “Unable to verify this recipient's payout wallet.”
- The recipient must finish verifying their payout wallet (/claim). Retry later with the same key.
NO_PAYOUT_DESTINATION422- “This identity hasn't set up a verified payout wallet yet.”
- Ask the recipient to make their handle FomoPay Ready.
UNSUPPORTED_ASSET422- “That asset isn't supported.”
- Use USDG or ETH (vaults: USDG only). There is no official USDC on Robinhood Chain.
UNSUPPORTED_NETWORK422- “This recipient can't receive on this network.”
- FomoPay settles on Robinhood Chain only; this recipient's wallet is elsewhere.
INSUFFICIENT_FUNDS422- “Insufficient balance.”
- The paying wallet can't cover amount + gas. Fund it and retry.
PAYMENT_LIMIT_EXCEEDED422- “This payment is over a configured limit.”
- Over a per-payment, daily, project, key or recipient limit. details names which.
DUPLICATE_PAYMENT409- “This payment was already created.”
- Same idempotency key, different body. Don't retry — use a new key for a new payout.
NETWORK_UNAVAILABLE503- “The network is unavailable right now. Try again shortly.”
- Robinhood Chain RPC is unreachable. Retry with backoff and the same key.
TRANSACTION_FAILED422- “Transfer failed.”
- The transfer reverted or failed verification. The payment is FAILED; create a new one if appropriate.
Platform errors
RECIPIENT_SUSPENDED422- “This identity can't receive payments right now.”
- Don't retry. Contact the recipient another way.
VALIDATION_FAILED400- “Some fields are invalid.”
- Fix the request. details.issues lists each field.
UNAUTHENTICATED401- “Sign in to continue.”
- Missing, malformed or revoked API key.
FORBIDDEN403- “You don't have permission to do that.”
- Not allowed — e.g. a test key against mainnet, or a role without permission.
INSUFFICIENT_SCOPE403- “This API key is missing a required scope.”
- Add the scope in details.required to the key (or use another key).
NOT_FOUND404- “Not found.”
- No such object in this project.
CONFLICT409- “Something went wrong.”
- The object changed underneath you. Re-read and retry.
IDEMPOTENCY_KEY_REUSED409- “This idempotency key was already used with a different request.”
- Same as DUPLICATE_PAYMENT for non-payment objects.
INVALID_STATE409- “This action isn't possible in the current state.”
- The action doesn't apply in the current status (e.g. canceling a submitted payment).
RATE_LIMITED429- “Too many requests. Slow down and try again.”
- Wait Retry-After seconds, then retry with the same key.
FEATURE_DISABLED403- “This feature isn't enabled.”
- That capability is switched off on this deployment.
MAINNET_DISABLED403- “Mainnet payments are not enabled yet.”
- This deployment hasn't enabled mainnet payments yet.
SIGNATURE_REJECTED422- “Wallet rejected signature.”
- The wallet user declined to sign. Nothing was sent.
BLOCKHASH_EXPIRED409- “The transaction expired before it was sent. Review and sign again.”
- The prepared transaction went stale before it was sent. Prepare it again.
VAULT_INSUFFICIENT422- “The reward vault doesn't have enough available balance.”
- Top up the vault wallet.
VAULT_ALLOWANCE_EXCEEDED422- “This payout is over the vault's approved allowance.”
- Raise the approved allowance from the vault wallet.
VAULT_NOT_ACTIVE409- “The reward vault isn't active.”
- Finish vault setup, unpause it, or re-approve the signer.
APPROVAL_REQUIRED202- “This payment needs approval before it's sent.”
- Not a failure: the payment exists and waits for project approvers.
RISK_HOLD202- “This payment is on hold for a security review.”
- Not a failure: the payment exists and is held (see hold_until).
COMPLIANCE_BLOCKED451- “This payment can't be processed.”
- Can't be processed. Don't retry.
CONFIRMATION_REQUIRED428- “Please confirm the recipient before sending.”
- Show the warning to a human, then retry with the confirmations listed in details.required.
UPSTREAM_UNAVAILABLE503- “Identity lookup is temporarily unavailable.”
- The identity provider is unreachable. Retry shortly.
INTERNAL500- “Something went wrong.”
- Our fault. Retry with the same key; quote request_id if it persists.
SDK-only errors
Raised by @fomopay/sdk when there is no usable API response. status is 0.
| Code | When |
|---|---|
NETWORK_ERROR | The API couldn't be reached after all retries (DNS, TLS, connection reset). |
TIMEOUT | No response within timeoutMs on every attempt. |
ABORTED | Your AbortSignal fired. |
INVALID_AMOUNT | The amount was rejected before sending: not a positive decimal, or more decimals than the asset has (0.1 + 0.2 for USDG). |
INVALID_RESPONSE | A 2xx response without a JSON body (usually a proxy in the way). |
WEBHOOK_SIGNATURE_INVALID | FomoPay.webhooks.verify: missing/garbled header or no matching v1 signature. |
WEBHOOK_TIMESTAMP_EXPIRED | FomoPay.webhooks.verify: the signed timestamp is outside the 5-minute window. |
Handling errors
handle.ts
Which errors are safe to retry?
429, 5xx and network failures — always with the same idempotency key. The SDK already does this for you (maxRetries, default 2, honouring Retry-After). Business errors (4xx) won't change by retrying the same request; fix the cause first.