> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vorel.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# View as tenant (operator impersonation)

> Operator runbook for the view-as-tenant impersonation feature. When to use it, how to use it, session semantics, audit-trail guarantees, security model.

<Note>
  This runbook is for Vorel platform operators (the `platform_operator` role). End customers and
  tenant admins do not have access to this feature. Tenant admins **do** see audit-log entries when
  an operator impersonates their tenant.
</Note>

The view-as-tenant feature lets a Vorel platform operator render the tenant-side dashboard (`/inbox`, `/cases`, `/leads`, `/analytics`, ...) **as that tenant**, without needing a user account on the tenant. It is the support primitive for "what is the tenant's admin actually seeing right now?", load-bearing for incident response, data-sync debugging, and live verification of new features against real production data.

## When to use it

<CardGroup cols={2}>
  <Card title="Customer-support escalation" icon="headset">
    A tenant admin reports their inbox is empty when their voice agent took 12 calls today. You need
    to see the inbox exactly as they see it (same RLS scope, same UI overlays, same filters) to
    confirm whether the calls are missing from the tenant view or whether the tenant's view has a
    stale filter applied.
  </Card>

  <Card title="Data-sync debugging" icon="bug">
    A tenant's CRM-write is failing intermittently. You need to confirm whether the lead row exists
    on the Vorel side from the tenant's perspective (it could be RLS-scoped out, or filtered by
    status). View-as-tenant lets you see what their admin would see.
  </Card>

  <Card title="Live verification of a new feature" icon="circle-check">
    You shipped a new dashboard view behind a feature flag for one tenant. Before announcing it, you
    want to log in as that tenant and confirm the surface renders correctly with their real data.
  </Card>

  <Card title="Onboarding handoff verification" icon="rocket">
    A new tenant just finished kickoff. You want to confirm their dashboard renders correctly with
    their persona, their offerings, their CRM connection, before sending the "you're live" email.
  </Card>
</CardGroup>

## When NOT to use it

* **Routine admin work.** Use the operator console at `/admin/tenants/<id>` for cross-tenant operator tasks: managing credentials, viewing audit logs, running cost rollups. View-as-tenant is for seeing the tenant-side UI, not for performing operator work.
* **Bulk operations across tenants.** If you're doing something that touches multiple tenants, use the operator-side cross-tenant surfaces. Repeatedly starting and stopping view-as-tenant sessions for ten tenants is a code-smell; there is probably an operator-side bulk surface for what you're trying to do.
* **As a workaround for missing operator-side functionality.** If you find yourself impersonating a tenant to do something the operator console should support, file an issue. The operator console is the surface that's supposed to evolve to cover those gaps.

## How to use it

<Steps>
  <Step title="Navigate to the tenant page">
    From the admin console at `/admin/tenants`, click the tenant you need to impersonate. You land at `/admin/tenants/<id>`.
  </Step>

  <Step title="Click 'View as tenant'">
    On the tenant page there is a card titled **View tenant-side as this tenant**. Enter a short reason (3–200 characters) in the **Reason** field, for example `debug data sync`, `verify cases dashboard`, `look at their inbox`. The reason is required and lands in the audit trail.
  </Step>

  <Step title="Submit the form">
    Click **View as tenant →**. Vorel writes an `impersonation_started` row to `audit_log` with your platform-operator id, the tenant id, and the reason. The system sets an 8-hour session cookie scoped to your browser session and redirects you to `/inbox`.
  </Step>

  <Step title="Use the tenant-side dashboard as that tenant">
    Every tenant-scoped query now resolves to the impersonated tenant. The dashboard renders as their admin sees it: same RLS scope, same offerings, same conversation history. A banner appears at the top of every tenant-side page reminding you that you are impersonating, displaying the tenant name and a **Stop impersonation** button.
  </Step>

  <Step title="Stop impersonation when done">
    Click the **Stop impersonation** button in the banner. Vorel writes an `impersonation_ended` row to `audit_log` and clears the session cookie. You return to your operator identity. Alternatively, clearing browser cookies ends the session; closing the browser does not (cookies persist) unless your browser is in private mode.
  </Step>
</Steps>

## Session semantics

