> For the complete documentation index, see [llms.txt](https://developers.bead.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developers.bead.xyz/reference-guide/payment-flows/reading-tender-availability-at-runtime.md).

# 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](/entity-management/terminal-management/managing-tender-types.md).

#### 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](/entity-management/terminal-management/managing-tender-types.md) 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](/reference-guide/payment-scenarios/under-and-over-payment-handling.md): 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](/reference-guide/payment-flows/choosing-tender-types-by-payment-environment.md)).

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.

#### Recommended pattern summary

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.

#### Related pages

* [Managing Tender Types](/entity-management/terminal-management/managing-tender-types.md)
* [Choosing Tender Types by Payment Environment](/reference-guide/payment-flows/choosing-tender-types-by-payment-environment.md)
* [Under and Over Payment Handling](/reference-guide/payment-scenarios/under-and-over-payment-handling.md)
* [Get Terminal](/entity-management/terminal-management/get-terminal.md)
* [Payment Webhooks](/payments/payment-webhooks.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://developers.bead.xyz/reference-guide/payment-flows/reading-tender-availability-at-runtime.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
