Tenant context errors
428, 409, 403 from tenant endpoints. Switching orgs feels stuck. Web app shows Forbidden after creating an org.
The platform uses explicit tenant context for every tenant-scoped request. Errors here are specific and recoverable.
The status codes
| Code | Name | Meaning |
|---|---|---|
428 | tenant_context_required | You hit a tenant endpoint without X-Viewport-Organization and no route binding. |
422 | invalid_tenant_context | The org id format is malformed (not a ULID). |
403 | tenant_forbidden | The org id is valid but you're not a member. |
409 | tenant_context_mismatch | The route's workspace and the header's workspace disagree. |
The web app should not surface these to end users. They should never happen with normal navigation. If you see one, something's wrong.
Symptom: "Forbidden" banner after creating a new org
Created a new org, switched into it, opened /workflows or /plans, banner says "Forbidden."
Cause: stale tenant context. The frontend switched the active org but a cached query fired with the old org id.
Fix:
1. Open dev tools → Application → Local storage
2. Find activeOrganizationId, delete it
3. ReloadThis forces a fresh org-selection round. If it persists, the membership row for your owner role may not have been created. Tell us with the workspace id from the URL.
Symptom: org switcher shows old org name
You switched to org B, but the chip in the top-left still says "Org A."
Cause: race between the API switch endpoint and the local store update.
Fix: reload. The store re-fetches from /api/me on load and picks up the actual active org.
Symptom: notifications from an org you don't recognize
Phone push from an org you don't remember being a member of.
Cause: most likely you were invited and accepted at some point. Less likely: a security issue.
Fix:
- Open the app. Click the org switcher. Pick the unfamiliar org.
- Go to Members → your row. Confirm your role and the org name.
- If you don't want to be a member, leave: Members → row → Leave organization.
If the org and inviter look suspicious, report it.
Symptom: API call from your script returns 428
You wrote a script using a personal access token:
curl -H "Authorization: Bearer $VIEWPORT_TOKEN" https://api.getviewport.com/api/plans
# {"error":{"code":"tenant_context_required"}}Cause: user-scoped tokens require explicit org context per request.
Fix: add the header.
curl -H "Authorization: Bearer $VIEWPORT_TOKEN" \
-H "X-Viewport-Organization: $WORKSPACE_ID" \
https://api.getviewport.com/api/plansFor CI / integrations that always run against one org, create an org-bound token instead. It has the org baked in and you don't need the header. See Reference: API. Auth.
Symptom: route + header mismatch (409)
GET /api/workspaces/01J7A.../plans
X-Viewport-Organization: 01J7B...Cause: the URL says org A, the header says org B. Platform refuses to silently choose.
Fix: pick one. If the URL is authoritative (route binding), drop the header. If the header is authoritative, drop the workspace from the URL and use the clean route /api/plans.