Architecture
Authentication
Human sessions use account authentication, registered agents can use owned signing keys and policies, and MCP clients use scoped authorization. The x402 protocol is public, but FurlPay's endpoint currently returns 503 and production settlement is gated separately.
Human sessions
Sign-in mints a short-lived signed JWT delivered as an HttpOnly cookie. The middleware verifies the signature (not just cookie presence) on every request; roles are then re-resolved from the user record — never trusted from the token alone — so a role change takes effect immediately.
| Method | Path | Description |
|---|---|---|
| POST | /api/auth/webauthn | Register / authenticate a passkey (FIDO2) |
| POST | /api/auth/otp/start | Phone / email OTP |
| POST | /api/auth/google | Sign in with Google Identity Services |
| POST | /api/auth/apple | Sign in with Apple |
| POST | /api/auth/guest | Demo guest session (never admin in production) |
- Passkeys are the primary factor — phishing-resistant, bound to the registrable domain (RP ID from server config, never the
Hostheader). - Privileged operations require step-up TOTP; MFA state is only writable through
/api/security/mfa. - The
adminrole is pinned to a single founder account in production, and guest sessions can never hold it. - A locked account returns
423 Lockedon every authenticated route until unlocked through the recovery flow.
Registered agents: signed requests
An agent registered via POST /api/agents/keys holds its own Ed25519 key bound to your account with a spend policy. Requests are signed with RFC 9421 HTTP Message Signatures (Signature-Input + Content-Digest). FurlPay endpoints that use agent spend authorization can enforce the key's per-transaction limit, daily budget and category allowlist. See Agent Trust & Mandates.
Public x402 endpoint status
The x402 protocol can issue a payment challenge without an account or API key. FurlPay's x402 endpoint returned 503 controls_unavailable with reason plan_limiton 11 October 2026. If the endpoint becomes available, a challenge still does not guarantee settlement or delivery of a paid resource:
curl "https://furlpay.com/api/x402/fx?from=USD&to=EUR&amount=100"
# current production response: 503 when rail controls are unavailable
# do not assume a 402 challenge or successful settlementAPI keys
API-key formats and authentication requirements vary by route. A developer credential does not enable a disabled payment rail and should not be assumed to replace a user session where the route requires one. Keep secret credentials server-side and follow the authentication contract for the endpoint you call.
Never send credentials in query strings
