Resource library

QA How-To

How to Chain Requests in Postman Using Variables from a Response

Learn to chain requests in Postman by saving a token and order ID from responses, reusing them in later requests, and verifying the full collection run.

18 min read | 3,021 words

TL;DR

To chain requests in Postman, extract a value in the first request's post-response script, save it with pm.collectionVariables.set(), and reference it as {{name}} in later requests. Run the collection in order and assert that each handoff used the expected value.

Key Takeaways

  • Capture response fields with pm.response.json() and pm.collectionVariables.set().
  • Place producer requests before consumers in the saved collection order.
  • Use {{sessionToken}} in the Authorization header and {{orderId}} in resource URLs.
  • Assert response status, field shape, and resource identity before trusting a variable.
  • Run the collection twice to expose stale IDs and scope collisions.
  • Clear transient variables after successful cleanup and inspect failures before retrying.

To chain requests in Postman, save a value from one response in a post-response script, then reference it as {{variableName}} in the next request. Run the requests in collection order so the producer runs before the consumer. This guide builds a local order workflow that captures a session token and an order ID, confirms the order, reads it back, and deletes it.

The example uses a small Node.js API that you run on your own machine. Its data lives in memory, which makes the expected responses deterministic and keeps test records out of a shared service. You will see both how to click through the chain during development and how to run the complete collection as a regression check.

For the broader API testing workflow, see the Postman tutorial for beginners and the API testing roadmap. This guide focuses on one practical question: how does data from a response become an input to a later request?

What You Will Build

You will assemble a collection named Order Chain containing five requests in this order:

  1. 01 Create session receives a token and stores sessionToken.
  2. 02 Create order sends that token, receives an ID, and stores orderId.
  3. 03 Confirm order updates the order identified by orderId.
  4. 04 Read order verifies that the confirmed order is the same order you created.
  5. 05 Delete order removes that record and clears the temporary variables.

The dependency graph is simple: session -> create -> confirm -> read -> delete. A failed extraction must fail a test instead of quietly forwarding an empty value. The collection will use the exact same requests when you press Send individually and when you use the Collection Runner.

Mechanism What it does Use it here?
{{name}} in a URL, header, or body Resolves a variable before the request is sent Yes, for baseUrl, sessionToken, and orderId
Post-response script Reads the completed response and saves values Yes, after session and order creation
Pre-request script Runs before a request, often to prepare data Optional, for a missing-variable guard
pm.execution.setNextRequest() Changes collection-run order No, the saved collection order already models this workflow

Postman's variable documentation explains scope and interpolation. Its collection-run documentation covers running the requests together. You need both concepts: storing a value without running the next request does not execute a chain.

Prerequisites

Install the current Postman desktop app and a maintained Node.js release. This tutorial pins no fabricated version number. Record your exact installed versions before starting: run node --version in a terminal and open Postman's Settings or About view to read the app version. If you also install the Postman CLI for the optional terminal run, use the CLI version that matches your installed toolchain and check it with postman --version.

You also need a terminal, curl, permission to listen on 127.0.0.1:3000, and a writable scratch directory outside any important project. The API below uses Node's built-in http and crypto modules, so there are no npm dependencies or package pins. Run the server and Postman on the same computer. A cloud runner cannot reach your laptop's loopback address.

Create a fresh Postman collection for this exercise. Keep its variable names distinct from those in your other workspaces. If an active environment already defines baseUrl, sessionToken, or orderId, deselect it or rename those variables. Postman resolves narrower scopes ahead of collection scope, so a same-named environment value could silently override your collection value.

Verification command: run node --version && curl --version in the terminal. Both should print installed tool information. If either command is missing, install that tool before continuing.

Step 1: Start a Local API for the Chain

Make an empty scratch folder and save the following as server.mjs. The server issues an opaque session token, accepts an authenticated order, updates it, returns it, and deletes it. It deliberately binds only to loopback. State resets whenever you restart the process, which is useful for repeatable learning but means old IDs become invalid.

