For the complete documentation index, see llms.txt. This page is also available as Markdown.

Reading Tender Availability at Runtime

tenderTypes on a terminal is not static. It changes when a merchant is approved for an additional tender, and it changes when a channel's configuration is narrowed — for example, disabling BTC (on-chain) at a physical lane per Bead's standing recommendation for fast in-person checkout. An integration that hardcodes its tender list will work in testing and then quietly drift from what the terminal actually supports.

This page covers the runtime side of that problem: what a checkout or POS integration should read to decide which tender buttons to show a consumer right now, how often to read it, and what to do when a consumer picks a tender that turns out not to be available. It assumes the terminal is already configured correctly — for how tender enablement flows from onboarding through partner, merchant, location, and terminal configuration, see Managing Tender Types.

The problem this solves

A partner that renders tender options from a fixed list in code — rather than from what the terminal reports — ships something that works at launch and breaks silently later:

  • A merchant is approved for a new tender. The terminal now reports it as available, but the UI never offers it because the option isn't in the hardcoded list.

  • BTC (on-chain) is disabled at a physical lane. The terminal stops reporting it, but the UI still shows the button. A consumer selects it, the payment fails, the merchant calls the partner, the partner calls Bead — and nothing in anyone's logs looks wrong, because the terminal was configured correctly the whole time.

Bead recommends that partners implement support for all tender types up front, even ones disabled by default, specifically so a tender can be turned on for a merchant without new development. That recommendation only holds if the integration reads enablement instead of hardcoding it. This page is the pattern that makes it true.

Read tenderTypes as a rendering input, not just a diagnostic

GET /Terminals/{terminalId} returns the terminal's current tenderTypes array. Managing Tender Types frames this endpoint as a verification and troubleshooting tool — check before creating payments, check when a tender isn't appearing. That framing is correct, but incomplete: for a checkout or POS surface, the same array is also the authoritative input for what to render.

Treat it as a lookup, not a one-time constant: when it's time to build the tender selection screen for a given terminal, use the terminal's current tenderTypes to decide which buttons appear. Don't decide that once at build time and bake it into the client.

When to read it

Read tenderTypes at the start of a session or terminal context — for example, when a POS lane initializes, when a checkout page loads for a given terminal, or when your backend first resolves which terminal a transaction will run against. Cache the result for that context.

A sensible refresh cadence is a cache TTL measured in hours, not minutes. There is currently no webhook or event that fires when a terminal's tender configuration changes — Bead's webhook events cover payment status transitions, not configuration changes — so there is no push signal to build against, and periodic re-read is the correct mechanism, not a workaround. If Bead introduces a configuration-change event or webhook in the future, prefer it over polling; this page will be updated to point to it.

Do not call GET /Terminals/{terminalId} on every transaction. Tender configuration changes rarely — on the order of onboarding events, not per-payment — and a call per transaction adds a round trip to every payment for data that is almost always unchanged. Partners will reasonably object to that cost, and it buys little: a cache measured in hours already keeps the UI current within a business-reasonable window.

Cache invalidation on failure

Re-read tenderTypes immediately after any tender-related payment failure — specifically, a failure indicating the selected tender isn't available for that terminal. This lets a stale cache self-correct on the next attempt rather than failing the same way repeatedly. A consumer who hits a stale "available" tender should see the corrected list on their very next try, not wait out the TTL.

The cache is for rendering, never for gating

This is the constraint that matters most: a cached tender list decides what buttons to show. It never decides whether a payment is allowed to proceed. The live payment response is always authoritative.

If a consumer selects a tender your cache says is available and the payment request comes back indicating otherwise, trust the live response and handle it as a graceful failure (below) — don't retry against the cache, and don't surface the discrepancy as if it were a client bug. A partner that trusts a cached list over an actual failure response has built something worse than a partner that never cached at all, because it will actively argue with the API instead of just being slow to update.

Graceful failure when a tender isn't available

When a consumer attempts a tender that the live payment response indicates isn't available for that terminal:

  • Show a clear, plain-language message — not a raw API error code or message.

  • Return the consumer to tender selection rather than leaving them on a dead-end screen.

  • Trigger a cache refresh (see above) so the next render reflects current availability.

This is the same recovery shape used for Under and Over Payment Handling: the original payment attempt doesn't complete, the consumer is not left stranded, and the path forward is a fresh attempt rather than trying to repair the one that failed. If you've already built that recovery flow for under/overpayment, you can reuse it here rather than building a second one.

Design for change, not for a fixed set

A terminal's tender set reflects current approval and configuration, not a permanent contract. It changes when:

  • a merchant is approved for an additional tender type, or

  • a channel's tender configuration is narrowed (for example, disabling BTC (on-chain) at a fast in-person checkout lane, per the guidance in Choosing Tender Types by Payment Environment).

Design the integration around the terminal's tender set as something that can change, not something fixed at boarding time. Concretely, that means driving the tender selection UI from a periodically refreshed read of tenderTypes rather than from a value chosen once and hardcoded.

Is this required?

No. Bead recommends reading tender availability rather than hardcoding it, because it's the pattern that lets a merchant's tender changes take effect without a partner code change. It is not mandatory. A partner that hardcodes its tender list and instead invests in clean failure handling — a clear message, a return to tender selection, no raw error, no dead end — has built an acceptable integration. What matters is that a disabled or newly available tender never produces a confusing dead end for the consumer. Reading tenderTypes is the simplest way to get there; solid failure handling on a fixed list is the fallback for partners who choose not to.

  1. Read GET /Terminals/{terminalId} at session or terminal-context start.

  2. Cache tenderTypes for that context, with a TTL measured in hours.

  3. Render tender options from the cache.

  4. On any tender-related payment failure, treat the live response as authoritative, show a clear message, return the consumer to tender selection, and refresh the cache.

  5. Never let a cached "available" override what a live payment response actually says.

Last updated