Agentic Pay
Firmly acts as a Network Token Router for agentic commerce. Destinations integrate once with Firmly’s APIs and the Firmly iFrame — Firmly routes tokenization, cardholder authentication, and payment instructions to the appropriate card network. Adding a new network is a Firmly-side change with no destination code impact.
Two Ways to Pay
Enrollment happens once per cardholder. After a card is enrolled and verified, the destination stores the resulting virtual_card_id and reuses it for every future payment — no PAN re-entry and no repeat verification. That single enrolled card powers both payment models below; the difference is only who approves the payment at transaction time.
The mandate: a cardholder’s delegated authority
Every payment intent carries a mandate — the cardholder’s authorization describing what the agent may charge. In the human-in-the-loop model the cardholder also approves it live; in the autonomous model the mandate is the guardrail that lets the agent act without a human present:
| Limit | Mandate field | What it bounds |
|---|---|---|
| Maximum amount | amount (+ currency_code) |
The order-completion capture amount must not exceed this. Firmly rejects a higher capture before the network is called. |
| Expiration | ttl_seconds |
How long the authorization stays valid. Once it lapses, a fresh intent is required. |
| Purchase intent / scope | merchant_name, merchant_category_code, quantity, consumer_prompt |
What the agent is authorized to buy, from whom, and the prompt the cardholder approved. |
How It Works
Agentic Pay breaks the payment flow into three phases: Enrollment, Intent, and Order Completion.
Phase 1: Card Enrollment
The destination sends the cardholder’s PAN. Firmly inspects the BIN, routes to the correct network, and returns abstract verification challenges. The destination drives the cardholder through the challenges without needing to know which network is involved.
After enrollment, the destination receives a virtual_card_id and masked card details (last4, expiry, brand). The flow_token carries all state between steps — no server-side session storage is needed.
Destinations should persist virtual_card_id, card_art, and masked for returning buyers. A stored card can start a new payment via /select-card without re-entering card details — it returns the same shape as /enroll, so the intent and order chain is reused unchanged.
Phase 2: Payment Intent
When it’s time to pay, the destination creates a payment intent carrying the mandate (amount, expiry, merchant scope — see The mandate). The mandate is the cardholder’s authorization for the agent to act on their behalf. In the human-in-the-loop model a FIDO assertion in the cardholder’s browser proves presence; in the autonomous model the agent transacts within the previously authorized mandate limits.
Phase 3: Order Completion
The destination sends the flow_token and intent_id to place the order. Firmly retrieves network token credentials and completes the payment.
Key Concepts
Flow Token
The flow_token is an encrypted token that carries the entire enrollment and payment state. It is:
- Stateless — no server-side session; all state lives in the token
- Refreshed on every API call — each response returns a new
flow_tokenthat must be used for the next call - Tamper-proof — encrypted and integrity-checked; any modification invalidates it
- Scoped — contains destination ID, network, enrollment status, and expiry
- Short-lived — expires 30 minutes after issue. An expired or out-of-sequence token returns
BadRequest(flow_token expired/Invalid flow_token format) — restart the flow from/enrollor/select-card.
BIN Routing
Firmly automatically detects the card network from the PAN’s BIN (Bank Identification Number):
| Network | BIN Range | Status |
|---|---|---|
| Visa | 4xxx | Supported |
| Mastercard | 5xxx, 222100–272099 | Supported |
| Discover | 6011, 65xx, 644000–649999 | Supported — no cardholder verification step (see below) |
| Amex | 34xx, 37xx | Planned |
If a network is not supported or not enabled for the destination, the API returns PaymentMethodNotAvailable. Networks are enabled per destination — confirm with Firmly which networks your API token can transact on.
Discover has no cardholder-verification ceremony. /enroll returns an empty verification_methods array and the card is immediately enrolled, and /intent returns the intent_id directly instead of a challenge — there is no /intent/challenge call. Mastercard can also skip the intent challenge when the cardholder authenticated moments earlier at enrollment (see Create Intent).
Verification Methods
The API returns verification_methods — an abstract challenge contract. Destinations see only the challenge type; network-specific details are encapsulated by Firmly:
| Type | Description |
|---|---|
embed_iframe_handshake |
Iframe that captures a secure_token via postMessage |
otp |
One-time passcode sent via SMS or email |
embed_iframe |
FIDO2 registration or assertion via network-hosted iframe |
New networks can plug in without destinations changing code — Firmly maps each network’s auth flow into one of the abstract challenge types.
Card Art
Every network returns card_art metadata for rendering the card visually, but the exact fields differ by network. Firmly proxies the image through a signed URL so destinations can render card images without CORS issues.
| Field | Visa | Mastercard | Discover |
|---|---|---|---|
network |
✅ | ✅ | ✅ |
url |
✅ | ✅ | ✅ |
background_color |
✅ | — | ✅ |
foreground_color |
✅ | — | ✅ |
descriptor (issuer name) |
— | ✅ | — |
{"card_art": {"network": "mastercard","url": "https://api.firmly.work/api/v1/wallets/agentic-pay/card-art/<token>","descriptor": "Test Bank 2"}}
The example above is a Mastercard card, so it carries descriptor and no color fields. A Visa or Discover card_art carries background_color and foreground_color instead of descriptor.
The url is HMAC-signed — no authentication header is needed. Drop it directly into .
Masked Card
Alongside card art, enrollment returns masked — display-only funding-card details for rendering a saved-card chip (e.g. “Test Bank 2 •••• 4595 · DEBIT”). It is a sibling of card_art (different source). Like card_art, its fields are network-dependent: Mastercard returns the full set (last4, exp_month, exp_year, brand, card_type, issuer_name); Visa returns last4, exp_month, exp_year, brand only (no card_type/issuer_name); Discover returns last4 and brand only.
{"masked": {"last4": "4595","exp_month": "12","exp_year": "2030","brand": "mastercard","card_type": "DEBIT","issuer_name": "Test Bank 2"}}
Card Metadata Availability
Capture card_art and masked at enrollment. They are returned only on /enroll, never on /select-card — persist them with the virtual_card_id and reuse them on every later payment and in the order UI.
| Source | card_art |
masked |
Notes |
|---|---|---|---|
/enroll (new card) |
✅ returned | ✅ returned | Persist both for reuse |
/select-card (saved card) |
null |
null |
Reuse the values persisted at enroll |
Order payment_summary |
✅ when enrolled this session | ✅ when enrolled this session | Carried through from /enroll → Vault → order; absent on the select-card path |
Order Completion Auth
The enrollment and intent endpoints use App Authentication (the destination API token). Order completion (/wallet-complete-order) is authenticated with a device session token, which can be obtained either way:
- Browser-session JWT — for browser-present checkout (human-in-the-loop).
- Server-to-server auth — for backend or autonomous-agent contexts where no browser is involved.
Next Steps
- Agentic Pay Integration Guide — Step-by-step implementation
- Headless SDK — Browser SDK that wraps enrollment, the auth iframes, and order completion
- Enroll Endpoint — Start card enrollment
- Wallet Complete Order — Place the final order