Auth model
Two distinct authentication paths live in BabyFX Pay. This page explains both, when each is used, and what each protects.
At a glance
| Path | Who | What it protects |
|---|---|---|
Bearer api_key |
Merchants calling pay/api |
Invoice creation, merchant configuration |
| HMAC-SHA256 signature | pay/webhooks calling merchant |
Webhook authenticity |
| JWT (internal) | pay/api ? pay/settlement ? pay/sweep |
Internal RPC between pay services |
| No auth (open) | Public read endpoints | Read-only data, rate-limited |
1. Merchant ? pay/api (Bearer api_key)
Every state-changing call from a merchant carries Authorization: Bearer bfx_....
POST /v1/invoices
Authorization: Bearer bfx_0123456789abcdef0123456789abcdef0123456789abcdef
Content-Type: application/json
Idempotency-Key: pos-2026-06-22-1234
{ "fiat_amount": "4.50", "fiat_currency": "USD" }
Server-side
- Parse
Authorizationheader. - Look up the
api_key_hash(Argon2id) in themerchantstable. - Constant-time compare.
- On match, attach the
merchantto the request context. - On miss, return
401with a generic message (no oracle for "valid key prefix").
Rotation
api_key is shown once at merchant creation and never again. To rotate:
GET /v1/merchantto find the merchant id.DELETE /v1/merchant(soft-delete) to invalidate the old key.POST /v1/merchantsagain to get a new key.
The webhook_secret is rotated the same way.
2. pay/webhooks ? Merchant (HMAC-SHA256)
Webhooks are signed with the merchant''s webhook_secret. The signed string is ${timestamp}.${rawBody}. See Webhooks for the full spec and verification code.
3. pay/* internal (JWT)
The three pay/ services (api, settlement, sweep, webhooks) talk to each other over an internal HTTP bus. Calls carry a short-lived JWT (15 min) signed with PAY_INTERNAL_JWT_SECRET. The bus is not exposed to the public internet.
4. Public read endpoints
GET /v1/invoices/{id}?lookup_token=... is open, gated only by possession of a 128-bit lookup_token returned at invoice creation. This is how a customer-facing page (no login) can poll invoice state.
lookup_token as a secret. It is the only credential required to read the invoice. Do not log it. Do not put it in URLs that will be cached or shared.