Account model

Environments and API keys

Each project ships traffic through two environments — live and test — resolved by the API key on every request. Here's how that routing works and how to operate it safely.

Every Sankofa project has two environments: live for production traffic and test for everything else (development, CI, staging, internal review apps). They share the same dashboard, the same flags, and the same configs — but their event streams are completely separate, and the engine routes a request to the correct one based on the API key the SDK sends.

That single design choice is why your code path is identical between dev and prod, and why you don't need a config flag to "switch environments." You just use the right key.

Two keys per project

Open any project in the dashboard and navigate to Settings → API keys. Every project has, at minimum:

Key prefixEnvironmentWhat it routes to
sk_live_…liveProduction-tier event store, dashboard's Live events view, billed against your monthly events quota.
sk_test_…testTest-tier event store with reduced retention; Test events view; not billed.

You can mint additional keys per environment (e.g. one per service or per CI pipeline) so that you can revoke a leaked key without disrupting unrelated workloads. The prefix always tells you which environment a key is bound to.

How routing works

Every SDK call sends x-api-key: sk_…. On the engine:

  1. Key lookup

    The engine does an O(1) cache lookup on the key. The cache lives in Redis with a 30-second TTL, so a revoked key stops authenticating within 30 seconds globally — no engine restart required.

  2. Environment resolution

    The cached entry contains (project_id, environment, plan_tier, allowed_features). The engine writes the event row with environment = "live" or "test" directly from this lookup, no client trust involved.

  3. Quota check + ingest

    For live traffic, the engine increments your monthly events counter against the project's plan limits. test traffic is exempt from quota. Then the row is written to ClickHouse and the SDK gets a 200.

The implication: an SDK can never accidentally cross environments. Even if your production code somehow gets a sk_test_… key, every event lands in the test stream — no contamination of the live data.

Choosing the right key

ScenarioUse
Local developmentsk_test_…
CI integration testssk_test_…
Internal review apps / stagingsk_test_…
Production buildssk_live_…
Server-side cron / batch jobs in prodsk_live_…
Open-source example appsa dedicated sk_test_… key, never your default

Rotating a key

Rotation is non-disruptive if you do it in this order:

  1. Mint the new key

    In the dashboard, Settings → API keys → New key. Copy it once — you won't be able to see the secret part again.

  2. Roll your apps to the new key

    Deploy with the new key. Both old and new keys are valid simultaneously — there's no traffic gap.

  3. Wait for rollout to finish

    For mobile apps, wait until your store-rollout window completes (often 7–14 days for a forced upgrade).

  4. Revoke the old key

    Settings → API keys → Revoke on the old key. Within 30 seconds, any remaining clients on the old key start getting 401s. They'll auto-retry; if they're still on the old build, those events drop on the floor (which is what you want when retiring a leaked key).

Inspecting events by environment

The dashboard's Live events view shows production traffic; Test events shows test traffic. Every chart, funnel, and cohort lets you pick the environment in the filter bar. You can't query both at once — that's by design, since the volumes are different orders of magnitude.

Region pinning (data residency)

If your organization has data-residency requirements (EU, US, regional), you can pin a project to a specific region at creation time. The API key for a regional project encodes the region into its prefix (sk_live_eu_…, sk_live_us_…), and the SDK's endpoint must match. See Data residency.

What's next

Edit this page on GitHub