mkdir -p postman-order-chain
cd postman-order-chain
cat > server.mjs <<'EOF'
import http from 'node:http';
import { randomUUID } from 'node:crypto';

const sessions = new Set();
const orders = new Map();

function json(res, status, value) {
  res.writeHead(status, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify(value));
}

async function readJson(req) {
  let raw = '';
  for await (const chunk of req) raw += chunk;
  try {
    return raw ? JSON.parse(raw) : {};
  } catch {
    return null;
  }
}

http.createServer(async (req, res) => {
  const path = new URL(req.url, 'http://127.0.0.1:3000').pathname;
  if (req.method === 'GET' && path === '/health') {
    return json(res, 200, { status: 'ok' });
  }
  if (req.method === 'POST' && path === '/sessions') {
    const token = randomUUID();
    sessions.add(token);
    return json(res, 201, { token });
  }

  const token = req.headers.authorization?.replace(/^Bearer /, '');
  if (!sessions.has(token)) return json(res, 401, { error: 'unauthorized' });

  if (req.method === 'POST' && path === '/orders') {
    const input = await readJson(req);
    if (!input || typeof input.sku !== 'string' ||
        !Number.isInteger(input.quantity) || input.quantity < 1) {
      return json(res, 400, { error: 'invalid order' });
    }
    const order = {
      id: randomUUID(), sku: input.sku, quantity: input.quantity,
      status: 'draft', owner: token
    };
    orders.set(order.id, order);
    return json(res, 201, {
      id: order.id, sku: order.sku,
      quantity: order.quantity, status: order.status
    });
  }

  const match = path.match(/^\/orders\/([^/]+)$/);
  if (!match) return json(res, 404, { error: 'not found' });
  const order = orders.get(match[1]);
  if (!order || order.owner !== token) {
    return json(res, 404, { error: 'not found' });
  }
  const visible = () => ({
    id: order.id, sku: order.sku,
    quantity: order.quantity, status: order.status
  });
  if (req.method === 'GET') return json(res, 200, visible());
  if (req.method === 'PATCH') {
    const input = await readJson(req);
    if (!input || input.status !== 'confirmed') {
      return json(res, 400, { error: 'invalid status' });
    }
    order.status = 'confirmed';
    return json(res, 200, visible());
  }
  if (req.method === 'DELETE') {
    orders.delete(order.id);
    res.writeHead(204);
    return res.end();
  }
  return json(res, 405, { error: 'method not allowed' });
}).listen(3000, '127.0.0.1', () => {
  console.log('Order API listening on http://127.0.0.1:3000');
});
EOF
node server.mjs

Leave that terminal running. The API does not persist data, and its token is only an exercise credential. Do not use this example's authentication model as a production design. A production service needs token expiry, identity validation, authorization rules, and persistent storage.

Verification command: in a second terminal, curl -i http://127.0.0.1:3000/health. Expect HTTP 200 and {"status":"ok"}. If the connection is refused, inspect the server terminal first. If port 3000 is occupied, change the listening port in the server and use the same port for baseUrl below.

Step 2: Configure the Collection and Capture a Session

Create the Order Chain collection. In its Variables tab, add baseUrl with local value http://127.0.0.1:3000. Add sessionToken and orderId with empty values, then save. Keep this example collection on the No Auth setting. The individual order requests will send an explicit Authorization header so it is clear where interpolation happens.

Add a request named 01 Create session, method POST, URL {{baseUrl}}/sessions. No request body is required. Open Scripts > Post-response and paste this script:

pm.test('session response is 201', () => {
  pm.response.to.have.status(201);
});

const session = pm.response.json();
pm.test('session has a nonempty token', () => {
  pm.expect(session.token).to.be.a('string').and.not.empty;
});

if (pm.response.code === 201 && typeof session.token === 'string'
    && session.token.length > 0) {
  pm.collectionVariables.set('sessionToken', session.token);
}

