Every project has two environments, test and live, distinguished by which API key you use. This isn't a cosmetic tag — test-mode traffic is genuinely isolated from live traffic, and genuinely excluded from billing.
th_test_... — a test-mode key. The one auto-generated when you create your first project (see the Quickstart) is always this kind.th_live_... — a live-mode key. Created explicitly from the dedicated API Keys page by choosing the Live environment.The prefix is not decorative — every request authenticated with a key carries its environment through the entire request, and that environment value is what the rest of this page is about.
Every table that stores end-user auth data — users, otp_codes, auth_events, among others — carries the environment alongside project_id. A user record is scoped to project_id and environment together: the same email can have a completely separate row in test versus live. Log in as you@example.com against a test key, then again against a live key, and you're two different users as far as threshold1 is concerned.
This scoping is enforced in the shared identity-resolution logic every method routes through — the same function covered on the External User ID Bridging page. When an environment is known, every lookup filters on it explicitly; nothing in that resolution path can leak a test-mode user into a live-mode lookup or vice versa.
Monthly Active User counts — what your plan limit is measured against — are computed by counting distinct users with a successful login in the last 30 days, filtered specifically to environment = 'live'. Test-mode registrations and logins, however many you run, are structurally excluded from that count — not rate-limited differently, not discounted, excluded. The same filter is what the plan-limit enforcement itself reads, so there's no separate "billing" number that might drift from what's displayed: the live-only count is the only number that gates you.
A separate, test-only count exists purely for the dashboard's own Live/Test/All toggle — for visibility, never for enforcement. It's a genuinely different function from the one billing reads, not a parameter that could accidentally be flipped.