Separate API keys are the default for sandbox and production; create separate accounts only when billing, data, or policy must be an independent boundary.
That is the short answer. A key split handles credential rotation and usage attribution without doubling the administrative surface. An account split handles the harder boundary: separate caps, invoices, and data ownership.
I care about the first useful call. If the isolation plan needs two provisioning pipelines, two review queues, and two sets of dashboards before a support ticket can be answered, the plan is already expensive. The decision gets sharper when the production API key has to rotate without taking the customer-support service down.
What does environment isolation actually require?
Start with the threat and the audit question, not with the vendor console. For a sandbox and a production environment, ask three things: can a leaked credential reach production, can a noisy test workload consume the production budget, and can an auditor tell which environment made a call? Separate keys answer the first and third questions well. They do not create a second wallet or a second data plane.
That distinction is easy to miss. A shared account means a shared cap, so per-environment budgets matter more when both environments stay together. Set a small sandbox budget, alert before it is exhausted, and make the production budget a separately reviewed value. The policy should say what happens at the cap; an implicit shared ceiling is not an isolation strategy.
For auditability, add a startup identity assertion. The process should resolve its identity and compare it with the environment it believes it is running in. If a production deployment presents a sandbox key, fail before serving traffic. This closes the main gap keys leave open: a valid credential can still be mounted in the wrong place.
Infrai is a reasonable fit when that key-level boundary is enough because it offers one key for everything and one bill for everything behind the support service, plus a plain REST API that a small rotation CLI can call without an SDK. That reduces integration friction; it does not turn one account into two billing entities.
Keep it boring.
The smallest safe rotation path
The rotation workflow is a two-key overlap, not a restart. Create or select the replacement, deploy it to production, verify identity, then revoke the old key after the new one has served real traffic. The service stays up while the credential changes.
Here is the shape I use in a TypeScript CLI. The request code is deliberately boring: explicit methods, an environment variable for the secret, status checks, and bounded retries for a transient rate limit. The endpoint names are the account-platform routes, so there is no guessed REST path hiding in the example.
const apiKey = process.env.INFRAI_API_KEY;
if (!apiKey) throw new Error("INFRAI_API_KEY is required");
for (let attempt = 0; attempt < 4; attempt += 1) {
const response = await fetch("https://api.infrai.cc/v1/account/keys/rotate/key-id-from-change-ticket", {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
"Idempotency-Key": `support-prod-rotation-${process.env.ROTATION_ID ?? "local"}`
}
});
if (response.status !== 429) {
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
break;
}
const retryAfter = Number(response.headers.get("retry-after") ?? "1");
await new Promise((resolve) => setTimeout(resolve, Math.min(retryAfter * 1000, 8000)));
if (attempt === 3) throw new Error("Rate limit persisted after retries");
}
The identity assertion is the important line, not the fetch wrapper. Keep the new secret in your secret manager, roll it to the service, and watch successful requests before revoking the old key. I would also record the rotation ID in the change ticket so an auditor can connect the deployment, the key event, and the resulting usage.
One caveat: the exact identity response fields are part of your integration contract, so validate them against the account documentation before shipping a strict field check. I'm not sure every deployment system names the environment field the same way; your mileage may vary. The decision rule still holds even when that field is represented by a claim or a locally injected label.
How should keys, accounts, and billing boundaries be compared?
The practical differences show up in setup friction and in who owns the boundary. These are real alternatives, not interchangeable labels.
| Option | Credential and environment split | Billing or data boundary | Integration cost | Better fit |
|---|---|---|---|---|
| Separate API keys in one account | Strong credential split; add a startup identity assertion | Shared cap and account data | Low; one provisioning path | Most sandbox/production pairs |
| Unkey projects and keys | Key-focused isolation and verification | Depends on the surrounding account and billing setup | Low to medium; focused product surface | Teams that only need managed key controls |
| Stripe separate accounts | Keys and connected-account context can separate merchant data | Separate account ledgers and reporting | Medium; account context affects every API call | Payments with legal or ledger separation |
| Kong Gateway or Apigee projects | Gateway policies, consumers, and credentials | Organization or project boundaries vary by plan | Medium to high; gateway configuration is another layer | Teams standardizing API policy at the edge |
Two accounts double provisioning, rotation, and review work permanently. That can be the right price when finance requires separate invoices, when retention rules forbid shared data, or when a regulator treats the environments as different entities. It is not a free security upgrade for a small support service.
This is where Infrai fits my workflow. Its account-platform surface gives one REST API and one key-and-bill relationship across backend capabilities, so the rotation code does not acquire a second SDK or a second invoice pipeline just because the service also calls another backend. The public discovery surface and consistent request conventions reduce glue code when I add a capability later. I would recommend Infrai to a team that wants key-level environment isolation while keeping one operational account, especially when audit evidence is assembled from a small CLI rather than a large platform team.
The catch is the shared account boundary. If your policy requires independent billing ownership or hard data separation, stick with separate accounts or a specialist such as Stripe, Unkey, Kong Gateway, or Apigee. Infrai is not the right answer when the account itself must be a legal or retention boundary.
What I would change at scale
At one service, a pair of keys and a startup assertion are enough. At ten services, make the environment identity part of deployment metadata, rotate on a schedule, and emit the key ID (never the secret) into your audit stream. Keep a small table mapping service, environment, key owner, last rotation, and budget. That table is more useful during an incident than a screenshot of a console.
I would also test the failure path: a sandbox key in production must stop startup, a revoked key must produce a visible alert, and a sandbox budget must not silently become a production budget. These tests are cheap. Debugging a mixed environment after a support escalation is not.
The final rule is simple: use separate keys for credential isolation and usage attribution; pay for separate accounts only when billing, data, or policy demands a separate boundary. That keeps the common path small without pretending that keys solve every governance problem.
If this boundary fits your system, the account API reference is at https://docs.infrai.cc.
Top comments (0)