Internal traffic
The developer machine you test from, the internal tools that hammer your API, the tab you leave open on your own product all day — that's real traffic, but it isn't your customers. Internal traffic filtering lets an account keep its own activity out of its dashboards, the way GA4's "internal traffic" does — with one important difference: Crossdeck never drops the events. They are always collected and stored. The exclusion is a view applied at read time, on every surface, and it reverses with a single toggle.
TL;DR
- Internal events are always collected and stored. Filtering is a query-time view, never an ingestion change. A misconfigured rule cannot lose data — the full firehose stays auditable.
- Every surface hides internal traffic by default — Overview, People, Activity, Revenue, Funnels, and Cohorts.
- Three ways to mark traffic internal, evaluated in priority order: a flagged Crossdeck User, an IP address / CIDR range, or a per-browser opt-out flag (
?crossdeck_internal=1). - Reclassification is retroactive. An anonymous event later stitched to a flagged user becomes internal after the fact — because the raw signals are persisted on every event and re-derived, not stamped once at ingest.
- One toggle flips it back. "Show internal traffic" is off by default; turn it on to see the full stream while you debug.
What counts as internal
Every event Crossdeck stores carries an actor_type — a small enum describing who generated it:
| Value | Meaning |
|---|---|
customer | A real person using your app. The default — every event is a customer event until a rule says otherwise. |
internal | Activity you generate yourself: a developer machine, an internal tool, you browsing your own product. Excluded from dashboards by default. |
test | Reserved. A future slot for sandbox / synthetic actors. Not wired up yet — present so the model doesn't need a migration later. |
"Internal" is the only classification active today. It answers a narrow, practical question: is this event me, or is it a customer? Getting that line right is what makes "active users" mean active customers, and what stops your own paywall-testing from showing up as a conversion.
Collected always, filtered on read
This is the load-bearing principle, and it is worth stating plainly: Crossdeck never drops an internal event at ingestion. Internal traffic is collected, validated, and stored exactly like customer traffic. Classification — and exclusion — happen later, at query time, every time a dashboard reads.
The alternative — dropping events that match an internal rule at the door — is how most "internal traffic" features work, and it is a trap. A fat-fingered CIDR range, an IP that turns out to be a shared corporate gateway, a rule a teammate adds without thinking it through: any of these can silently delete real signal you will never get back. There is no undo on data you didn't keep.
Because rules change and identity resolves over time. An event you couldn't classify at ingest (anonymous, no resolved user yet) might become classifiable an hour later when the session stitches to a flagged account. A query-time model re-asks the question on every read against the current rules; an ingestion-time model freezes a verdict the instant the event arrives and can never revisit it. Keeping the raw firehose intact is what makes the retroactive case below possible at all.
Three ways to define internal traffic
You define what counts as internal in Settings → Internal traffic. There are three independent signals; you can use any combination of them.
1 · Flag a Crossdeck User
Toggle an individual Crossdeck User as internal. Every event that resolves to that user's ID is internal — across every device and session they use, past and future. This is the strongest and most durable signal: it follows the human, not the machine or the network they happen to be on.
Use it for yourself and your team's own accounts on your product. Because it keys off the resolved Crossdeck User ID, it composes directly with identity resolution — the moment an anonymous session stitches to a flagged user, its events reclassify (see retroactive reclassification).
2 · IP addresses and CIDR ranges
Add individual IP addresses or CIDR ranges (for example, your office or VPN egress). Any event whose source IP falls inside a listed range is internal. This is the right tool for traffic that isn't tied to a logged-in identity — an internal dashboard hitting your API, a QA device that never signs in, a marketing site preview.
A residential or coffee-shop IP is shared and reassigned; a corporate gateway can sit in front of real customers. Prefer the user flag where an identity exists. The never-drop design is your safety net here — if a range turns out to be too broad, widen or remove it and the affected events reclassify back to customer on the next read. Nothing was lost.
3 · The browser opt-out flag
Visit any tracked surface with ?crossdeck_internal=1 in the URL. The web SDK persists a flag in that browser's localStorage, and every later event from that browser is tagged internal — until you clear it by visiting with ?crossdeck_internal=0.
This covers the cases IP and identity miss: a dynamic home IP that changes nightly, or you browsing your own product logged out. It is per-browser and self-service — no Settings change required to set or clear it.
Keep https://yourapp.com/?crossdeck_internal=1 and …=0 as two bookmarks. One click tags the browser before a testing session; one click clears it after. Because the flag lives in localStorage, it survives reloads and new tabs on the same browser profile, but not a different browser, a private window, or a cleared cache.
How a signal wins
An event is internal if any of the three signals matches. They are evaluated in priority order, strongest first:
- Identity — the resolved Crossdeck User ID is flagged internal. Authoritative whenever an identity is known.
- IP / CIDR — the event's source IP falls in a listed range.
- Opt-out flag — the browser carries the persisted
?crossdeck_internal=1flag.
The order matters for explainability, not for the verdict: identity is checked first so that "internal because this is a flagged teammate" wins over the weaker, network- or browser-level reasons. A customer event stays customer only when none of the three match.
Retroactive reclassification
Identity resolves over time. A visitor lands anonymously, clicks around, and only signs in three pages later — at which point identity stitching connects the earlier anonymous events to the now-known user. If that user is flagged internal, those earlier events must become internal too, even though they were unclassifiable when they arrived.
Crossdeck handles this by persisting the raw signals on every event — the resolved user ID (if known at the time), the source IP, and the browser opt-out flag — rather than a single classification stamped once at ingest. Because the inputs are kept, actor_type can always be re-derived from the current rules:
- An identity-merge re-derives
actor_typefor the stitched session, so an anonymous event resolved to a flagged user flips to internal retroactively. - A rule change — flagging a user, adding or removing a CIDR — takes effect on the next read for all matching events, backwards and forwards.
A value stamped at ingest ("internal: true") is frozen — it can't learn that the session later belonged to you. Storing the inputs and re-deriving on demand is what lets a week-old anonymous event become correctly internal the moment its identity is known. It is the same instinct behind the never-drop rule: keep the evidence, re-decide on read.
Showing internal traffic
Every dashboard carries a "Show internal traffic" toggle. It is off by default — your own activity is hidden on Overview, People, Activity, Revenue, Funnels, and Cohorts without any setup. Turn it on to fold internal events back into the view while you are debugging or developing.
It is purely a view preference. Turning it on does not change what was collected, and turning it off does not delete anything — both states read the same stored firehose and differ only in the filter applied. There is no ingestion-time version of this switch, by design.
What gets stored
For the curious, the shape underneath:
- Hot path. A best-effort
actor_typeis stamped at ingestion so the dashboard's fast, live reads can filter immediately — alongside the raw signals (resolved user ID if known, source IP, opt-out flag) needed to re-derive it. - Identity merges re-derive
actor_typefor the affected session, so stitched sessions reclassify without waiting for a batch job. - Cold path. The analytics warehouse re-derives
actor_typefrom the stored raw signals and the current rules at batch time, so historical queries reflect the latest configuration — not whatever was true the moment an event arrived.
The two paths agree because they answer the same question from the same stored inputs; the hot path is just an optimisation so live dashboards don't wait for the warehouse.
Related
- Identify users — how anonymous sessions resolve to a Crossdeck User ID, the mechanism that makes the user flag and retroactive reclassification work.
- Identity verification — how Crossdeck stitches identities across devices and sessions.
- Sandbox vs production — the other axis your dashboards filter on. Internal traffic is orthogonal: an event is sandbox-or-production and customer-or-internal.