Architecture
Configuration and deployment setup
Most of what FurlPay can do on a given deployment is decided by configuration: which credentials are present, which database migrations are applied and which money flags are on. This page is the map. The full variable list with comments is apps/web/.env.example.
Unset means unavailable, not simulated
503. Setting INTEGRATION_MODE=live does not make an unbuilt integration real.The three layers of gating
| Parameter | Type | Description |
|---|---|---|
| Integration moderequired | INTEGRATION_MODE | Deployment-wide. "live" permits real provider calls; anything else is mock. Production without live mode fails closed on money paths. |
| Financial environmentrequired | FURLPAY_ENV | development, staging or production. Separate from NODE_ENV, because a preview build also has NODE_ENV=production and must not be treated as a place where real money moves. |
| Money flagsrequired | *_LIVE | One switch per capability, default off. A flag reports on only when it is enabled, allowed in this environment, has every required credential, and the kill switch is not engaged. |
FURLPAY_MONEY_KILL_SWITCH disables every money capability at once and is checked before any flag. FURLPAY_FROZEN_API_PREFIXES freezes named API prefixes with a 503 without a deploy.
Money flags
| Parameter | Type | Description |
|---|---|---|
| ONCHAIN_BALANCES_LIVEoptional | can enable | Read balances from chain nodes. Read-only; allowed in development. |
| HARDWARE_WALLET_LIVEoptional | can enable | Signing with an external hardware wallet. No credentials required. |
| ONCHAIN_SETTLEMENT_LIVEoptional | needs credentials | Broadcast settlement transactions. Needs FURLPAY_KMS_PROVIDER and ARBITRUM_RPC_URL. |
| BROKERAGE_LIVEoptional | needs credentials | Route orders to the brokerage. Needs the Alpaca key pair. Off means order submission returns 503. |
| AI_TRADING_LIVEoptional | needs credentials | Agent-submitted trades within a policy. Needs the Alpaca key pair. |
| TOKENIZED_STOCKS_LIVEoptional | needs registry | Needs a human-verified token registry, which ships empty. |
| ARC_RAIL_LIVEoptional | not implemented | No Arc signer is wired and no FurlPay contract is deployed to Arc. |
| CARD_ISSUING_LIVEoptional | not implemented | No card issuer is integrated. |
| BANKING_LIVEoptional | not implemented | No banking or payment-aggregator partner is contracted. |
| ONRAMP_LIVE / OFFRAMP_LIVEoptional | not implemented | No provider capability row is live; no verified-beneficiary path. |
| CREDIT_LIVE / BILL_PAY_LIVEoptional | not implemented | No lender and no biller network. |
| REWARDS_LIVE / P2P_LIVEoptional | not implemented | No rewards ledger; peer-to-peer state is not durable. |
| RECURRING_INVESTING_LIVEoptional | not implemented | Schedules are not durable and no worker executes them. |
| ACCOUNT_SPEND_LIVEoptional | not implemented | No rail delivers value debited from an account balance. |
| DEFI_COLLATERAL_LIVE / CROSS_CHAIN_STOCKS_LIVEoptional | not implemented | No collateral risk engine; no transfer-restriction checks. |
A flag marked not implemented can never report on, whatever the environment says. Admins can see every flag and the names of any missing credentials at GET /api/ops/readiness.
Core platform
| Parameter | Type | Description |
|---|---|---|
| FURLPAY_SESSION_SECRETrequired | secret | Session signing key. Production refuses to start signing sessions without it. |
| FURLPAY_RP_ID / FURLPAY_ORIGINrequired | config | Passkey relying-party ID and origin. Never read from the request Host header. |
| SUPABASE_URL / SUPABASE_SERVICE_ROLE_KEYrequired | secret | Postgres through Supabase. The service-role key is server-only; row-level security is on with no public policies. |
| UPSTASH_REDIS_REST_URL / _TOKENrequired | secret | Durable key-value store: challenges, rate limits, webhook de-duplication, locks, operational controls. KV_REST_API_URL and _TOKEN are also honoured. |
| FURLPAY_ADMIN_EMAILrequired | config | The only account allowed to hold the admin role. |
| CRON_SECRET / FURLPAY_CRON_SECRETrequired | secret | Scheduled-job credentials. With neither set, every cron request is refused. |
| FURLPAY_DATA_PLANEoptional | config | Preview isolation. A Vercel deployment that is not production gets no database or key-value credentials unless its scope sets this to "isolated", and never a production database. Leave unset in production. |
| PUBLIC_API_ORIGIN / FURLPAY_TRUSTED_PROXY_HOPSoptional | config | Needed only behind a reverse proxy other than Vercel. |
The key-value store is a hard dependency
503 controls_unavailable rather than guessing. Treat a rail as unavailable until the status endpoint answers again.Integrations by area
| Parameter | Type | Description |
|---|---|---|
| Sign-inoptional | GOOGLE_CLIENT_ID, TWILIO_* | Google sign-in and one-time codes by SMS or email. |
| Emailoptional | RESEND_API_KEY, EMAIL_FROM | Transactional email. The sending domain must be verified. |
| Pushoptional | FURLPAY_VAPID_*, FURLPAY_FCM_SERVICE_ACCOUNT | Web push and Android push for approval prompts. |
| Identity checksoptional | PERSONA_*, SUMSUB_* | Chosen with COMPLIANCE_IDENTITY_PROVIDER. |
| Screeningoptional | CIRCLE_COMPLIANCE_API_KEY, COMPLYADVANTAGE_* | Layers on top of the built-in sanctions list. |
| Circle walletsoptional | CIRCLE_W3S_API_KEY, CIRCLE_W3S_APP_ID | Both required, or the wallet routes return 503. |
| Signingoptional | FURLPAY_KMS_PROVIDER, TURNKEY_* | Only "turnkey" is implemented. "env" is development-only and refused in production. Other values are refused by name. |
| Chain nodesoptional | *_RPC_URL | Per network, each with an optional _FALLBACK. Public endpoints are the default and are rate limited. |
| Swapsoptional | LIFI_API_KEY, LIFI_FEE | LI.FI routing. Quotes work without a key at low rate limits. |
| x402optional | FACILITATOR_EVM_ADDRESS, X402_* | Facilitator settlement. A raw private key is refused in production; signing goes through the KMS provider. |
| Rampsoptional | FURLGATEWAY_*, FURLBRIDGE_* | Coinbase and Transak. See the ramp provider page. |
| Cardsoptional | RAIN_*, FURLPAY_CARDAUTH_ENDPOINT_SECRET | Issuer adapter and authorisation webhook. Setting these does not enable card issuing. |
| Traveloptional | DUFFEL_API_KEY, TRIPADVISOR_API_KEY, GOOGLE_PLACES_API_KEY | Inventory and content. A test-mode booking key is refused in production. |
| Marketsoptional | ALPACA_* | Brokerage and market data. |
| Payments (Stripe)optional | STRIPE_* | Optional Stripe payment intents and webhook. |
| Payrolloptional | FURLPAY_ROUTER_ADDRESS_*, FURLPAY_TAX_ENCRYPTION_KEY | Router address per network and the key that seals tax records. |
| Merchant collectionoptional | SUI_GRAPHQL_URL, FURLPAY_TESTNET_COLLECTION | Sui reads, and testnet collection outside production only. |
| On-chain dataoptional | ALLIUM_*, ONCHAIN_*, USDC_INDEX_* | Ingestion and index settings for the on-chain dashboards. |
| Telegramoptional | TELEGRAM_BOT_TOKEN, TELEGRAM_WEBHOOK_SECRET | Mini App sign-in and bot webhook. |
| Community botsoptional | DISCORD_* | Market brief, price cards and member welcome jobs. |
Server-side limits
FURLPAY_MAX_SWAP_USD,FURLPAY_MAX_CCTP_USD,FURLPAY_MAX_TRANSFER_USD— per-transaction caps. Each defaults to 500 USD when unset.FURLPAY_3DS_CHALLENGE_USD— amount at which an out-of-band approval is required.FURLPAY_AGENT_HITL_THRESHOLD_USD— agent-key spends at or above this always need a person. Default 3,000 USD.SETTLEMENT_*— settlement circuit-breaker thresholds.FURLPAY_MIN_CLIENT_VERSIONS— minimum app versions allowed on money routes.
Database migrations
Migrations are numbered SQL files in supabase/migrations and are applied in order. A feature whose migration is missing reports its storage as unavailable; it does not fall back to memory in production.
| Parameter | Type | Description |
|---|---|---|
| 0001 – 0030required | platform | Accounts, durability, ledger, payments, notifications, hotels, webhook events, analytics, usernames, control-plane approvals. |
| 0031 – 0036required | platform | Function-execute revocation, on-chain ingestion, asset snapshots and index, CCTP records, USDC index. |
| 0037optional | settlement | Settlement workspace draft records. |
| 0038optional | settlement | Business onboarding state and the private document bucket. |
| 0039optional | compliance | Compliance cases. |
| 0040optional | payments | Merchant payment links, destinations, intents, receipts, events and the webhook outbox. |
| 0041optional | payments | Merchant webhook endpoints and the delivery claim function. |
| 0042optional | payroll, payments | Payroll rosters and runs, merchant pricing tiers, operator notes, reconciliation alerts. |
| 0043optional | payments | Sui collection: widens the network checks and accepts Sui addresses. |
| 0044optional | payroll | Contractor invitations, account linking, wallet-proven payout addresses. |
| 0045optional | payments | Merchant refunds. |
| 0046optional | payments | Liveness alerts for collection. |
| 0047optional | payments | Arc testnet collection, booked to separate testnet ledger accounts. |
| 0048optional | payroll | Excluding flagged recipients; journaling the payroll fee. |
| 0049optional | site | Partnership inquiries. |
| 0050optional | telegram | Telegram account links. |
Scheduled jobs
Every job authenticates with Authorization: Bearer <CRON_SECRET> or x-furlpay-cron-key: <FURLPAY_CRON_SECRET>.
| Parameter | Type | Description |
|---|---|---|
| /api/cron/reconcilerequired | daily, scheduled | Ledger reconciliation. |
| /api/cron/healthrequired | daily, scheduled | Provider and scheduler health, including a live read against the key-value store; raises alerts. |
| /api/cron/market-briefoptional | daily, scheduled | Posts the market brief to Discord and Telegram. |
| /api/cron/price-cardsoptional | daily, scheduled | Posts price cards to Discord. |
| /api/cron/discord-welcomeoptional | daily, scheduled | Greets new community members. |
| /api/cron/settlement-safetyrequired | daily, scheduled | Settlement circuit-breaker checks. |
| /api/cron/onchain-reconcileoptional | daily, scheduled | Reconciles on-chain ingestion. |
| /api/cron/merchant-paymentsoptional | every 2 minutes, scheduled | Rescans open checkouts and delivers merchant webhooks. |
| /api/cron/merchant-reconcileoptional | every 10 minutes, scheduled | Merchant and payroll reconciliation; raises alerts only. |
| /api/cron/onchain-nodeoptional | not scheduled | On-chain node ingestion. |
| /api/cron/usdc-indexoptional | not scheduled | USDC index maintenance. |
| /api/cron/ramp-maintenanceoptional | not scheduled | Expires stale ramp orders and retries partner callbacks. Needs to run every few minutes. |
| /api/cron/price-alertsoptional | not scheduled | Evaluates user price alerts. |
"Scheduled" means the job is listed in apps/web/vercel.json, which has nine entries. The others exist as endpoints and need an external scheduler. Schedules more frequent than daily depend on the hosting plan; the AWS deployment schedules the same jobs with EventBridge.
Running locally
npm install
cp apps/web/.env.example apps/web/.env.local # every value may stay blank
npm run dev # starts the web appWith nothing configured the app uses in-memory stores and development-only demo paths. Before a production build, the claims check (npm run check:claims) runs automatically and fails the build if public copy describes an unavailable capability as available.
