# Resolving “403 Forbidden” When Creating Payments

Use this guide to troubleshoot `403 Forbidden` errors returned by Payments endpoints such as `POST /payments/crypto`.

A `403` usually means your request was authenticated, but the credential you used is not permitted to create a payment for the merchant or terminal in your request.

Related:

* `401 Unauthorized` usually means the API key is missing or invalid.
* `400 Bad Request` usually means the request body failed validation.

### 1) Confirm you are using the correct authentication method

Payments use header based authentication with a terminal API key (preferred).

Required headers:

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

Common mistakes:

* Using `Authorization: Bearer ...` for Payments (this is legacy and may not work for your credentials)
* Using the wrong header name (must be exactly `X-Api-Key`)
* Copying a maskedApiKey value instead of the real apiKey value

#### Example request

```bash
curl -s -X POST "https://api.test.devs.beadpay.io/payments/crypto" \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: {apiKey}" \
  -d '{
    "merchantId": "{merchantId}",
    "terminalId": "{terminalId}",
    "requestedAmount": 1.00,
    "reference": "ORDER-123"
  }'
```

### 2) Understand the Payments credential set (API key model)

With API key auth, the credential set is:

* `apiKey` (terminal API key)
* `merchantId` (the merchant you are creating a payment for)
* `terminalId` (the terminal that will host the payment page)

These values must match the same configured relationship in the environment you are calling. A valid API key used with the wrong merchantId or terminalId is one of the most common causes of `403 Forbidden`.

Practical rule:

* If you change `terminalId`, you should expect to use an API key that is authorized for that terminal.
* Do not mix IDs across different partners, merchants, or terminals.

### 3) Confirm you are calling the right environment

Mixing Sandbox and Production values will often produce a `403`.

* Sandbox base URL: `https://api.test.devs.beadpay.io`
* Production base URL: `https://api.devs.beadpay.io`

Rules:

* Sandbox apiKey works only in Sandbox
* Production apiKey works only in Production
* Sandbox merchantId and terminalId work only in Sandbox
* Production merchantId and terminalId work only in Production

### 4) Common 403 causes and fixes

| Cause                                 | Check                                                                                                     | Fix                                                                                         |
| ------------------------------------- | --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| Valid apiKey, wrong merchantId        | Confirm the merchantId you are sending is the one associated with the terminal credential you were issued | Use the merchantId that matches the terminal key set                                        |
| Valid apiKey, wrong terminalId        | Confirm the terminalId you are sending is the one associated with the apiKey                              | Use the terminalId that matches the apiKey, or request the correct apiKey for that terminal |
| Mixing environments                   | Confirm your base URL and IDs are all for the same environment                                            | Use Sandbox IDs with Sandbox base URL and Production IDs with Production base URL           |
| Terminal not permitted or not enabled | Confirm the terminal is configured for Payments in that environment                                       | Verify terminal configuration with your Bead contact                                        |
| Request sent with legacy Bearer token | Confirm you are not using `Authorization: Bearer ...`                                                     | Use `X-Api-Key` for `POST /payments/crypto`                                                 |
| Caching the wrong credential          | Confirm your app is not reusing an apiKey from a different terminal                                       | Store apiKey per terminal and select the correct one at runtime                             |

### 5) Still blocked?

When escalating a `403`, include the following so support can trace the request quickly:

1. Timestamp (with timezone)
2. Environment (Sandbox or Production)
3. Endpoint called (`POST /payments/crypto`)
4. merchantId and terminalId you sent
5. The request ID from response headers (if present)
6. The full response body (do not include your apiKey)

Tip:

* Do not paste the apiKey into tickets or chat. Treat it like a password.

### Legacy: 403 troubleshooting for Bearer token integrations

Some existing integrators authenticate Payments using legacy OAuth and send `Authorization: Bearer {access_token}`. If you are on the legacy model, most `403` issues come from mixing credential sets (for example, token generated for one terminal used with a different terminalId in the request body).

If you believe you are on the legacy model and still seeing `403`, confirm with your Bead contact whether your integration should migrate to `X-Api-Key` authentication for Payments.
