> 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/quick-start.md).

# Quick Start

This guide walks you through creating a crypto payment, launching the hosted payment page, and confirming the final outcome. It also includes the required behavior for underpayments and overpayments.

#### What you will build

* Create a hosted crypto payment session
* Present the hosted payment page to a customer
* Confirm payment completion using webhooks or status polling
* Handle `underpaid` and `overpaid` outcomes correctly
* Understand the reclaim flow for any unconverted crypto

#### Prerequisites

You will need:

* A sandbox terminal API key, which is the secret `apiKey`, not a masked value
* A sandbox `merchantId` and `terminalId`
* A server or tool capable of making HTTPS requests, such as curl, Postman, or your backend
* A compatible crypto wallet funded with the real asset and real network fee token required for the tender you are testing

Optional but recommended:

* A webhook endpoint for payment status updates

Bead Sandbox crypto payments use live blockchain networks. Test payments require real assets and real network fee tokens. Keep test amounts small.

For USDC on Base and USDC on Solana, the minimum Bead payment amount is $1.00 USD. This minimum is separate from any live-network fee the payer's wallet may require to submit the transaction, such as ETH on Base or SOL on Solana.

#### Environment and base URLs

Common sandbox base URL:

* `https://api.test.devs.beadpay.io`

Payments endpoints used in this quick start:

* `POST /Payments/crypto`
* `GET /Payments/{id}/tracking`

Authentication used in this quick start:

* `X-Api-Key: {apiKey}`

Sandbox crypto payments use live blockchain networks. Sandbox changes the Bead API environment, merchant configuration, credentials, and hosted payment environment. It does not mean crypto payments are sent over blockchain testnets.

#### Step 1: Create a payment

Create a hosted payment session and receive a `paymentId` plus one or more hosted `paymentUrls`.

For USDC on Base and USDC on Solana, create payments with `requestedAmount` of `1.00` or higher.

**Request**

```http
POST https://api.test.devs.beadpay.io/Payments/crypto
```

Headers:

```http
X-Api-Key: {apiKey}
Content-Type: application/json
Accept: application/json
```

Minimal body:

```json
{
  "terminalId": "TERM-123",
  "merchantId": "MERCH-456",
  "requestedAmount": 100.00
}
```

For USDC on Base and USDC on Solana, the smallest valid Bead payment request is:

```json
{
  "terminalId": "TERM-123",
  "merchantId": "MERCH-456",
  "requestedAmount": 1.00
}
```

Recommended additions:

* `refundEmail`: If you have an email address for the payer, include it. This is the email Bead uses to send reclaim instructions when reclaim is required.
* `redirectUrl`: If you want the hosted experience to return the shopper to your confirmation page.
* `webhookUrls`: If you want webhooks for this payment in addition to any terminal level webhook configuration.

Example expanded body:

```json
{
  "terminalId": "TERM-123",
  "merchantId": "MERCH-456",
  "requestedAmount": 100.00,
  "reference": "ORDER-0001",
  "description": "Sample order",
  "refundEmail": "customer@example.com",
  "redirectUrl": "https://merchant.example.com/payment-return",
  "webhookUrls": [
    "https://merchant.example.com/webhooks/payments"
  ]
}
```

**Example curl**

```bash
curl -s -X POST "https://api.test.devs.beadpay.io/Payments/crypto" \
  -H "X-Api-Key: {apiKey}" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "terminalId": "TERM-123",
    "merchantId": "MERCH-456",
    "requestedAmount": 100.00,
    "reference": "ORDER-0001",
    "refundEmail": "customer@example.com"
  }'
```

Example USDC test payment curl:

```bash
curl -s -X POST "https://api.test.devs.beadpay.io/Payments/crypto" \
  -H "X-Api-Key: {apiKey}" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "terminalId": "TERM-123",
    "merchantId": "MERCH-456",
    "requestedAmount": 1.00,
    "reference": "ORDER-0001",
    "refundEmail": "customer@example.com",
    "tenderTypes": ["usdcBase"]
  }'
```

**Response**

You will receive a response similar to:

```json
{
  "paymentId": "pay_c10b29e3c8104e0f8dc139c20d9eeb6c",
  "trackingId": "c10b29e3c8104e0f8dc139c20d9eeb6c",
  "paymentUrls": [
    "https://pay.qa.beadpay.io/crypto?paymentPageId=a12b34c56d789e01"
  ]
}
```

Save:

* `paymentId`: your primary lookup key for status checks, webhooks, reporting, and support
* `paymentUrls[0]`: the hosted payment page URL to present to the customer

#### Step 2: Present the hosted payment page

Open the hosted page URL from `paymentUrls`.

Common options:

* Web: open in a new tab or window, or embed if allowed in your flow
* Mobile: open in an in-app browser or webview
* POS or terminal assisted: display the hosted page on a customer facing device

What the customer will do in a typical crypto wallet flow:

1. Select an asset, such as BTC or USDC
2. View a QR code or payment details
3. Open their wallet app and scan the QR code
4. Manually enter the crypto amount in their wallet app
5. Submit the transfer

Important: because the wallet requires the payer to enter an amount manually, miskeyed amounts can result in `underpaid` or `overpaid`.

The payer's wallet must hold the correct asset on the correct live network. For example, USDC on Base requires USDC on Base for the payment amount and ETH on Base for network fees. USDC on Solana requires USDC on Solana for the payment amount and SOL for network fees.

#### Step 3: Confirm the final result