The tests establish the contract before the token becomes a dependency. The guarded assignment prevents an error response from overwriting a valid token with undefined. Because a previous token could still remain after a failed request, treat any failed assertion as a stop signal; do not manually continue the sequence. For an even stricter run, clear both temporary variables before each fresh collection run, as shown later.

Click Save, then Send. The response should contain a token string. Open the collection's Variables tab and confirm sessionToken has a local value. Postman saves collection variables via pm.collectionVariables.set; the scripting reference documents that API.

Verification command: curl -i -X POST http://127.0.0.1:3000/sessions should show HTTP 201 and a token-shaped UUID. This curl check verifies the producer endpoint. The Postman test result and the Variables tab verify that extraction worked, which curl alone cannot prove.

Step 3: Create an Order and Capture Its ID

Add 02 Create order immediately after the session request. Set method POST, URL {{baseUrl}}/orders, header Authorization: Bearer {{sessionToken}}, and header Content-Type: application/json. Select Body > raw > JSON and enter:

{"sku":"QA-BOOK","quantity":2}

Paste this into Scripts > Pre-request. It turns a missing upstream value into a clear failure before you inspect a 401 response. It does not create a token, and it does not hide the dependency.

const token = pm.collectionVariables.get('sessionToken');
if (!token) {
  throw new Error('Run 01 Create session before 02 Create order');
}

In Scripts > Post-response, validate the new resource and save its ID:

pm.test('order creation returns 201', () => {
  pm.response.to.have.status(201);
});

const created = pm.response.json();
pm.test('created order matches the request', () => {
  pm.expect(created.id).to.be.a('string').and.not.empty;
  pm.expect(created.sku).to.eql('QA-BOOK');
  pm.expect(created.quantity).to.eql(2);
  pm.expect(created.status).to.eql('draft');
});

if (pm.response.code === 201 && typeof created.id === 'string'
    && created.id.length > 0) {
  pm.collectionVariables.set('orderId', created.id);
}

Save and Send. Inspect the request in the Postman Console if you need to prove that {{sessionToken}} was replaced in the header. Avoid copying its actual value into screenshots or shared reports. The response id is generated by the server, so hardcoding one would make the next run fail after the in-memory server restarts.

Verification command: after creating the session in Postman, run curl -i -X POST http://127.0.0.1:3000/orders -H "Authorization: Bearer YOUR_SESSION_TOKEN" -H "Content-Type: application/json" -d '{"sku":"QA-BOOK","quantity":2}', replacing the placeholder with your local token. Expect HTTP 201. This creates a separate order; the Postman response test and orderId variable still verify the order you will continue using.

Step 4: Confirm the Exact Order You Created

Add 03 Confirm order after creation. Set method PATCH, URL {{baseUrl}}/orders/{{orderId}}, Authorization header Bearer {{sessionToken}}, and Content-Type: application/json. Use this raw JSON body:

{"status":"confirmed"}

The path parameter is the actual handoff from the prior response. A successful status check alone is weak here: a server could update a different record and still return 200. Compare the returned ID with the collection variable. Use this post-response script:

pm.test('confirmation returns 200', () => {
  pm.response.to.have.status(200);
});

const confirmed = pm.response.json();
pm.test('confirmation changed the captured order', () => {
  pm.expect(confirmed.id).to.eql(pm.collectionVariables.get('orderId'));
  pm.expect(confirmed.status).to.eql('confirmed');
  pm.expect(confirmed.sku).to.eql('QA-BOOK');
  pm.expect(confirmed.quantity).to.eql(2);
});

Save and Send only after orderId is populated. If you open the request URL field and hover over {{orderId}}, Postman shows the resolved variable and its scope. That is a quick diagnosis when the API reports 404. The update response provides an immediate assertion; the next step independently reads the order to confirm stored state.

Verification command: curl -i -X PATCH http://127.0.0.1:3000/orders/YOUR_ORDER_ID -H "Authorization: Bearer YOUR_SESSION_TOKEN" -H "Content-Type: application/json" -d '{"status":"confirmed"}'. Use the values shown in your collection Variables tab and expect HTTP 200 with the same ID and "status":"confirmed". This replays the update safely for this example because assigning the same status again has the same final state.

