Skip to main content
Every SDK initialization needs three identifiers — orgId, projectId, and env — plus an API key. This page explains what those mean and what’s configured where.

The hierarchy

Organization (org_acme)
└── Project (proj_marketplace)        ← unit key, bucket count
    ├── Environment (production)      ← config bundle, API keys
    ├── Environment (staging)
    └── Environment (development)
Three levels, each with a clear job.

Organization

The top-level tenant. Holds billing, member access, audit logs, and one or more projects. Most teams need exactly one organization. You only need more if you have legally separate entities that must not share data.

Project

A project is the unit of experimentation. It contains:
  • Its parameters, layers, and policies
  • Its event definitions
  • Its hashing configuration — most importantly the unit key and the bucket count
Two projects never share a config bundle. They can share parameter naming conventions, but not state. A typical setup is one project per product surface, or one per logical product. A B2C marketplace might have one project (proj_marketplace). A B2B SaaS might have two — one keyed on userId for user-level experiments, one keyed on companyId for company-level experiments.

Environment

An environment is a runtime variant of the project. Production, staging, and development share the same parameters, layers, and policies — but each environment has its own parameter default overrides and its own API keys. What an environment lets you do:
  • Override a parameter’s default value per-environment. Set feature.enabled = false in staging while keeping it true in production. The bundle for staging ships the override; the bundle for production ships the canonical default.
  • Issue separate API keys so staging traffic and production traffic are clearly distinguished in dashboards and rate limits.
  • Serve a separate bundle for each environment — the SDK fetches bundle:{projectId}:{env} and only sees the values relevant for that environment.
What an environment does not do:
  • Policies are not environment-scoped. A policy is created in the project — it applies to every environment. Pausing a policy pauses it everywhere. If you need an experiment to run only in production (or only in staging), use targeting conditions keyed on a context field your SDK sends (e.g. env or deployment_stage).
  • Layers, policies, allocations, conditions, and event definitions live at the project level. They’re the same across environments.
Every project comes with production, staging, and development out of the box. You can create more if you need (preview, qa, per-team environments).

API keys

API keys are scoped to a project and environment. Every key has the same traffical_sk_... prefix — what differs is the key’s scopes, which determine what it can do and whether it is browser-safe:
ScopesTypeWhere to use
sdk:read, sdk:writeSDK keyAll SDKs (@traffical/node, @traffical/js-client, @traffical/react, @traffical/svelte, React Native) and direct SDK API calls. Browser-safe — fetches the bundle and sends events but cannot modify configuration.
mgmt:read, mgmt:write, or adminManagement keyCreating and modifying projects, layers, and policies. Must stay secret — never ship in client code. (The CLI normally uses device login instead.)
Create and manage keys in Settings → API keys in the dashboard.

The unit key

The unit key is the property of the user that determines bucketing. It’s a project-level setting. The most common choice is userId:
project:
  hashing:
    unitKey: userId
    bucketCount: 10000
You can also pick:
  • companyId for B2B / multi-tenant SaaS where every user in an organization should see the same variant
  • deviceId for pre-login mobile experiences
  • sessionId for cohort-based randomization
  • a custom key — anything stable the SDK can read out of context
Whichever you pick, the SDK reads the value of that key from the context argument you pass to getParams. If you set unitKey: userId, every call must include context.userId.
The unit key choice is hard to change later. If you start with userId and switch to companyId, every assignment will reshuffle. Pick deliberately.

Anonymous users

For browser SDKs, you often don’t have a userId yet — the user hasn’t logged in. The browser SDK auto-generates a UUID on first visit (the “stable ID”), stores it in localStorage with a cookie fallback, and uses it as the value of the project’s unit-key field. So a project keyed on userId ends up with the UUID standing in for userId until you call identify(realUserId). Calling identify() overwrites the stable ID with the real user ID. From that point on, bucketing uses the real value — which produces different buckets from the anonymous phase. A user who saw the treatment variant while anonymous may see control after login, and there’s no built-in way to preserve the anonymous assignment across the login boundary (different unit-key value → different hash → different bucket). For experiments where this matters, target logged-in users only (a policy condition on a context field your SDK sends, or simply running the experiment server-side after the user has been identified).

Bundle scope

Every config bundle is built for exactly one (project, environment) pair. The SDK fetches the bundle for the IDs you initialize it with, and every parameter, layer, and policy in scope is included. You don’t need to specify which experiments to load.
const traffical = await createTrafficalClient({
  orgId: "org_acme",
  projectId: "proj_marketplace",
  env: "production",
  apiKey: process.env.TRAFFICAL_API_KEY!,
});
Switching env from production to staging swaps the bundle and the API key — your code doesn’t change.

A note on env

env is a free-form string. The default environments are production, staging, and development, but you can create environments with any name. Whatever string the SDK sends, the service looks up the matching bundle. This is useful for ephemeral environments (per-PR previews, feature-branch deployments) — create the environment, scope the API key to it, and your team can experiment without touching production.

Next steps

Parameters

Typed values with defaults.

Layers

Mutual exclusivity and orthogonal bucketing.

Multi-tenant SaaS pattern

When to use a company-keyed project.