You should always confirm the final status before fulfilling an order. Use either webhooks or status polling.

**Option A: Webhooks, recommended**

If you configured webhooks, either terminal level or per payment `webhookUrls`, your server will receive events when the payment status changes.

Recommended handling:

* Verify the webhook signature if your configuration supports it
* Persist the event quickly
* Return `200 OK` promptly
* Treat the webhook as the primary status signal

**Option B: Polling**

Use the tracking endpoint with the `paymentId` until the payment reaches a terminal status.

Request:

```http
GET https://api.test.devs.beadpay.io/Payments/{paymentId}/tracking
```

Example curl:

```bash
curl -s -X GET "https://api.test.devs.beadpay.io/Payments/{paymentId}/tracking" \
  -H "X-Api-Key: {apiKey}" \
  -H "Accept: application/json"
```

Recommended polling behavior:

* Use a reasonable interval, such as 2 seconds, during an active checkout
* Stop polling once a terminal status is returned
* Prefer webhooks for production scale

#### Step 4: Handle status outcomes

The payment status is returned in `statusCode`.

**Non terminal statuses**

| statusCode   | Meaning                                      |
| ------------ | -------------------------------------------- |
| `created`    | Payment created, waiting for customer action |
| `processing` | Funds detected and processing is in progress |

**Terminal statuses**

| statusCode  | Meaning                                                | What you should do                              |
| ----------- | ------------------------------------------------------ | ----------------------------------------------- |
| `completed` | Customer paid the requested amount                     | Fulfill and mark as paid                        |
| `underpaid` | Customer sent less than requested                      | Treat as not successful payment, do not fulfill |
| `overpaid`  | Customer sent more than requested                      | Treat as not successful payment, do not fulfill |
| `expired`   | Payment window ended without a valid completion        | Treat as not successful payment, do not fulfill |
| `invalid`   | Irregular condition, funds not eligible for conversion | Treat as not successful payment, do not fulfill |
| `cancelled` | Payment cancelled                                      | Treat as not successful payment, do not fulfill |

#### Underpayments and overpayments

Underpayments and overpayments occur because the payer manually enters the crypto amount in their wallet app.

**Underpaid**

Definition: the payer sent less than the requested amount.

Integrator behavior:

* Treat as not successful payment
* Do not fulfill the order
* If the customer still wants to pay, create a new payment and start a new hosted checkout session

What Bead does:

* The crypto received remains unconverted
* The full amount received is returned to the payer through the reclaim process

**Overpaid**

Definition: the payer sent more than the requested amount.

Integrator behavior:

* Treat as not successful payment
* Do not fulfill the order
* If the customer still wants to pay, create a new payment and start a new hosted checkout session

What Bead does:

* The crypto received remains unconverted
* The full amount received is returned to the payer through the reclaim process

#### Reclaiming unconverted crypto

Reclaim is how Bead returns any unconverted crypto to the payer.

Reclaim can occur when the payment is:

* `underpaid`
* `overpaid`
* `expired`
* `invalid`
* `cancelled`

**Email behavior for reclaim**

* If you provided `refundEmail` in the Create Payment request, Bead emails reclaim instructions to that address when reclaim is required.
* If `refundEmail` was not provided, the hosted payment page prompts the payer to enter an email address in underpaid and overpaid outcomes so Bead can send reclaim instructions.

Recommended best practice:

* Provide `refundEmail` whenever you have it, especially for digital and virtual checkout flows.

#### Troubleshooting

**401 Unauthorized**

Common causes:

* Missing `X-Api-Key` header
* Using a masked value instead of the real `apiKey`
* Using a key from the wrong environment

**Hosted payment page does not load**

Common causes:

* Using an old or incorrect `paymentUrls` value
* Terminal is not configured for the tender types you are testing
* Environment mismatch between API base URL and hosted page environment

**Payment creation returns a validation error**

Common causes:

* For USDC on Base or USDC on Solana, `requestedAmount` is less than `1.00`
* The tender type in `tenderTypes` is not enabled for the merchant, location, or terminal
* Required fields for the configured terminal or tender flow are missing

**Wallet cannot send the payment**

Common causes:

* The wallet has the payment asset but not the required live-network fee token
* The wallet asset is on the wrong network, such as USDC on Solana instead of USDC on Base
* The wallet cannot scan the QR code or send to the address or invoice shown on the hosted payment page

**Status never reaches a terminal state**

Common causes:

* Customer did not complete the wallet transfer
* Customer sent funds on the wrong network
* Customer sent funds to an old or recently used address instead of the current hosted payment page address
* Webhook endpoint is failing and you are relying only on webhooks without polling

**Payment reached `expired` before the customer paid**

The quote window timed out. The expired payment URL cannot be reused. Direct the customer to a new payment session. If they sent funds after the session expired, Bead's reclaim process will return them.

#### Next steps

After you complete this quick start in sandbox:

* Review [Payment Statuses](/payments/payment-statuses.md) for full status definitions and guidance
* Review [Under and Over Payment Handling](/reference-guide/payment-scenarios/under-and-over-payment-handling.md) for detailed integrator workflows
* Review [Reclaiming Unconverted Crypto](/reference-guide/payment-scenarios/reclaiming-unconverted-crypto.md) to understand reclaim timing and payer experience
* Review [Test Crypto Transactions](/payments/test-crypto-transactions.md) for live-network testing instructions and wallet funding guidance
* Implement production grade webhooks and internal state mapping before going live


---

# 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:

```
GET https://developers.bead.xyz/quick-start.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
