On this page10 sections

Postman Environments & Variables

Parameterise a Postman collection with environments and variables — creating reusable dev and staging configs, scoping variables correctly, and chaining an auth token from a login request into all subsequent requests automatically.

Role

Manual QA engineer

Difficulty

Intermediate

Time limit

~60 min

Category

api client

Postman (free desktop app at postman.com/downloads or web at app.getpostman.com)

Scenario

Your team has a Postman collection for the Reqres.in API, but every tester has hardcoded the base URL and re-enters the auth token manually for each session. As the QA lead, you need to refactor the collection to use environments so it can be pointed at different base URLs (dev vs. staging) without editing individual requests, and to chain the auth token from the login request into subsequent requests automatically using a pre-request or post-response script. Your goal is to produce a collection that a new team member can clone, select an environment, and run immediately — with no manual URL or token substitution required.

Requirements

  • 1.Create two Postman environments named 'Reqres – Dev' and 'Reqres – Staging'; in both environments define a variable named baseUrl set to https://reqres.in (in a real project these would differ; here they are the same to demonstrate the pattern); also define a variable named authToken with an empty initial value
  • 2.Refactor all request URLs in the collection to use the {{baseUrl}} environment variable (e.g. {{baseUrl}}/api/users) so no request contains a hardcoded host
  • 3.Add a collection-level variable named defaultTimeout set to 5000 (milliseconds) and a collection-level variable named apiVersion set to api; update at least two request URLs to use {{apiVersion}} (e.g. {{baseUrl}}/{{apiVersion}}/users)
  • 4.In the POST /api/login request, add a Tests-tab script that extracts the token from the response body and stores it in the authToken environment variable: pm.environment.set('authToken', pm.response.json().token); verify the variable is populated by checking the environment's current value after running the request
  • 5.Add an Authorization header to at least two subsequent requests (e.g. GET /api/users and GET /api/users/2) set to Bearer {{authToken}} — on Reqres.in these endpoints do not enforce auth, but the header pattern is what you are practising
  • 6.Write a short variable-scope summary (3–5 sentences or a table) explaining the four Postman variable scopes — Global, Collection, Environment, Local — their precedence order, and when you would use each

Starter data

  • If you completed postman-collection-basics, start from that exported collection; otherwise create a fresh collection with the Users and Auth folders described there
  • Environment variable syntax in Postman: wrap the variable name in double curly braces in any URL, header value, or body field — {{baseUrl}}, {{authToken}}, {{apiVersion}}
  • Token extraction script (Tests tab on POST /api/login): const responseJson = pm.response.json(); pm.environment.set('authToken', responseJson.token); pm.test('Token saved', () => pm.expect(responseJson.token).to.be.a('string'));
  • Variable scope precedence (highest to lowest): Local > Data > Environment > Collection > Global — a variable set at Environment scope overrides the same name at Collection scope; this is why per-environment overrides work without touching the collection
  • Checking current variable values: in Postman desktop, click the 'eye' icon (Environment Quick Look) in the top-right corner — 'Initial Value' is what gets shared when the environment is exported; 'Current Value' is your local runtime value and is NOT exported
  • Reqres.in POST /api/login credentials: { "email": "eve.holt@reqres.in", "password": "cityslicka" } → returns { "token": "QpwL5tpe83ilfWH2" }

Expected deliverables

  • Two exported environment files (JSON): 'Reqres – Dev' and 'Reqres – Staging', each with baseUrl and authToken defined (authToken initial value left empty)
  • Updated collection export with all request URLs refactored to use {{baseUrl}}/{{apiVersion}} — no hardcoded hosts in any request URL
  • POST /api/login Tests tab containing the token-extraction script, with a note on what the script does and which scope it writes to
  • At least two requests (e.g. GET /api/users, GET /api/users/2) with an Authorization: Bearer {{authToken}} header
  • Variable-scope summary (3–5 sentences or a table) covering Global, Collection, Environment, and Local scopes and their precedence

Evaluation rubric

