Every API team hits the same wall. The first requests you build point at one server, with one token pasted into a header. Then staging appears. Then production. Suddenly you’re editing URLs by hand before every run, and someone tests a delete endpoint against prod because a base URL was stale. API environment variables exist to kill this entire class of mistake, and Apidog builds them into the core of the product instead of bolting them on.
This guide shows you how to set up dev, staging, and prod environments in Apidog, store tokens and API keys as variables instead of hardcoded strings, keep real secrets off the cloud with local values, and pass environments into CI through the Apidog CLI. If you want the broader picture of what an API client with environment and secrets management should handle, we’ve covered that separately. Here we get practical.
Why hardcoded URLs and tokens break with a second environment
With one environment, hardcoding works fine. https://api.acmepay.dev sits in every request, your token sits in every Authorization header, and nothing hurts yet.
The pain starts the moment a second environment shows up:
- Every request needs editing to retarget. Fifty endpoints pointed at dev means fifty URL edits to test staging, then fifty more to switch back. You’ll miss one.
- Tokens leak across boundaries. A prod API key pasted into a request body gets saved with the project, shared with the team, and exported with the collection. The Twelve-Factor App methodology is blunt about this: config varies between deploys, code doesn’t, so config never belongs in the artifact you share.
- Runs stop being reproducible. When the URL and credentials live inside each request, “run the smoke tests against staging” becomes a manual find-and-replace ritual instead of a one-click switch.
The fix is old and proven: separate the request definition (method, path, body, assertions) from the deployment context (base URL, credentials, environment-specific IDs). Requests stay identical everywhere. Only the context changes.
How Apidog models environments and variables
Apidog splits the problem into two pieces that work together.
An environment is a named context, like Dev, Staging, or Prod. Each environment carries its own base URL (the server requests get sent to) and its own set of variable values. Switch the environment and every request in the project retargets at once, as the environment management docs describe.
A variable is a named placeholder you reference as {{variable_name}} anywhere a value goes: URLs, query params, headers, request bodies, and scripts. At runtime, Apidog resolves the placeholder against the active environment and the other scopes in play.
Variable scopes and which one wins
Apidog resolves variables through five scopes. From lowest to highest priority: global, module, environment, data, and local.
| Scope | Lives where | Typical use |
|---|---|---|
| Global | Whole project, every environment | Constants like {{api_version}} |
| Module | One module of the project | Per-service settings in a microservice project |
| Environment | The active environment only | {{base_url}}, {{auth_token}}, {{merchant_id}} |
| Data | External CSV/JSON files in test runs | Row-by-row test inputs |
| Local (temporary) | One request or test run, then gone | A token extracted mid-scenario |
The priority order matters in practice. Define {{auth_token}} as a global fallback and it works everywhere, but the moment your Staging environment defines its own {{auth_token}}, the environment value wins while Staging is active. That’s exactly what you want: shared defaults below, environment-specific overrides on top. For a deeper walkthrough of each scope, see our guide to mastering variables in Apidog.
One behavior trips people up: local variables are temporary by design. Set one in a script and it disappears when the run completes. That’s a feature for scratch values inside a test scenario, and a bug in your mental model if you expected it to persist. Anything you need tomorrow belongs in an environment or global variable.
Set up dev, staging, and prod in Apidog
Here’s the workflow for a payments API with three deployments.
1. Create the three environments
Open environment management from the top-right of the project and create a new environment for each deployment. Give each one a name and a base URL:
Dev→https://api-dev.acmepay.devStaging→https://api-staging.acmepay.devProd→https://api.acmepay.com
Keep base URLs protocol-prefixed and without a trailing slash, so paths concatenate cleanly.
2. Define the same variable names in each environment
Consistency is the whole trick. Every environment defines the same variable names with different values:
| Variable | Dev | Staging | Prod |
|---|---|---|---|
{{auth_token}} |
dev token | staging token | prod token |
{{merchant_id}} |
mrc_test_449 |
mrc_stg_449 |
mrc_live_8821 |
{{webhook_secret}} |
dev secret | staging secret | prod secret |
3. Reference variables in requests, never raw values
A request to create a charge now looks like this everywhere:
POST /v1/charges
Authorization: Bearer {{auth_token}}
{
"merchant_id": "{{merchant_id}}",
"amount": 1999,
"currency": "usd"
}
The base URL doesn’t appear at all; Apidog prepends the active environment’s base URL automatically. Nothing in the request definition names an environment, which is what makes it portable.
4. Switch with the selector
The environment selector sits in the top-right corner of the Apidog window. Pick Staging and every request, test scenario, and script in the project resolves against staging’s base URL and staging’s variable values. No editing, no find-and-replace. If you’re weighing what should live in which deployment tier, our comparison of sandbox vs test environments covers how teams typically split them.
Coming from Postman? Your existing environments carry over. The Postman migration guide walks through importing collections and environments in a few clicks, variable values included.
Keep secrets local: shared values vs local values
This is the part most teams get wrong, and the part where Apidog’s design earns its keep.
Every environment and global variable in Apidog can hold two values, as documented in the variables reference:
- Shared value: synchronized with Apidog’s servers and visible to everyone on the project.
- Local value: stored only in your client’s cache on your machine. It never syncs to the cloud and teammates never see it.
When both exist, your client uses the local value. So the safe pattern for secrets is simple:
- Create the variable, for example
{{auth_token}}, in each environment. - Leave the shared value empty, or set it to a placeholder like
SET_LOCALLY. - Put the real token in the local value on your own machine.
The variable structure syncs to the team. The secret doesn’t. Each engineer drops in their own credentials once, and every shared request works for them immediately. This lines up with the OWASP Secrets Management Cheat Sheet: scope secrets tightly, share them through controlled channels, and keep them out of anything that gets replicated broadly.
Two caveats worth knowing. Local values live in the client cache, so clearing Apidog’s cache deletes them, and moving to a new laptop means re-entering them. Budget five minutes for that, not five hours of incident review because a prod key synced to twelve people.
You can also mark an entire environment as private instead of shared. A Prod environment visible only to the two people who deploy is a legitimate setup, and it stacks with local values for defense in depth.
Use environments in test scenarios and CI
Environments carry straight into Apidog’s test scenarios. Build a scenario once (create charge, poll status, assert settlement), then choose which environment to run it against at execution time. The same scenario becomes your dev smoke test and your staging regression suite.
Scripts read and write the same scopes. A post-processor that captures a fresh token from a login response looks like this:
const body = pm.response.json();
pm.environment.set("auth_token", body.access_token);
Later requests in the scenario resolve {{auth_token}} to the captured value. For patterns like pulling request params into scripts, see retrieving request params in pre/post-request scripts.
For CI, the Apidog CLI takes the environment as a flag:
apidog run --access-token $APIDOG_ACCESS_TOKEN \
-t 637132 \
-e 358171 \
--env-var "auth_token=$STAGING_API_TOKEN"
-e picks the environment by ID. Note the CLI resolves shared values, not your machine’s local values, which is correct behavior: your personal secrets shouldn’t be reachable from a build agent anyway. Inject the real credentials at runtime instead, with --env-var and --global-var overrides in key=value form, or --variables to load a whole file. Store the actual secrets in your CI provider’s secret store (GitHub Actions secrets, GitLab CI variables) and pass them through. The pipeline never contains a token in plain text, and rotating a credential means updating one CI secret.
The team workflow that falls out of this
Put together, the division of labor is clean:
- Shared, synced: environment names, base URLs, variable names, placeholder shared values, test scenarios.
- Personal, local: each engineer’s tokens and keys as local values.
- CI-owned: pipeline credentials in the CI secret store, injected via CLI flags.
A new teammate joins, opens the project, and sees three ready-made environments with every variable named and documented. They paste their own dev token into one local value field and start working. Nobody DMs a prod key. Nobody maintains a “current staging URL” wiki page that drifts out of date.
Common pitfalls to avoid
- Committing real tokens into shared values. The most common mistake by far. If a secret needs to reach teammates, it goes through a password manager or vault, not through a synced variable. Audit your shared values once; anything that looks like a live credential should move to local values and get rotated.
- Forgetting which environment is active. Muscle memory sends requests before eyes check the selector. Make destructive operations harder to fat-finger: keep
Prodprivate to fewer people, and give prod-only variables distinct names or placeholder shared values so a wrong-environment run fails loudly on auth instead of succeeding quietly. - Expecting temporary variables to persist. Local-scope variables set during a run vanish when it ends. Promote anything durable to environment scope explicitly in your script.
- Different variable names across environments. If dev calls it
{{token}}and staging calls it{{auth_token}}, switching environments breaks half your requests. Same names everywhere, different values only. - One giant environment for everything. If you’re stuffing
dev_base_urlandprod_base_urlinto a single environment, you’ve rebuilt the hardcoding problem with extra steps. One environment per deployment context.
Ready to set this up? Download Apidog for free, create your three environments, and move your first token into a local value. It takes about ten minutes for an existing project.
FAQ
How do I keep secrets out of shared Apidog projects?
Store them as local values. Every variable has a shared value (synced to the team) and a local value (cached only on your machine). Leave the shared value as a placeholder and keep the real token local. For extra isolation, mark sensitive environments like Prod as private so only specific people see them at all.
What’s the difference between global and environment variables?
Global variables apply across the whole project regardless of which environment is active; use them for values that never change between deployments, like an API version string. Environment variables belong to one environment and win over globals when both define the same name. Our variables guide breaks down all five scopes, including module, data, and local.
Why does my test pass in the Apidog client but fail in CI?
Usually because the client resolves local values while the CLI resolves shared values. If your token lives only in a local value, the CLI sees an empty or placeholder variable. Pass the credential explicitly in the pipeline with --env-var "auth_token=$YOUR_CI_SECRET" so CI supplies its own secret at runtime.
Can I move my Postman environments into Apidog?
Yes. Apidog imports Postman collections and environments directly, keeping variable names and values intact, so your {{base_url}} references keep working after migration. Review imported values afterward and move any real credentials into local values, since Postman exports can carry secrets in plain text.



