TypeScript SDK
@fomopay/sdk — zero dependencies, typed, fetch + Web Crypto only. Node 18+, Bun, Deno, Cloudflare Workers and Vercel Edge.
Options
apiKeystringrequired- A project key. The constructor throws if it's missing or malformed, and refuses to run in a browser (opt out with
dangerouslyAllowBrowserfor local tooling only). baseUrlstringoptional- This deployment:
https://fomopay.family/v1(or setFOMOPAY_BASE_URL). Locallyhttp://localhost:6280/v1. timeoutMsnumberoptional- Per attempt. Default 30 000.
maxRetriesnumberoptional- Default 2. Retries 429, 5xx, timeouts and network errors.
backoff{ baseMs, maxMs }optional- Default 500 ms doubling to 8 s, with ±25 % jitter.
Retry-Afteris honoured up to 60 s. fetchtypeof fetchoptional- Bring your own fetch (proxies, instrumentation, tests).
headersRecord<string, string>optional- Extra headers on every request.
Per-call options
Every method takes an optional last argument { idempotencyKey, timeoutMs, maxRetries, signal, headers }.
Methods
| Method | Endpoint | Does |
|---|---|---|
send(params) | POST /v1/rewards | Pay a handle from your Reward Vault (USDG). Auto idempotency key. Alias: pay(). |
rewards.create(params) | POST /v1/rewards | Same, with your own (required) idempotencyKey, reason, metadata, eligibility. |
payments.create(params) | POST /v1/payments | Wallet checkout (USDG or ETH) or vault payment. Auto idempotency key. |
payments.get(id) | GET /v1/payments/:id | One payment; refreshes in-flight ones from the chain. |
payments.list(query) | GET /v1/payments | A page: { data, next_cursor, has_more }. |
payments.iterate(query) | GET /v1/payments … | Async iterator over every page. |
payments.wait(id, opts) | GET /v1/payments/:id … | Polls until CONFIRMED / FAILED / CANCELED (or `until`). |
resolve(handle, { asset }) | GET /v1/resolve/@handle | Identity state and payout summary. No addresses. |
profiles.payments(handle) | GET /v1/profiles/@handle/payments | Your project's payments to one identity. |
bulk.validate(params) | POST /v1/payments/bulk | Validate rows; returns the batch. Nothing is paid. |
bulk.execute(id, { onInvalid }) | POST /v1/payments/bulk/:id/execute | Pay the payable rows from the vault. |
bulk.get(id) | GET /v1/payments/bulk/:id | Batch progress and per-row results. |
vaults.list() · vaults.get(id) | GET /v1/vaults … | Balance, allowance, reserved, available. |
webhooks.create · list · get · update · delete · rotate | /v1/webhooks … | Manage endpoints; create and rotate return the secret. |
me() | GET /v1/me | The key's project, mode, scopes and rate limit. |
FomoPay.webhooks.verify(raw, headers, secret) | — | Static. Verify a delivery and return the parsed event. |
Amounts
Pass a decimal string ("100.25") or a number. Numbers are converted exactly — never rounded: 100 → "100", 1e-7 → "0.0000001". More decimals than the asset has (USDG 6, ETH 18) — like 0.1 + 0.2 for USDG — throws INVALID_AMOUNT before any request is made. Responses always carry decimal strings plus amount_base_units.
Retries and idempotency
- Retried:
408,429,500,502,503,504, timeouts, network errors. Never: other4xx. - Every retry of a create call reuses the same
Idempotency-Key, so a request that reached FomoPay before the connection dropped is replayed, not repeated. send,payments.createandbulk.*generate a key when you don't pass one;rewards.createrequires yours.- Calls without a key (webhook management) are only retried on
429.
Errors
Everything throws FomoPayError with code, status (0 when there was no response), message, requestId, details, idempotencyKey and a retryable getter. The full list is on Errors.
Types
Response types mirror the /v1 JSON exactly (snake_case) and allow unknown extra fields, so new API fields never break your build.
Runtimes
Node 18 doesn't expose Web Crypto globally: run it with --experimental-global-webcrypto, or pass your own idempotencyKey and verify webhooks on Node 20+. Node 20 and later need nothing.