DimensionWhat reviewers look for
Correct environment setupAre both environments defined with the correct variable names and values? Does switching environments in Postman cause all {{baseUrl}} references to resolve to the correct host without editing any individual request? An environment with a variable named 'base_url' (underscore) instead of 'baseUrl' (camelCase) will cause every parameterised request to show an unresolved {{baseUrl}} literal — the variable name must exactly match the placeholder in the request URLs.
Variable scopingAre variables placed at the correct scope? baseUrl and authToken belong at environment scope (per-environment override). apiVersion and defaultTimeout belong at collection scope (shared across environments). Using global variables for per-environment data means every environment shares the same value — defeating the purpose of environments. The scope summary must correctly state that Environment overrides Collection, which overrides Global.
Working token chainDoes running POST /api/login followed by GET /api/users — in a single Postman run or manually in sequence — result in {{authToken}} being populated and the Authorization header being sent with the actual token value? A token-chain that requires the tester to manually copy the token from the login response and paste it into the environment is not a working chain; the pm.environment.set() script must do the transfer automatically.
No hardcoded hosts in URLsDo all request URLs use {{baseUrl}} rather than a literal https://reqres.in? Even one hardcoded URL breaks the environment-switching promise — switching to 'Reqres – Staging' would point most requests to the staging host but leave that one request pointing at prod. The collection must be consistent: zero literal hostnames in any URL field.
Variable-scope understandingDoes the scope summary accurately explain all four scopes and when to use each? A common mistake is describing Global and Collection as interchangeable. The key distinction: Global variables persist across all workspaces and are suitable for cross-collection tokens; Collection variables scope to a single collection and are suitable for shared configuration like API version or timeout; Environment variables are the right home for per-environment data like base URLs and credentials.

Sample solution outline

  • Environment 'Reqres – Dev': baseUrl = https://reqres.in (initial + current), authToken = '' (initial value empty; current value populated at runtime by the login script)
  • Environment 'Reqres – Staging': identical structure — baseUrl = https://reqres.in in this exercise; in a real project this would be https://staging.yourapp.com; authToken = ''
  • Collection-level variables: apiVersion = api (no leading slash), defaultTimeout = 5000; example refactored URL: {{baseUrl}}/{{apiVersion}}/users?page=2 resolves to https://reqres.in/api/users?page=2
  • POST /api/login Tests script: const j = pm.response.json(); pm.environment.set('authToken', j.token); pm.test('Token is a non-empty string', () => { pm.expect(j.token).to.be.a('string').and.not.empty; }); — after running, Environment Quick Look shows authToken = QpwL5tpe83ilfWH2 under Current Value
  • GET /api/users Authorization header: Type = Bearer Token, Token = {{authToken}}; Postman resolves this to 'Bearer QpwL5tpe83ilfWH2' in the request; confirmed via Postman Console (View → Show Postman Console) showing the outgoing Authorization header
  • Scope summary: Global — workspace-wide, use for tokens shared across multiple collections; Collection — scoped to one collection, use for API version or timeout; Environment — per-environment config (base URL, credentials); Local — in-request scripts only, not persisted after the request completes. Precedence: Local > Data > Environment > Collection > Global.

Common mistakes

  • Naming the environment variable 'base_url' or 'baseURL' but referencing it as {{baseUrl}} in request URLs — Postman variable names are case-sensitive and the placeholder must match exactly; a mismatch results in the literal {{baseUrl}} appearing in the sent URL
  • Storing the auth token at Global scope instead of Environment scope — global variables persist across all collections and workspaces; using them for per-environment tokens means dev and staging share the same token, which breaks the isolation that environments are designed to provide
  • Setting the authToken initial value to the actual token string — initial values are exported with the environment file and may be committed to version control; sensitive tokens should only be in the Current Value field, which Postman never exports
  • Running the collection runner before running POST /api/login — if the login request is not the first request in the run order, {{authToken}} is empty when the first authenticated request executes; either put the login request first in the folder order or use a pre-request script on the collection to check whether the token is already set
  • Confusing Collection variables with Environment variables — collection variables are shared across all environments and cannot be overridden per environment; if you put the base URL in a collection variable rather than an environment variable, switching environments will not change the host
  • Not verifying the token was stored by checking the Environment Quick Look — writing the pm.environment.set() script is not sufficient proof; open the environment panel, run the login request, and confirm the Current Value field is populated with the token string before adding it to other requests

Submission checklist

  • Two exported environment JSON files: 'Reqres – Dev' and 'Reqres – Staging', each with baseUrl and authToken
  • All request URLs refactored to use {{baseUrl}} — zero hardcoded hostnames in any URL field
  • At least two URLs also using {{apiVersion}} collection variable
  • POST /api/login Tests script with pm.environment.set('authToken', ...) and a pm.test assertion
  • At least two requests with Authorization: Bearer {{authToken}} header
  • Variable-scope summary covering all four scopes with precedence order
  • Screenshot or note confirming the authToken Current Value is populated after running POST /api/login

Extension ideas

  • +Add a collection-level pre-request script that checks whether authToken is set and, if not, sends a login request using pm.sendRequest() to obtain the token automatically before any authenticated request runs — this removes the requirement to manually run login first
  • +Create a third environment named 'Reqres – Local' with baseUrl pointing to http://localhost:3000 (simulating a locally running server) and document what changes when you switch to this environment while the local server is not running
  • +Export both environments and the collection, commit them to a git repository, and write a short note explaining which fields are safe to commit (initial values) and which must never be committed (current values containing real credentials or tokens)