# Why is my request returning 403 Forbidden?

A **403** means Bead understood your request format but refused to fulfil it because your credentials **lack permission** for the target resource or action.

### 1 — Most common causes & fixes

| Likely cause                                                                       | How to confirm                                                                            | How to resolve                                                                                          |
| ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| **Missing / malformed `Authorization` header**                                     | Header absent or shows `Bearer null` in request logs.                                     | Add `Authorization: Bearer {access_token}` exactly; no extra spaces or quotes.                          |
| **Expired access token**                                                           | Decode the JWT (jwt.io) — `exp` is in the past.                                           | Re-authenticate (`POST /protocol/openid-connect/token`) and retry with the new token.                   |
| **Wrong `client_id` or scope**                                                     | Token’s `client_id` ≠ `bead-terminal`, or `scope` lacks `openid`.                         | Use the terminal credentials (`bead-terminal`) from Bead Support; include `scope=openid profile email`. |
| **Terminal / merchant mismatch**                                                   | Token’s `terminalId` header vs body value differ (e.g., writing to another terminal).     | Ensure `terminalId` and `merchantId` in the request belong to the authenticated terminal.               |
| **Webhook signature check failing (for calls&#x20;*****to your*****&#x20;server)** | Your handler returns 403 to Bead; Bead retries.                                           | Verify the `x-webhook-signature` using the correct `signingSecret`.                                     |
| **IP or WAF block**                                                                | API gateway log shows request reached Bead but response is 403 from *your* reverse-proxy. | Allow Bead IP ranges or relax geo/IP filtering.                                                         |
| **CORS pre-flight denied (browser apps)**                                          | Browser console shows `CORS policy: Response to preflight… 403`.                          | Proxy calls through your backend or add Bead origin to your allowed CORS list.                          |

### 2 — Debug checklist

1. **Dump request & response headers** – confirm the Bearer token is present, well-formed, and not stale.
2. **Decode the JWT** – validate `aud`, `client_id`, `exp`, and `scope`.
3. **Call a simple endpoint** – e.g., `GET /payments/tracking/{trackingId}`. If that also returns 403, the issue is global (token or firewall).
4. **Check token vs resource IDs** – the token ties you to one terminal; accessing another terminal’s resources returns 403.
5. **Inspect network middle-boxes** – WAFs often rewrite or drop auth headers; pause them temporarily.

***

### 3 — Automated recovery pattern

```pseudo
if response.status == 403:
    refresh_token()
    retry_once()
    if still 403:
        escalate_to_log()
```

Refreshing the token catches >90 % of accidental 403s due to expiry.

### 4 — Still blocked?

Send the following to [**developers@bead.xyz**](mailto:developers@bead.xyz):

* Timestamp & timezone
* Full request path (omit secrets)
* `x-request-id` header (if present)
* The first 50 chars of the JWT (`eyJ0eXAiOiJK…`) for token lookup

We’ll trace the request in our logs and identify the exact policy that triggered the 403.


---

# Agent Instructions: 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/faqs-and-troubleshooting/webhooks-and-error-codes/why-is-my-request-returning-403-forbidden.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.