Step 5: Read Back the Result, Not Just the Update Response

Add 04 Read order with method GET, URL {{baseUrl}}/orders/{{orderId}}, and Authorization header Bearer {{sessionToken}}. There is no body. This request demonstrates why chaining is more than passing strings: the read operation checks persistence separately from the mutation response.

Use this post-response script:

pm.test('read returns 200', () => {
  pm.response.to.have.status(200);
});

const saved = pm.response.json();
pm.test('stored order is the confirmed order', () => {
  pm.expect(saved.id).to.eql(pm.collectionVariables.get('orderId'));
  pm.expect(saved.status).to.eql('confirmed');
  pm.expect(saved.sku).to.eql('QA-BOOK');
  pm.expect(saved.quantity).to.eql(2);
});

Save and Send. The status assertion distinguishes a missing resource from malformed response content. The ID assertion connects the read to the create response, and the status assertion connects it to the update. Together, these checks catch a wrong interpolation, an update that did not persist, or a response generated from the wrong order.

Verification command: curl -i http://127.0.0.1:3000/orders/YOUR_ORDER_ID -H "Authorization: Bearer YOUR_SESSION_TOKEN". Expect HTTP 200, the same ID, and confirmed status. If curl succeeds but Postman fails, inspect variable scope and the resolved Postman URL rather than changing the server. If both fail, check whether the server was restarted after order creation.

Step 6: Delete the Order and Clear Transient Values

Add 05 Delete order as the last request. Set method DELETE, URL {{baseUrl}}/orders/{{orderId}}, and Authorization header Bearer {{sessionToken}}. The example API returns 204 No Content, so do not call pm.response.json() on this response. Parse only when an endpoint actually promises JSON.

Use this post-response script:

pm.test('deletion returns 204', () => {
  pm.response.to.have.status(204);
});

if (pm.response.code === 204) {
  pm.collectionVariables.unset('orderId');
  pm.collectionVariables.unset('sessionToken');
}

Save and Send. The cleanup runs only after a successful delete. An empty orderId after success is intentional: the next collection run should obtain a fresh ID from the new create response. Clearing the token also prevents a later manual request from accidentally using a prior session when the producer failed.

Verification command: use curl -i http://127.0.0.1:3000/orders/YOUR_ORDER_ID -H "Authorization: Bearer YOUR_SESSION_TOKEN" with the values you recorded before deletion. Expect HTTP 404 after Postman deleted the record. The Variables tab should show no local orderId or sessionToken value. A 401 instead of 404 usually means the token you supplied to curl was mistyped; it does not prove deletion.

Step 7: Chain Requests in Postman with the Collection Runner

Open the collection and select Run. Choose a local functional run, one iteration, and the five requests in their saved order. Check the order in the runner before starting. Postman normally follows collection order; pm.execution.setNextRequest() is useful only when you intentionally need branching or looping. The request-order guide says that method affects collection runs, not a single click on Send.

Start the run. Expect five successful requests and all scripted tests to pass. The first post-response script writes sessionToken before the second request is prepared. The second writes orderId before the third request is prepared. Deletion clears both variables after the last request. Run the collection a second time to check that no ID from the first pass was required.

For a terminal regression run, export the saved collection as a JSON file into your scratch folder and install the current Postman CLI following its official installation instructions. Match the CLI to your installed toolchain; no version is assumed here. The official CLI accepts a local collection file path:

postman --version
postman collection run ./Order-Chain.postman_collection.json --env-var baseUrl=http://127.0.0.1:3000

If your export has a different filename, substitute that filename. The explicit --env-var supplies baseUrl even if the collection export omits your local value. The terminal runner must execute on the same machine as the local API. Postman's CLI run documentation describes the local-file command and run report.

Verification command: postman collection run ./Order-Chain.postman_collection.json --env-var baseUrl=http://127.0.0.1:3000. Expect a successful run report with all five HTTP requests and their assertions. If you have only the desktop app, the equivalent verification is the local Collection Runner report; open failed request details before rerunning. Do not treat a green HTTP status as proof that the cross-request ID assertions passed.

