# A screen returned 403

Two separate boundaries answer 403, and they fail for different reasons: the browser-side rewrite that turns an operator path into a customer one refuses anything it does not recognise, and the allowlist in front of the API refuses a path a customer session may not call. Both are working as designed. What is usually broken is that the capability has no customer route yet.

- **Status:** Available
- **Audience:** both
- **Last verified:** 2026-09-10
- **Canonical:** https://connectbyjbrh.com/docs/troubleshooting/403-on-a-screen/

## What the symptom looks like

A screen loads and then does not fill: an empty panel, a message saying the request was refused, or a section that stays blank while the rest of the page works. The distinguishing feature is that the same screen behaves differently for the two audiences — it works signed in as the operator and refuses signed in as a customer, or the reverse.

That asymmetry is the diagnosis, not a detail of it. A capability that is genuinely broken breaks for everybody.

## What it means

Connect is one application serving two audiences over one implementation. A customer's browser does not call the same paths the operator's does: a rewrite maps an operator path onto its customer equivalent before the request leaves the browser, and a second check in front of the API decides whether the session that arrived may call what it asked for.

Both refuse rather than guess, and that is the correct behaviour. A rewrite that fell back to the operator path when it did not recognise something would be a rewrite that occasionally sent a customer's request at the operator's surface. Refusing an unmapped path is the fail-closed choice, and it is why an unfinished capability shows up as a refusal rather than as data from the wrong place.

## The causes, in the order they are worth checking

1. **The capability has no customer route.** Something new exists for the operator and the rewrite has no rule for it, so the request is refused. This is the common one and it is a gap in the work rather than a fault in your workspace.
2. **The path is not on the allowlist for a customer session.** The route exists and this audience may not call it. Sometimes deliberate, sometimes an omission when a route was added.
3. **It is a permission inside the workspace, not an audience boundary.** A member without the right to change integrations sees a refusal on that action and no other. See [You do not have permission](/docs/troubleshooting/permission-denied/).
4. **The session ended.** A refusal and an expired session look similar from a panel that will not load. See [You were signed out](/docs/troubleshooting/session-expired/).

| Clue | Points to the rewrite | Points to the allowlist |
|---|---|---|
| The capability is new | Very likely | Possible |
| Every part of one screen refuses | Likely — nothing on it is mapped | Less likely — the allowlist is per path |
| One action refuses, the rest of the screen works | Unlikely | Likely — one path is not permitted |
| It works for the operator | Consistent with both | Consistent with both |
| Another customer workspace sees the same refusal | Yes, it is not about you | Yes, it is not about you |

## What Connect completed, and what it did not

**Completed:** the refusal itself, cleanly. No partial read happened, nothing was written, and no data from another workspace was returned on the way to saying no. The session is intact and every other screen keeps working.

**Not complete:** whatever you were trying to do. Nothing was queued for later and nothing is waiting in the background — a refused request leaves no residue, so returning in an hour changes nothing on its own.

## What you can do

1. Reload once, then sign in again if the whole application is refusing rather than one screen.
   - Result: That separates an expired session from a boundary. A boundary refusal survives a fresh sign-in; an expired session does not.
2. Try the same capability from its other entry point, if it has one.
   - Result: One path refusing while another reaches the same records points at the allowlist rather than at the capability.
3. Note whether it is the whole screen or one action, and whether the capability is recent.
   - Result: Those two facts are most of what a report needs, and they are the two people forget to include.

There is no setting on your side that opens a boundary, and there is no workaround worth finding. A refusal that can be talked around is a refusal that was not protecting anything.

## What an administrator can do, and when to escalate

- Confirm the person's permission for the action, since a permission refusal and an audience refusal look alike from a screen.
- Check whether the same refusal occurs for every member, which separates a person-level cause from a capability-level one.
- Escalate as soon as it is clear the capability exists for one audience and not the other. That is a gap to be closed rather than a support question, and gaps are enumerated with a reason rather than left to be rediscovered.
- Include the screen, the action, the audience and whether the capability is recent. That is enough; nothing about a refusal requires a screenshot of anything sensitive.

## Questions

### Is a 403 evidence that somebody tried to reach my data?

No. It is almost always your own session asking for something this audience does not have a route to. The boundary refused before anything was read, which is what it is for.

### Will it fix itself?

Not on its own. A missing customer route is closed by a change to the application, not by waiting or by clearing anything in the browser. Reporting it is what moves it.

### Why not fall back to the operator path when a rewrite is missing?

Because that fallback would occasionally send a customer's request at the operator's surface. Refusing an unmapped path is the choice that cannot be wrong in the dangerous direction, even though it produces this symptom.

## Related

- [The customer facade](https://connectbyjbrh.com/docs/security/customer-facade/)
- [Routing a request for the Owner or a customer](https://connectbyjbrh.com/docs/workflows/owner-tenant-routing/)
- [You do not have permission](https://connectbyjbrh.com/docs/troubleshooting/permission-denied/)
- [You were signed out](https://connectbyjbrh.com/docs/troubleshooting/session-expired/)
- [A tenant facade that fails closed](https://connectbyjbrh.com/research/closed-by-default-facade/)

## What this page is based on

- Connect source pack section 1 — the rewrite refuses an unrecognised path; the allowlist refuses a path a customer session may not call (`docs-source/sources/GENERAL.md`)
- Connect source pack section 1 — capability gaps are enumerated with reasons and must never grow (`docs-source/sources/GENERAL.md`)
- Connect capability registry (`docs-source/facts.py`) — `owner_tenant_routing`, `session_replacement`
