1) Environment creation and variable mapping
An environment in Postman is a named set of variable values that represent a specific deployment (for example, dev, staging, production). The goal is to run the same requests and the same collection logic against different systems by changing only the environment selection, not the request definitions.
Create an environment for each deployment
- Create one environment named Dev and another named Staging (you can add Production later, but keep it locked down).
- In each environment, define the same variable names so requests can be identical across deployments.
Recommended variable map
Use a consistent naming scheme across environments. Typical variables include:
- baseUrl: root URL for the API (differs per environment).
- username, password: placeholders for credentials (values differ; often secrets).
- token: bearer token or session token (usually set at runtime).
- tenantId or accountId: if the API is multi-tenant (may differ).
- safeMode: a guard flag (e.g., true/false) used to block risky calls in production.
Map variables into requests
In requests, reference environment variables instead of hardcoding. Example patterns:
- URL:
{{baseUrl}}/v1/users - Authorization header:
Authorization: Bearer {{token}} - Login body:
{"username":"{{username}}","password":"{{password}}"}
Keep the variable names identical across environments; only the values change per environment.
2) Switching environments and validating which values are active
Switching environments changes which values resolve when Postman renders {{...}} variables. The most common failure mode is thinking you are on one environment while actually running another, so build quick checks into your workflow.
Continue in our app.
You can listen to the audiobook with the screen off, receive a free certificate for this course, and also have access to 5,000 other free online courses.
Or continue reading below...Download the app
Switch environments intentionally
- Select the active environment from the environment dropdown before running a request or collection.
- When using the Collection Runner, confirm the selected environment in the runner settings.
Validate active values inside requests
Add lightweight checks that confirm the request is hitting the expected deployment.
- Check the resolved URL in the Postman console (useful when debugging).
- Assert on a response field that differs by environment (e.g.,
environment,build,version), if your API provides it.
Example test that validates you are not accidentally hitting production (adapt to your API):
pm.test("Not calling production", function () { const baseUrl = pm.environment.get("baseUrl") || ""; pm.expect(baseUrl.toLowerCase()).to.not.include("prod");});If your API has a health endpoint that returns metadata, add a request like GET {{baseUrl}}/health and assert on environment markers:
pm.test("Health indicates expected environment", function () { const json = pm.response.json(); pm.expect(json).to.have.property("env"); // Example: env is "dev" or "staging" pm.expect(["dev","staging"]).to.include(json.env);});3) Separating secrets from non-secrets and reducing accidental production calls
Environment variables often mix non-sensitive configuration (like baseUrl) with sensitive values (like passwords or API keys). Treat them differently to reduce risk and prevent accidental production-impacting calls.
Separate secrets from non-secrets
- Keep non-secrets in the environment:
baseUrl,tenantId, feature flags, and safe guard variables. - Keep secrets out of shared environments whenever possible: passwords, long-lived API keys, private tokens.
- Prefer short-lived tokens generated at runtime (login request sets
{{token}}) instead of storing long-lived secrets.
Use guardrails to reduce accidental production calls
Even with correct variables, accidents happen (wrong environment selected, copied URL, or a runner configured incorrectly). Add explicit safety checks for requests that can mutate data (POST/PUT/PATCH/DELETE).
Example pre-request script guard: block destructive calls if the environment is production or if a safety flag is not enabled.
const baseUrl = (pm.environment.get("baseUrl") || "").toLowerCase();const safeMode = pm.environment.get("safeMode");const method = pm.request.method;const isWrite = ["POST","PUT","PATCH","DELETE"].includes(method);if (isWrite) { // Block if baseUrl looks like production or safeMode is not explicitly true const looksProd = baseUrl.includes("prod") || baseUrl.includes("production"); if (looksProd || safeMode !== true) { throw new Error("Write request blocked: enable safeMode=true and avoid production baseUrl."); }}Operationally, keep production environments read-only for most users: only include baseUrl and allow GET requests, or require a deliberate opt-in variable like allowWrites that defaults to false.
4) Using initial vs current values conceptually for safe sharing
Postman environment variables have two value slots: Initial and Current. Conceptually:
- Initial value: the shareable baseline configuration. Think “what teammates can safely see and sync.”
- Current value: your local, private runtime value. Think “what changes during runs (tokens) or what I don’t want to share (secrets).”
Safe sharing pattern
- Put non-sensitive defaults in Initial (e.g.,
baseUrlfor dev/staging,tenantId,safeMode). - Leave secrets blank in Initial and set them only in Current (e.g.,
password,apiKey). - Store short-lived tokens in Current only, and refresh them automatically when needed.
Example: a login request sets the token into the environment (current value) after a successful authentication:
// In Tests tab of the login requestconst json = pm.response.json();pm.test("Login succeeded", function () { pm.expect(pm.response.code).to.be.oneOf([200,201]); pm.expect(json).to.have.property("token");});pm.environment.set("token", json.token);This approach keeps the environment shareable without leaking credentials, while still enabling automation.
5) Troubleshooting environment-related failures (wrong URL, outdated tokens)
When a request fails after switching environments, the cause is often not the API itself but a mismatch in environment values. Use a consistent checklist to isolate the issue quickly.
Failure: wrong URL or wrong deployment
Symptoms: DNS errors, 404 on known endpoints, unexpected response schema, or hitting a different version of the API.
Checks:
- Verify the active environment selection (dropdown and runner settings).
- Inspect the resolved URL: confirm
{{baseUrl}}is correct for that environment. - Confirm the base path and version segment (e.g.,
/v1) matches that deployment. - If the API exposes a metadata endpoint, call it and assert on
env/version.
Fix: update baseUrl in the correct environment; ensure both environments have the same variable names (missing variables often resolve to blank and create malformed URLs).
Failure: outdated or invalid token
Symptoms: 401/403 responses after switching environments, or after some time has passed.
Checks:
- Confirm the token was generated for the same environment you are calling (dev token won’t work on staging).
- Check token expiry behavior; some systems invalidate tokens quickly.
- Verify the Authorization header is using
{{token}}and thattokenexists in the active environment.
Fix: re-run the login/auth request for the active environment and overwrite token in that environment’s current value. If you have multiple auth flows, ensure the correct one runs per environment.
Failure: credentials placeholders not set
Symptoms: login request fails (400/401), or token is never set.
Checks:
- Confirm
{{username}}and{{password}}are populated in the active environment’s current values. - Ensure you did not accidentally sync secrets into initial values; keep them local.
Fix: set current values locally; keep initial values blank for secrets.
Failure: environment variable drift
Symptoms: teammates get different results, or your run differs from yesterday’s run.
Checks:
- Compare variable names across environments; ensure no typos (
baseURLvsbaseUrl). - Look for stale current values (old tokens, old tenant IDs).
- Reset current values for non-secrets if needed to match initial values.
Fix: standardize variable names, and adopt a routine: non-secrets in initial values, runtime values in current values, and refresh tokens via auth requests.
Guided lab: Run the same requests against Dev and Staging
This lab configures two environments and proves that identical requests behave differently based on environment values, while keeping the request definitions unchanged.
Lab setup: create two environments
- Create environment Dev with variables:
baseUrl,username,password,token,safeMode. - Set
baseUrlinitial value to your dev API root (example:https://dev.api.example.com). - Set
safeModeinitial value totrue. - Leave
tokeninitial value blank. - Set
username/passwordin current values only.
- Create environment Staging with the same variable names.
- Set
baseUrlinitial value to your staging API root (example:https://staging.api.example.com). - Set
safeModeinitial value totrue. - Leave
tokeninitial value blank. - Set
username/passwordin current values only (staging credentials may differ).
Lab requests: build an identical mini-flow
Create (or reuse) a small set of requests inside a collection. Do not hardcode any environment-specific values.
Request A: Health/Info check
Request: GET {{baseUrl}}/health (or your API’s equivalent).
Tests: verify the endpoint responds and capture an environment marker if available.
pm.test("Health is reachable", function () { pm.expect(pm.response.code).to.equal(200);});let json = {};try { json = pm.response.json(); } catch (e) {}if (json && json.env) { pm.environment.set("reportedEnv", json.env);}Request B: Login/Auth to obtain token
Request: POST {{baseUrl}}/auth/login (adjust path/body to your API). Body uses placeholders:
{ "username": "{{username}}", "password": "{{password}}"}Tests: store token in the active environment.
pm.test("Auth succeeded", function () { pm.expect(pm.response.code).to.be.oneOf([200,201]);});const json = pm.response.json();pm.environment.set("token", json.token);Request C: Authenticated read request
Request: GET {{baseUrl}}/v1/me (or any endpoint that returns user/account data).
Header: Authorization: Bearer {{token}}
Tests: verify the call is authorized and capture a field that should differ between dev and staging (for example, tenant ID, user ID range, or a banner field).
pm.test("Authorized", function () { pm.expect(pm.response.code).to.equal(200);});const json = pm.response.json();if (json && json.tenantId) { pm.environment.set("observedTenantId", json.tenantId);}Request D (optional): A guarded write request
Request: POST {{baseUrl}}/v1/items with a simple payload. Add the guard pre-request script shown earlier to prevent unsafe execution if safeMode is not enabled or if the base URL looks like production.
Purpose: practice safety constraints and confirm your guardrail works.
Run the lab against Dev
- Select the Dev environment.
- Run Request A, then B, then C (and D if included).
- Confirm
{{token}}is set in Dev current values after login. - Record any environment marker from the response (for example,
reportedEnvorobservedTenantId).
Run the same lab against Staging (no request changes)
- Switch to the Staging environment.
- Run the exact same requests in the same order.
- Confirm a new token is stored in Staging current values (separate from Dev).
- Compare the captured markers (for example,
reportedEnvshould differ, or returned IDs/data should reflect staging).
Verification checklist
- The request URLs remain identical except for
{{baseUrl}}resolution. - Dev and Staging tokens are different and stored in their respective environments.
- At least one response attribute differs between environments (env marker, dataset differences, or version/build).
- Guarded write requests are blocked unless
safeModeis explicitly enabled, and they never run against a production-looking base URL.