* **Cookie-scoped.** The impersonation lives in an 8-hour session cookie. There is no server-side persistent state recording "operator X is currently impersonating tenant Y"; the cookie is the only carrier. Two browsers running side-by-side can each have independent impersonation contexts.
* **8-hour expiry.** The cookie expires 8 hours after the session starts. After expiry, the dashboard reverts to your operator identity automatically; no `impersonation_ended` audit row fires for the silent expiry (the row only fires on explicit stop). Auditors who need to know when a session ended can infer it from the next `impersonation_started` row or from access patterns.
* **Single-tenant at a time.** You can impersonate one tenant at a time. Starting a new impersonation while another is active replaces the active one (an `impersonation_ended` row fires for the old, an `impersonation_started` row fires for the new).
* **No write actions during impersonation are tagged as the tenant.** Mutations you perform while impersonating still carry your operator identity in their audit rows, so a write that happens during an impersonated session is traceable back to the operator that performed it, not laundered through the tenant identity.

## Audit-trail guarantees

Every start and explicit stop of an impersonation session writes an `audit_log` row:

* **`impersonation_started`**: fields: operator's user id, target tenant id, reason text, IP address, user agent, ISO timestamp.
* **`impersonation_ended`**: fields: operator's user id, target tenant id, ISO timestamp.

These rows are written into the tenant's `audit_log` partition and are **visible to the tenant's admin** at the tenant-side audit surface. The tenant admin can see that on 2026-05-19 at 14:32 UTC, Vorel operator Alice impersonated their tenant for the reason "debug data sync" and ended the session 17 minutes later. This is intentional: the tenant's trust posture is that Vorel operators can impersonate, but every impersonation is visible to the tenant's admin in the same surface they audit their own team's activity.

The `audit_log` table is append-only at the database level via a Postgres trigger; impersonation rows cannot be redacted or deleted by anyone (operator, tenant admin, or platform-level) except via the global retention regime. There is no admin path to hide an impersonation event from a tenant.

## Security model

* **Role-gated.** Only users with `platformRole === 'platform_operator'` can start an impersonation. The gate runs in the server action that handles the form submission, independent of any client-side check.
* **Defense in depth.** The admin route group (`/admin/*`) is already operator-gated by middleware; the impersonation server action enforces the same role check independently. A bypass of the admin route would still fail at the action.
* **Reason required.** The reason field is required (3–200 characters); the action rejects an empty or too-short reason. Reasons are not validated for content; write "debug data sync" not "lol". The audit row is what tenant admins read.
* **No token persistence.** There is no JWT, no API key, no server-side impersonation token table. The session cookie is signed with the same secret used for operator authentication. There is no way to "share" an impersonation session across browsers or recover one after the cookie is cleared.
* **No bypass of tenant-side guardrails.** Impersonation overlays the tenant identity onto your operator session; it does not grant you privileges that the tenant admin does not have. If a tenant's admin cannot see archived conversations, neither can you while impersonating them.

## Operating notes

* **Two operators impersonating the same tenant** is allowed. The audit log shows both. There is no "exclusive lock"; view-as-tenant is read-style support, not a destructive operation that needs serialization.
* **Long-running session timeouts.** An 8-hour cookie is long for a support session and short for a debugging session that spans a workday. If you need to span multiple workdays, re-start the impersonation each morning. Two `impersonation_started` rows on consecutive days for the same tenant is normal.
* **Customer-facing notifications.** Vorel does not email the tenant admin when an impersonation starts; the audit row is sufficient per the trust model. If a particularly sensitive tenant (PHI-adjacent, financial-services) wants per-event email alerts, that is a per-tenant configuration request via the operator console; the audit row remains the source of truth.

## Related docs

* [Security overview](/security/overview): multi-tenant isolation, append-only audit, role gates.
* [Security, data handling](/security/data-handling): `audit_log` integrity, append-only triggers, right-to-erasure semantics.
* [Compliance](/security/compliance): DPA disclosure on operator-side access to tenant data.

{/* verified-against: apps/web/src/app/admin/tenants/[id]/impersonate-form.tsx (form: reason required minLength=3 maxLength=200; redirect to /inbox on start) */}

{/* verified-against: apps/web/src/app/(dashboard)/layout.tsx (impersonation overlay applied at the dashboard layout) */}