How to Chain Requests in Postman Without Stale Variables

The core pattern for how to chain requests in Postman is short: assert the response shape, extract the value, store it at a deliberate scope, and consume it in the next request. The detail that prevents flaky suites is lifecycle control. A collection variable persists locally between manual sends, so it can conceal a broken producer if you keep clicking downstream requests. Clear transient variables before a new run or, as this collection does, after successful cleanup. If cleanup is skipped because an earlier test fails, inspect and clear them manually before retrying.

Choose one scope for each value. This tutorial uses collection variables because the five requests live together and run against one local API. For separate staging and production URLs, use environment variables for baseUrl and select the intended environment explicitly. Postman's scope order allows an active environment value to override a same-named collection value. Read the collection variables and scopes guide before mixing scopes in a team collection.

For a real bearer token, apply your organization's secret handling rules. Do not share a token as a collection default, export it into a repository, or log it in a console transcript. An environment's secure value or Postman Vault can be suitable depending on how the collection runs. The exercise token has no privileges beyond the local in-memory API, but the habit of keeping credentials out of exported artifacts matters.

Data-driven runs need another decision: should each iteration create and delete its own record, or should all iterations share one? The create-to-delete flow here is isolated per iteration because it generates a new ID and removes it before the next iteration. If a failed iteration leaves an order behind, the local server is disposable; in a persistent test environment, add an independent cleanup path and use uniquely identifiable test data. See Postman data-driven testing for iteration data design.

Troubleshooting

Problem: {{baseUrl}} is sent literally or the request cannot connect -> Save the collection variable, confirm its local value is http://127.0.0.1:3000, and keep the Node process running. Hover over the placeholder in the URL field to check resolution. A cloud run cannot call the loopback service on your computer.

Problem: Create order returns 401 -> Run 01 Create session first, confirm sessionToken has a value, and inspect the outgoing Authorization header. It must start with Bearer followed by the token. If an environment defines the same variable name, remove the collision or use one scope consistently.

Problem: Confirm or read returns 404 -> Compare the resolved orderId with the ID in the create response. Restarting server.mjs empties its maps, so run the entire collection again after a restart. An ID from a previous server process cannot identify a current order.

Problem: A script reports a JSON parse error -> Open the response body before calling pm.response.json(). The delete endpoint returns 204 with no body, so its script checks only status. For a non-JSON error response from another API, assert status and content type before parsing.

Problem: Clicking Send does not jump to the next request -> Send executes one request. Use the Collection Runner or Postman CLI to execute the saved order. pm.execution.setNextRequest() only changes order within a collection run, and this straight-line example does not need it.

Problem: A second run passes despite a failed session request -> A previous local variable probably survived. Clear sessionToken and orderId, rerun from the first request, and never continue after a failed assertion. For a larger suite, add a dedicated setup step that unsets transient variables at the start of each iteration.

Interview Questions and Answers

The questions below are useful when explaining this workflow in a QA or SDET interview. Give the concrete example first: a token is produced by POST /sessions, then an order ID by POST /orders; the downstream requests consume those values. The detailed model answers are also available in the interviewQnA section of this article.

Common Mistakes

  • Saving a response value without asserting its presence. A missing field can turn the next URL into /orders/undefined or leave an old value in scope.
  • Reusing the name orderId in an active environment while writing it to the collection. The visible request can resolve the environment value.
  • Running the read request before create. Request order is part of the test setup, not merely presentation.
  • Parsing a 204 response as JSON. No Content means there is no document to decode.
  • Assuming manual Send follows setNextRequest(). Use a runner when you need workflow execution.
  • Exporting a collection with a real token as a shared variable. Keep secrets out of source control and shared examples.

Conclusion

This workflow makes each request accountable for the value it supplies or consumes. A successful collection run proves that a fresh session created a fresh order, the same order was confirmed and read, and cleanup removed it. Keep those identity assertions when you adapt the pattern to your own API.

