FurlPay Docs
Open App
  • Introduction
  • Quickstart
  • For AI Agents
  • Monorepo
  • API Routes
  • Authenticationnew
  • Webhook Eventsnew
  • Error Codesnew
  • Rate Limitsnew
  • Agentic Payments (x402)
  • Agent Trust & Mandates (TAP)
  • CCTP Cross-Chainnew
  • LI.FI Swapsnew
  • FurlPay Travels (Travel MCP)
  • Solana Actions & Blinks
  • Claude Connectornew
  • AI Assistants
  • Stripe Crypto
  • Persona KYC
  • MiCA Roadmap
  • Security Posturenew
  • MPC & WebAuthn
  • Help Centernew
  • Getting Started
  • KYC Verification
  • Passkeys & Biometrics
  • Privacy & Data Protection
  • Transaction Statuses
  • Gasless Transfers
  • Deposits & Withdrawals
  • Managing Virtual Cards
  • Freezing & Unfreezing Cards
  • Declined Transactions
  • SDK & API Support
  • x402 Monetization Basics
  • Booking Travel
  • Travel Refunds & Cancellations

Resources

  • Changelog
  • System Status
  • OpenAPI Spec
  • Community
  • GitHub
Docs/Architecture/API Routes

Architecture

API Routes

The API surface lives under apps/web/src/app/api as Next.js route handlers. Every request body is validated with zod; every mutation is written through the double-entry ledger.

Request lifecycle

Every request passes the same gauntlet before its handler runs — and money-moving routes add rail pause checks, per-transaction caps, and idempotency on top:

Rendering diagram…
One request, in order: edge middleware, session, zod validation, route guards, handler, ledger + ops events.

Authentication

MethodPathDescription
POST/api/auth/webauthnRegister / authenticate a passkey (FIDO2)
POST/api/auth/otp/startSend a phone / email OTP (Twilio Verify)
POST/api/auth/googleSign in with Google Identity Services

Cards

MethodPathDescription
POST/api/cardsIssue a 2-of-2 MPC virtual card
POST/api/cards/settingsFreeze, set limits, update controls
bash
curl -X POST https://furlpay.app/api/cards/settings \
  -H "Authorization: Bearer sk_live_…" \
  -d '{ "cardId": "card_1a2b", "frozen": true, "spendLimit": 25000 }'

Investing & market data

MethodPathDescription
POST/api/investing/orderFractional stock / ETF order (Alpaca)
GET/api/investing/portfolioHoldings, cost basis and P/L

Quotes and fundamentals are sourced from the Twelve Data API; real-time execution and price streams run over Alpaca WebSockets.

Agentic payments (x402)

Furlpay speaks x402 — the HTTP 402 "Payment Required" protocol — so AI agents and API clients pay per-request in USDC with no account or key. An unpaid request returns a signed quote; the client pays and retries with an X-PAYMENT header.

MethodPathDescription
GET/api/x402/fxPay-per-call mid-market FX quote ($0.01 USDC)
bash
# 1. unpaid request → 402 with a payment quote
curl -i https://furlpay.com/api/x402/fx?from=USD&to=EUR&amount=100

# 2. sign the EIP-3009 authorization, then retry with the payment attached
curl https://furlpay.com/api/x402/fx?from=USD&to=EUR&amount=100 \
  -H "X-PAYMENT: $(base64_payload)"   # → 200 + X-PAYMENT-RESPONSE receipt

Verification is hardened against the authorization, binding, replay, and web-layer attack classes documented in 2026 x402 research. See /docs/payments/agentic-x402 for the full flow.

USDC payments & compliance review

The unified USDC payment service settles on Arbitrum One (0.5% fee vs 1.5–3.5% card interchange). The flow is create (quote + EIP-712 sign data) → execute (signature → settle); both legs are OFAC-screened before anything touches the chain.

MethodPathDescription
POST/api/payments/createCreate a USDC payment intent (honors Idempotency-Key)
POST/api/payments/executeSubmit the EIP-712 signature and settle
GET/api/payments/:idPoll payment status
GET/api/payments/reviewHuman-in-the-loop review queue (session-gated)
POST/api/payments/reviewApprove / reject a paused payment (session-gated)

Idempotency. create honors an Idempotency-Key header — a retry with the same key returns the same intent (HTTP 200 with replayed: true) instead of minting a duplicate. This is the retry-storm guard: agents that resubmit after a dropped receipt, or fan-outs of sub-agents sharing a coordinated key, never generate a double charge. execute is idempotent too — a retry against an already-settled payment replays the original receipt rather than erroring.

Human-in-the-loop (HITL) compliance gateway. A payment that clears sanctions screening but crosses a regulatory threshold does not settle automatically — it pauses at pending_review and execute returns 202 Accepted. A payment pauses when:

  • its value is at or above FURLPAY_HITL_THRESHOLD_USD (default $3,000), or
  • either counterparty screens as elevated risk, or
  • its metadata.jurisdiction is in FURLPAY_HIGH_RISK_JURISDICTIONS.

The compliance inbox is notified, and a human officer approves or rejects via /api/payments/review (session-gated, so the decision is always attributable to a person) before funds move on-chain. Agents research and flag; humans make the final call.

bash
# create with an idempotency key (safe to retry verbatim)
curl -X POST https://furlpay.com/api/payments/create \
  -H "content-type: application/json" \
  -H "Idempotency-Key: sess_9f3c-booking-42" \
  -d '{ "amount": 5000, "recipient": "0xabc…", "metadata": { "jurisdiction": "US" } }'

# execute → 202 pending_human_review when it crosses the $3k threshold
# officer approves it:
curl -X POST https://furlpay.com/api/payments/review \
  -H "content-type: application/json" \
  --cookie "furlpay_session=…" \
  -d '{ "paymentId": "pay_…", "decision": "approve" }'

Solana Actions & Blinks

Shareable checkout links unfurl into signable payment widgets on X, Discord, and any Blink client.

MethodPathDescription
GET/actions.jsonBlink client discovery rules
GET/api/actions/pay/[orderId]Blink metadata (icon, label, buttons)
POST/api/actions/pay/[orderId]Return the base64 SOL/USDC transaction to sign

Webhooks & signature verification

MethodPathDescription
POST/api/webhooks/marqetaCard processor events
POST/api/webhooks/bridgeOn/off-ramp settlement events
POST/api/webhooks/wiseLocal bank account events

Every webhook carries a Furlpay-Signature header. Verify it against your endpoint secret before acting on the payload.

typescript
import { Furlpay } from '@furlpay/furlpay-node';

export async function POST(req: Request) {
  const raw = await req.text();
  const event = Furlpay.webhooks.constructEvent(
    raw,
    req.headers.get('furlpay-signature'),
    process.env.FURLPAY_ENDPOINT_SECRET!,
  );
  // event is now trusted — handle event.type
}

Idempotency

Send an Idempotency-Key header on POSTs so retries never double-issue a card or double-place an order.
Did this page help?
Edit this page on GitHub

← Previous

Monorepo

Next →

Authentication