Every system downstream of a login assumes the person holding the session is the person it belongs to. When support impersonates a customer, that assumption breaks. To exclude impersonation from analytics, emails, usage meters and background jobs, derive one verified “support is acting” flag from the session on your server, carry the real actor with every request and job, and have each integration decide whether to suppress the effect, attribute it, or let it through.
None of this is exotic. It’s an inventory problem. The side effects are spread across client SDKs, server helpers, queues and outbound webhooks, and each one needs a deliberate answer before a customer reports it.
What side effects does user impersonation cause?
A user impersonation side effect is anything your application does because a session exists, rather than because the customer chose to do it. WorkOS published a thorough inventory (opens in a new tab) of these, from analytics identify calls to lifecycle email and queued jobs. Here’s a working list with a sensible default for each:
| Side effect | What goes wrong | Sensible default |
|---|---|---|
| Product analytics and session counts | Support clicks inflate the customer’s usage and skew funnels | Suppress, or tag and filter |
| Error tracking | Errors the agent triggers appear as the customer’s | Tag with support context |
| Lifecycle and marketing email | A support visit completes an onboarding step and advances a drip campaign | Suppress |
| Transactional notifications (“you changed X”) | The customer is told they did something they didn’t do | Suppress the self-action notice; decide separately about a notice of access |
| Last seen, last login, read receipts | Inactive customers look active; unread items become read | Skip the update |
| Billing and usage metering | Support activity consumes the customer’s quota or bill | Exclude, or meter to an internal bucket |
| Background jobs | Work runs later as the customer with no record of who started it | Attribute at enqueue time |
| Webhooks to the customer’s systems | Events land in their tools attributed to their user | Depends on scope; see below |
Not every row applies to every product. The point is to answer each one on purpose.
Where should the impersonation flag come from?
Take it from verified authentication state on the server, never from a header, query parameter or local storage value the browser can set.
Most auth providers already mark impersonated sessions:
- Clerk exposes an
actorobject on impersonated sessions, available fromuseAuth()in the browser and from the auth object on the server, according to its user impersonation guide (opens in a new tab). - Better Auth’s admin plugin stores (opens in a new tab)
impersonatedByon the session, the ID of the admin who is impersonating. - Devora provider integrations resolve (opens in a new tab) a verified context for each impersonated request, with
actor,subject,sessionId,scopeand the authorization source. Your integration keeps it in signed token claims or server-side session data and must never accept it from browser-supplied headers or bodies.
Turn that into a small, boring object that every layer can read: isSupportSession, the actor reference, the subject reference and the support session ID. Put it on your request context on the server, and render it into the initial page payload for the browser.
Two details decide whether this works in practice.
Timing. Analytics SDKs usually initialize on page load. If the flag arrives in a later fetch, the identify call has already fired. Ship the flag with the session so it’s available before any tracking code runs.
Tabs. Don’t rely on browser-only state. Devora’s frontend SDK, for example, reports isImpersonating() for a validated session in this tab, as its JavaScript reference (opens in a new tab) notes. Its per-tab capability is never written to browser storage, so without a session bridge (opens in a new tab), a reload or new tab runs as the impersonated user with no banner, scope enforcement or capture. A suppression check that only reads browser state would miss that tab. A flag derived from the server session wouldn’t.
Write the check so a missing or malformed flag leaves normal behavior in place. A bug in your suppression code should mean one support session gets tracked by mistake, not that tracking stops for every customer.
How do you exclude impersonation from analytics?
Skip identify for impersonated sessions, then either opt the session out of capture or tag every event so you can filter it. Pick one approach per tool and apply it on the client and the server.
Opting out. Most analytics SDKs have a documented switch:
- PostHog’s
posthog.opt_out_capturing()prevents all data from being captured and sent, including autocapture, manual captures and session replays, per PostHog’s data collection docs (opens in a new tab). PostHog persists the opt-out in local storage or a cookie, so callopt_in_capturing()when the staff member is back in their own session. - Mixpanel’s
mixpanel.opt_out_tracking()stops the SDK sending data for the current instance, andopt_out_tracking_by_defaultlets you start opted out, as its JavaScript SDK docs (opens in a new tab) describe. - Segment’s Analytics.js supports (opens in a new tab) source middleware through
addSourceMiddleware, which can enrich an event or drop it by returningnull.
Opt-out switches don’t always cover everything analytics-adjacent. WorkOS points out that PostHog’s opt-out suppresses capture but doesn’t stop feature flag evaluation, which can still enroll the customer in an experiment. Check flags, surveys and experiments in whichever SDK you use.
Tagging instead. If you want to measure support work, keep the events but add a property such as support_session: true and exclude it from customer-facing reports. This keeps the data, but only works if every dashboard actually applies the filter.
Server-side tracking. Events sent from your backend usually go through a separate helper. Gate it on the same request-context flag.
URLs. Impersonation links often carry a one-time code. Devora’s SDK removes its exchange parameter immediately, and its docs say not to forward (opens in a new tab) it to analytics, error reporting or another URL. Make sure your page-view tracking doesn’t record the landing URL before that happens.
Should impersonated sessions send emails and notifications?
Suppress the noise, keep the signal. Emails that describe the customer’s own actions (“you updated your billing address”) or advance lifecycle sequences should not fire for support activity. The customer didn’t do it, and a confusing alert can look like an account takeover.
Notifications also act as the customer’s independent check on what happened to their account. WorkOS makes this case (opens in a new tab), arguing that suppression should depend on session type computed from the token, not on a control the operator can toggle. A reasonable middle ground:
- Suppress self-action notices and lifecycle triggers for impersonated requests.
- Decide, as a policy, whether customers get a notice that support accessed their account.
- Never give support agents a way to cancel notifications for a session they’re running.
Better still, block the actions that would trigger security notices. Password, MFA, email, role and billing changes generally shouldn’t happen in an impersonated session at all. The read-only impersonation guide covers how to enforce that on the backend.
What about last seen, read receipts and usage metering?
Skip the update for impersonated requests. These values feel harmless but drive real decisions.
Last seen and last login feed churn analysis, inactive-account cleanup, “we miss you” campaigns and seat reporting. A customer who hasn’t logged in for months shouldn’t look active because support helped them. Store support visits in a separate field if you want them.
Read state is trickier. Devora’s own scope docs (opens in a new tab) give marking a notification as read as an example of a write that’s operationally read-only, which a team might allow as a read-only exception. Mechanically that’s true. From the customer’s side, though, an unread item they were saving has quietly disappeared. Decide whether support’s views should mark anything as read, and if not, skip the write rather than allowlisting it.
Billing, quotas and rate limits. If you meter API calls, exports, seats or AI usage, support activity shouldn’t count against the customer. Exclude impersonated requests from the meter, or record them against an internal account so you can still see the cost.
How do background jobs and webhooks keep actor context?
Capture the actor when the job is enqueued, not when it runs. A job queued during a support session may run minutes or hours later, after the session has ended. By then there’s no session to inspect.
Put these fields in the job payload:
- Staff actor reference and customer subject reference.
- Support session reference (from verified context, never a token).
- Scope at enqueue time.
The worker then attributes its effects to both identities and decides whether to re-check that the session is still allowed. In Devora, every ending triggers a signed termination request (opens in a new tab) to your backend, retried if delivery fails and sent even with no tab open, so you can record which session IDs are revoked and have workers consult that list. The audit log guide covers recording “accepted” and “completed” as separate events.
Webhooks to customer systems need a scope-based answer. In a read-only session, nothing should emit them, because nothing should change. In a write session where support fixes data, the customer’s integrations usually need the event to stay in sync, so suppressing it creates drift. Emit it, and keep the support attribution in your own audit trail. If you add an “initiated by support” field to the payload, treat it as a schema change and document it.
Test each side effect in staging
Run one impersonated session against a staging account with every integration pointed at test destinations. These are pass conditions to check, not results we’ve run.
| Scenario | Expected outcome |
|---|---|
| Page load in an impersonated session | No identify call; events suppressed or tagged before the first one is sent |
| Same session, server-side tracking path | Event suppressed or tagged, same as the client |
| New tab or reload during the session | Same suppression; the flag comes from the server session |
| Support views an onboarding page | No lifecycle step completed, no drip email queued |
| Support views a notification list | Unread state unchanged, unless you chose otherwise |
| Customer’s last-seen value | Unchanged after the session |
| Metered action during a write session | Not counted against the customer’s usage |
| Job enqueued during the session | Payload carries actor, subject and session reference |
| Staff member’s own session after impersonation ends | Analytics opted back in and attributed to staff |
Where Devora fits
Devora handles the access side: requests with a reason, approval by an approver or admin in your organization, read-only or read-and-write scope, a time limit, access history, optional recording and a backend scope guard. It passes your backend verified context (opens in a new tab) with the actor, subject, session ID and scope for every impersonated request.
It doesn’t know about your analytics, email provider, meters or queues, and it doesn’t suppress anything in them. That code is yours, and the context is what makes it straightforward. For the wider set of responsibilities, see the multi-tenant impersonation checklist.
If you want that context without building the access workflow yourself, create a Devora account and try one staging session against the table above.
Sources
- WorkOS: What else happens when you impersonate a user? (September 11, 2026) (opens in a new tab)
- Clerk: User impersonation (opens in a new tab)
- Better Auth: Admin plugin (opens in a new tab)
- Devora: Auth providers (opens in a new tab)
- Devora: JavaScript reference (opens in a new tab)
- Devora: Frontend integration (opens in a new tab)
- PostHog: Controlling data collection (opens in a new tab)
- Mixpanel: JavaScript SDK (opens in a new tab)
- Twilio Segment: Analytics.js source (opens in a new tab)
- Devora: Scope and security (opens in a new tab)
- Devora: Session lifecycle (opens in a new tab)