Where To Go Next

Once this five-request flow is reliable, move the URL into environment-specific configuration and add negative tests for unauthorized access and invalid order bodies. Use Postman pre-request scripts when a request needs a timestamp, signature, or generated input before it is sent. Use Postman Newman in CI or the Postman CLI to make the chain a repeatable pipeline check. If you are comparing the tools, Postman versus Bruno for API automation discusses the workflow trade-offs.

Keep the invariant visible: each producer must prove it returned a usable value, each consumer must prove it used the right value, and the last request must clean up what the chain created. That is the practical answer to chaining requests from response variables.

Interview Questions and Answers

How would you chain a create response to a GET request in Postman?

I assert that create returned the expected success status and a nonempty ID, then store that ID with pm.collectionVariables.set(). The GET URL uses {{orderId}}. I run the collection in create-then-read order and assert that the GET response ID matches the stored ID.

What is the difference between a pre-request and a post-response script in this workflow?

The pre-request script runs before its request and can reject a missing upstream token or prepare input. The post-response script receives the completed response, validates it, and extracts fields for later requests. I use post-response for the new ID because that ID does not exist until the server replies.

How do variable scopes affect a Postman chain?

Postman resolves variables according to scope precedence. A same-named environment variable can override a collection variable when a request uses {{name}}. I inspect the resolved value and avoid duplicate names so the request uses the value written by its producer.

How would you prevent a stale ID from making a broken chain appear healthy?

I fail the producer test if the ID is absent, stop interpreting downstream results after that failure, and clear transient state before a fresh run. Cleanup unsets the ID after a successful delete. A second consecutive collection run is a useful check that the flow creates its own data.

When would you use pm.execution.setNextRequest()?

I use it when the collection needs conditional branching, looping, or a nonstandard request order during a run. A simple login-create-read-delete flow is clearer in saved collection order. The method does not make a manual Send click execute the next request.

How do you prove that a PATCH request updated the resource created earlier?

I assert the PATCH response ID equals the captured create ID and its state equals the requested state. Then I issue a separate GET for the same ID and assert the stored state. That separates response formatting from actual persistence.

How do you handle a DELETE endpoint that returns 204 in Postman?

I assert the status code and do not parse the empty body. I can follow it with a GET expecting 404 when the API contract defines that behavior. I clear the captured ID only after the delete result satisfies the contract.

Frequently Asked Questions

How do I save a value from a Postman response for the next request?

In the producer request's Post-response script, parse the JSON with pm.response.json(), validate the field, and call pm.collectionVariables.set('orderId', response.id). Reference it later as {{orderId}}. Run the producer before the consumer.

Why is my Postman variable unresolved in the next request?

The producer may not have run, the variable name may differ in case or spelling, or the value may have been stored in a different scope. Check the collection Variables tab and hover over the placeholder in the request. Also check whether an active environment overrides the collection variable.

Does pm.execution.setNextRequest() run when I click Send?

No. It changes flow during a collection run, such as the Collection Runner, Postman CLI, or Newman. Clicking Send runs only the current request; use the saved collection order for a simple straight-line chain.

Should I use collection or environment variables for chained requests?

Collection variables fit values shared only by requests in one collection. Environment variables fit configuration that changes between targets, such as baseUrl. Be careful with duplicate names because narrower scopes override broader ones.

Can I chain a token from one response into an Authorization header?

Yes. Save the token after checking that it is present, then use Authorization: Bearer {{sessionToken}} in later requests. For real credentials, use your team's secure storage and sharing rules rather than exporting a populated token.

Why does a chained request work once but fail after restarting my API?

A local or test API may clear its data on restart while Postman retains a local collection variable. The old ID then points to no resource. Clear transient variables and rerun the chain from its first request.

How do I validate a 204 response in a Postman script?

Assert pm.response.to.have.status(204) and do not call pm.response.json() because a 204 response has no body. Clear temporary variables only after the 204 assertion succeeds.

Related Guides