threshold1

Environments (test / live)

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.

The prefix tells you what you're holding

  • 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.

Data isolation is real

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.

Test-mode traffic never counts toward billing

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.

What this means practically

  • Use a test key for local development, staging, CI, and anything else where you want real request/response behavior without touching your MAU count. This is exactly what the Quickstart's key is.
  • Use a live key only for your actual production traffic — this is what counts toward your plan.
  • The two are fully separate user pools. Don't expect a user who registered against your test key to be findable when you switch to querying with a live key, or the other way around — that's the isolation working as intended, not a bug.
  • Passkey domain requirements differ by environment too — a test key needs Test Domain configured, a live key needs Primary Domain. See the fallback page for the details.

What's next

  • Passkey-first & smart fallback covers the domain setup each environment's passkey flow needs.
  • External user ID bridging covers how user resolution works within a given environment.