Create Payment
Create a payment with POST /Payments/crypto to generate one or more hosted payment URLs. The hosted payment page handles the checkout experience for supported crypto and digital wallet tenders.
You can optionally pass tender-selection details when you want to direct the shopper into a specific tender flow or limit the hosted page to a curated tender subset. If you omit tenderTypes, Bead uses the active tender configuration for the terminal.
Overview
Endpoint:
POST /Payments/crypto
Sandbox base URL:
https://api.test.devs.beadpay.io
Use this endpoint to:
create a hosted payment session
return one or more hosted payment URLs
optionally pass order, invoice, customer, receipt, redirect, webhook, and tender-selection details
allow Bead's hosted payment page to manage the payer experience
For an end-to-end Sandbox flow, see Quick Start.
Step 1: Authenticate
Payments use header-based authentication with a terminal API key.
The terminal API key is the secret credential used to authenticate requests. The masked API key is a display value only and cannot be used to authenticate.
Required headers:
Authentication guidance:
Preferred for new integrations: use
X-Api-KeySupported for existing legacy Payments integrations: OAuth password grant with
Authorization: Bearer {access_token}Do not place the API key in the request body or URL
Do not expose the API key in browser or mobile client code
See Authentication for full token and legacy OAuth details.
Step 2: Create a hosted payment URL
Send a request to create a payment. The response includes paymentId, trackingId, and paymentUrls.
Endpoint:
Headers:
Request shape guidance
At a minimum, payment creation requests should include:
terminalIdmerchantIdrequestedAmount
Other common fields include:
referencedescriptioncartItemsredirectUrlemailReceiptsmsReceiptrefundEmailwebhookUrlscustomertenderTypes
Required fields can vary by tender type, terminal configuration, and risk or compliance requirements. Use the simplest request body that matches your flow, and add optional fields only when you need them.
Terminal type considerations
Terminals have a type of either virtual or physical.
A virtual terminal is generally used for ecommerce, hosted checkout, remote payment, or other customer-not-present flows.
A physical terminal is generally used for in-store, point-of-sale, or customer-present flows.
The terminal type helps determine which tender types and payment experiences are available, but it does not mean the customer object is always required.
Terminal type = virtual
For virtual terminals, the customer object is optional unless the selected tender, merchant configuration, risk review, or compliance flow specifically requires customer details.
For common hosted checkout flows, you can create a payment without a customer object.
Use refundEmail when you already have the payer's email and want Bead to send reclaim instructions directly if reclaim is required. If refundEmail is not provided, the hosted payment page can prompt the payer for an email address when reclaim is needed.
Terminal type = physical
For physical terminals, the request can also use the basic payment fields unless the selected tender or terminal configuration requires additional information.
Physical terminal flows may be customer-present and may not always need customer details. If your tender flow does require customer details, include the complete customer object.
Customer object rule
Do not send a partial customer object.
If you include customer, provide all required customer fields:
firstNamelastNameemailaddresscitystatepostalCodecountryCode
Optional customer fields include:
phoneaddress2birthday
If you send a partial customer object, the API may return 400 Bad Request with validation errors.
Cart items
Use cartItems when you want to pass order, invoice, or basket-level detail with the payment.
cartItems can be omitted when you do not have item-level detail. Do not send placeholder or blank cart item values.
When you include cartItems, each item should include:
id— your item, SKU, invoice line, or service identifiername— optional display name for the item; may benullquantity— quantity for the itemprice— price for the item
When possible, keep the cart item detail consistent with requestedAmount so reporting and reconciliation remain clear.
Tender type selection
Use tenderTypes when you want to limit or direct a payment to one or more specific tender types that are already available for the merchant, location, and terminal.
Passing tenderTypes on a payment request does not enable a new tender type for the merchant or terminal. It only narrows or directs the hosted payment experience to tender types that are already approved and active for that payment context.
If tenderTypes is omitted, Bead uses the available tender configuration for the terminal and hosted payment experience.
Before using payment-level tenderTypes, confirm the terminal's active tender set with GET /Terminals/{terminalId}. If a tender type is not active on the terminal, the merchant may need additional onboarding, pricing, provider enrollment, or operational support before that tender can be used.
BTC (on-chain) tender selection
BTC (on-chain) refers to Bitcoin on-chain payments. When you want to direct the hosted payment experience to BTC (on-chain), use bitcoin in the tenderTypes array.
Do not use bitcoin_Lightning for BTC (on-chain). Bitcoin Lightning is a separate BTC payment rail with a faster customer-facing payment experience.
Only present BTC (on-chain) when the merchant's payment environment can support delayed confirmation. BTC (on-chain) is usually appropriate for invoices, ecommerce orders with later shipment, service payments before pickup, deposits, and high-ticket or high-touch purchases. It is usually not appropriate for fast in-person checkout or instant digital delivery.
If a payment is limited to BTC (on-chain), make sure your integration can:
show the customer that the payment has been detected when the status reaches
processingkeep the order, invoice, or service request in a pending payment state
use webhooks or status polling to detect the final status
fulfill only after the payment reaches
completed
Minimum payment amounts
Minimum payment amounts can vary by tender.
For USDC on Base and USDC on Solana, the minimum Bead payment amount is $1.00 USD. If a payment request is limited to either of these tenders, or if the hosted payment experience is expected to present either of these tenders as an available option, use a requestedAmount of 1.00 or higher.
This minimum is separate from any network fee the payer's wallet may require to submit the transaction.
Sandbox transaction amount limit
In Sandbox, the test server may enforce a $100 maximum transaction amount. If you send a requestedAmount greater than 100.00, the API may return 400 Bad Request with a message similar to:
This is expected Sandbox behavior. Use amounts at or below 100.00 for normal happy-path testing.
You should also test your integration's error handling by intentionally sending a payment amount above the limit and confirming that your point-of-sale or checkout experience displays a clear message to the merchant.
In Production, maximum transaction limits can vary by merchant and terminal configuration and are typically based on the merchant's approval.
Example request body: basic hosted payment
Use this as the default hosted payment example.
Example request body: virtual terminal
This example is appropriate for a common virtual terminal checkout flow. The customer object is omitted because it is not required by default for virtual terminals.
Example request body: physical terminal
This example is appropriate for a common physical terminal payment flow. The customer object is omitted unless your specific tender flow or terminal configuration requires it.
Example request body: with customer details
Use this pattern only when your payment flow requires customer details or when you intentionally want to pass customer details for reporting, receipts, or operational handling.
Example request body: with cart items
Use cartItems when you want to pass line-item order context with the payment.
Example request body: tender type selection
Use tenderTypes when you want to limit the hosted payment experience to a specific tender or tender group.
Only pass tender types that are already enabled and available for the merchant, location, and terminal.
Example request body: BTC (on-chain) tender selection
Use bitcoin when you want to limit the hosted payment experience to BTC (on-chain) / Bitcoin on-chain.
BTC (on-chain) payments can remain in processing for several minutes or longer. Do not fulfill the order based on processing. Wait until the payment reaches completed.
Example request body: expanded
Notes:
cartItemscan be omitted when you do not have item-level detail.Do not send placeholder cart items with blank
id, blankquantity, or blankprice.customercan be omitted unless your selected tender, terminal configuration, or compliance flow requires it.If you include
customer, provide all required customer fields.refundEmailis recommended when you have a payer email available. It is used to send reclaim instructions if the payment ends asunderpaid,overpaid,expired,invalid, orcancelled.In Sandbox, keep normal happy-path test amounts at or below
100.00.
Example response
Key fields
terminalId — Terminal identifier in Bead. Identifies the terminal used for the payment and determines terminal-level settings such as the default webhook, terminal type, and tender types available to the hosted payment experience.
merchantId — Merchant identifier. Used for reporting and settlement.
requestedAmount — Amount in the requested currency. For USDC on Base and USDC on Solana, use 1.00 or higher. In Sandbox, the test server may enforce a maximum transaction amount of 100.00.
cartItems — Optional array of order, invoice, or basket line items associated with the payment. Use this when you want to pass item-level context for reporting, receipts, or reconciliation. If you do not have item-level detail, omit the field.
tenderTypes — Optional array used to limit or direct the payment to specific tender types. The selected tender types must already be enabled for the merchant, location, and terminal. Passing tenderTypes does not enroll the merchant into a new tender type. Use bitcoin for BTC (on-chain) / Bitcoin on-chain payments. Use bitcoin_Lightning for Bitcoin Lightning. Do not treat these as the same customer experience. When you limit a payment to BTC (on-chain), design the checkout flow for asynchronous completion: acknowledge processing, keep the order or invoice pending, and fulfill only after completed. When you limit a payment to USDC on Base or USDC on Solana, the requestedAmount must be at least 1.00.
reference — Merchant-side reference such as an order or invoice number.
description — Description that may be shown to the customer and in reporting.
customer — Optional customer object. Include it only when your selected tender, terminal configuration, compliance flow, or reporting process requires customer details. If included, provide the full required customer fields.
redirectUrl — Optional URL Bead redirects to after checkout completes or is cancelled.
emailReceipt, smsReceipt — Optional receipt delivery settings.
refundEmail — Optional email used for reclaim instructions when unconverted crypto must be returned to the payer. Recommended when you already have the payer's email.
webhookUrls — Optional array of additional webhook endpoints for this payment.
Underpayments and overpayments
In wallet-based crypto payments, the customer typically scans a QR code to set the destination and then manually enters the amount in their wallet app. If they mistype the amount, the payment can end in one of these outcomes:
underpaid — the customer sent less than the requested amount
overpaid — the customer sent more than the requested amount
Recommended handling:
Underpaid: treat as unsuccessful. Do not fulfill. The crypto sent by the customer is returned through the reclaim flow. If the customer still wants to pay, create a new payment and start a new hosted checkout.
Overpaid: treat as unsuccessful. Do not fulfill. The crypto sent by the customer is returned through the reclaim flow. If the customer still wants to pay, create a new payment and start a new hosted checkout.
Email behavior for reclaim
If you provide refundEmail, Bead emails reclaim instructions when reclaim is required.
If refundEmail is not provided, the hosted payment page prompts the payer to enter an email address when an underpaid or overpaid outcome occurs, then Bead emails reclaim instructions.
Webhooks
If you include webhookUrls, Bead sends payment lifecycle events to both:
the terminal's default webhook
each URL in the
webhookUrlsarray
Configure at least one webhook endpoint before you depend on webhooks in production. See Payment Webhooks for payload and retry details.
Webhooks are strongly recommended for BTC (on-chain) / Bitcoin on-chain payments. In those flows, the customer may leave the hosted payment page after the payment reaches processing, while the merchant still needs to know when the payment reaches completed.
Use webhooks to update the merchant order, invoice, or fulfillment workflow when the final payment status is available. Do not rely on the customer keeping a browser tab open until BTC (on-chain) completion.
Step 3: Present the hosted page
Use the first URL in paymentUrls to start checkout.
Common patterns:
Web or SPA: embed the URL in an iframe or open a new tab or window
Native app: load the URL in an in-app browser or webview
Redirect behavior:
If
redirectUrlis provided, Bead redirects the browser toredirectUrlafter success or cancel and includes context in the query string.If
redirectUrlis not provided, Bead displays a hosted confirmation page that indicates success or cancellation.
Regardless of redirect choice, always confirm the final status via webhooks or status polling before shipping goods or granting access.
For BTC (on-chain) / Bitcoin on-chain payments, the customer experience should not depend on the customer waiting on the hosted page until final completion. Once the payment is detected and the status reaches processing, show the customer that the transaction is underway and allow them to move to an order, invoice, receipt, or payment-status screen.
Continue to treat the payment as pending until it reaches completed.
Step 4: Confirm payment status
You can confirm payment status using webhooks or polling.
Option A: Webhooks
Configure a terminal-level webhook and optionally supply
webhookUrlsper paymentYour server receives JSON payloads whenever
statusCodechangesRespond with
200 OKas soon as you have persisted or queued the event
Option B: Polling
Endpoint:
Headers:
Example curl:
Use the paymentId returned from the Create Payment response.
Typical statuses:
created— the payment has been created and Bead is waiting for customer paymentprocessing— funds have been detected and the payment is underway, but the payment is not finalcompleted— the payment has fully succeeded; use this as the fulfillment triggerunderpaid,overpaid,expired,invalid, orcancelled— non-happy-path outcomes that should not be fulfilled
For BTC (on-chain) / Bitcoin on-chain payments, processing can last several minutes or longer. In those flows, tell the customer the BTC payment has been detected, let them move on from the hosted payment experience, and rely on webhooks or polling to detect completed.
Do not ship goods, release physical items, grant digital access, or mark an invoice as paid until the status is completed.
For full status details, see Payment Statuses.
Troubleshooting
401 Unauthorized
Confirm the API key is present.
Confirm the header name is exactly
X-Api-Key.Confirm you are using the correct terminal API key for the environment.
Do not use the masked API key value for authentication.
Hosted page will not load
Confirm you are using a URL from the
paymentUrlsarray returned by the create payment response.Confirm the terminal has at least one active tender type in the environment you are testing.
Use
GET /Terminals/{terminalId}to verify the terminal's currenttenderTypesarray.
Validation error on payment creation
Confirm the request body matches the tender and terminal requirements for the flow you are testing.
If testing USDC on Base or USDC on Solana, confirm
requestedAmountis at least1.00.If using
tenderTypes, confirm the selected tenders are already approved and active for the merchant, location, and terminal.Use
GET /Terminals/{terminalId}to verify the terminal's currenttenderTypesarray.If the tender is missing, it may require onboarding, pricing, provider enrollment, or operational support before it can be used.
If using
customer, confirm all required customer fields are present.If your flow does not require
customer, omit the object instead of sending a partial object.If using
refundEmail, confirm it is a valid email address.
Invalid cart item values
If you include
cartItems, confirm each item has complete values forid,quantity, andprice.Do not send blank placeholder values.
If you do not have line-item detail, omit
cartItemsfrom the request.
Payment amount exceeds terminal maximum limit
Confirm
requestedAmountdoes not exceed the terminal's configured maximum transaction amount.In Sandbox, the test server may enforce a $100 maximum transaction amount.
A Sandbox request greater than
100.00may return400 Bad Requestwith a message similar toThe requested payment amount exceeds the terminal's maximum limit.This is expected Sandbox behavior and is a good scenario to test for graceful error handling.
In Production, maximum transaction limits can vary by merchant and terminal configuration.
No status change
Confirm you are polling the correct
paymentId.If using webhooks, confirm your endpoint is reachable.
Confirm your webhook endpoint returns HTTP
200quickly after receiving an event.If the payment uses BTC (on-chain) / Bitcoin on-chain and the status is
processing, the payment may still be waiting for Bitcoin network confirmation. This can take several minutes or longer. Do not create a duplicate payment unless the original payment reaches a final unsuccessful state or the customer intentionally starts over.
Suggested checkout error handling
If the API returns a terminal maximum transaction error, show a clear message to the merchant or operator.
Example message:
In Sandbox, this can happen when testing amounts above 100.00.
Next steps
After you can create and complete a payment in Sandbox:
Configure Payment Webhooks and verify your endpoint receives status events
Use Payment Statuses for ad hoc queries and troubleshooting
Review Why do BTC (on-chain) payments take longer? for Bitcoin on-chain timing and customer messaging
Review Choosing Tender Types by Payment Environment before enabling BTC (on-chain) in physical, digital, invoice, or delayed-fulfillment flows
Explore Reporting and Settlement to build payment history and reconciliation jobs
When ready for Production, request production credentials and switch API base URLs to Production
Last updated