Furlpay Docs
Open App

Architecture

Authentication

Three ways in, depending on who is calling: passkey sessions for humans, signed requests for registered agents, and x402 payments for anonymous agents — where the payment itself is the authentication.

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.

MethodPathDescription
POST/api/auth/webauthnRegister / authenticate a passkey (FIDO2)
POST/api/auth/otp/startPhone / email OTP
POST/api/auth/googleSign in with Google Identity Services
POST/api/auth/appleSign in with Apple
POST/api/auth/guestDemo 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 Host header).
  • Privileged operations require step-up TOTP; MFA state is only writable through /api/security/mfa.
  • The admin role is pinned to a single founder account in production, and guest sessions can never hold it.
  • A locked account returns 423 Locked on 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), and the policy — per-transaction limit, daily budget, category allowlist — is enforced server-side on every spend. See Agent Trust & Mandates.

Anonymous agents: payment is the auth

Pay-per-call endpoints need no account and no API key. The signed EIP-3009 authorization in the X-PAYMENT header proves control of the paying wallet cryptographically — a stronger claim than a bearer key, scoped to exactly one payment:

bash
curl "https://furlpay.com/api/x402/fx?from=USD&to=EUR&amount=100"
# -> 402 with a quote. Sign it, retry with X-PAYMENT, get the resource.

API keys

The SDK quickstart uses sk_test_… keys against the sandbox. Production API-key issuance ships with the developer dashboard; server-side sessions and x402 cover every current production surface.

Never send credentials in query strings

Session cookies, signatures, and payment headers all travel in headers or cookies. Query-string secrets end up in access logs and referrer headers — requests shaped that way are rejected.
Did this page help?
Edit this page on GitHub