At what point in a Postman or Newman run is a {{token}} in the request URL actually expanded?
answer
- Not the file; the runtime does it
- Just before the send, after the script
- A resolved copy, never the saved item
- The URL is expanded then re-parsed
basics
~20 sImmediately before the send. The runtime resolves the item and its auth in the request step, after the pre-request script has finished, so a value the script has just written is already visible to the expansion.
solid answer
~50 sExpansion is not something the file carries; it happens in the **runtime**, in the step that prepares the outgoing request, and it operates on a **resolved copy** of the item rather than on the collection you saved. The runtime's `resolveVariables` builds that copy through the SDK's `toObjectResolved`, which walks the item's own JSON and replaces tokens in every string it finds, and it resolves the request's auth separately as its own `RequestAuth`. The URL takes a different route: it is flattened to a single string, run through `Property.replaceSubstitutions`, and the resolved string is then re-parsed into a fresh `Url` — because nested tokens do not survive URL parsing well. All of this runs after the pre-request script has completed, which is why a value that script wrote is already in effect for this very request.
go deeper
Know that the token is still plain text in the saved file and only becomes a value when the request is about to be sent, so nothing you export will contain the resolved host.
Explain that the runtime builds a resolved copy of the item just before the send, resolves the auth separately, and handles the URL by expanding the whole string and re-parsing it.
Use the ordering as a diagnostic: a leftover token means the name was unanswered at that instant, so ask whether it was written late or under a different name rather than assuming it was never defined.
Own where configuration is allowed to be decided — late binding at send time is flexible but means a run's real inputs exist only in memory, so decide what your pipeline must record to make a run reproducible.
## Where the expansion happens It helps to be precise about the three layers. The **collection format** is a file; it stores `{{token}}` as ordinary text and knows nothing about expansion. The **SDK** owns the expansion machinery — the pattern, the replacer, the repeated passes. The **runtime** decides *when* to invoke it, and the answer is: in the step that prepares an outgoing request, immediately before the HTTP call is issued. Concretely, the runtime's request step calls `resolveVariables`, which: - builds a **resolved copy** of the item via the SDK's `toObjectResolved`, walking the item's JSON and running every string through substitution; - resolves the request's **auth separately**, producing a new `RequestAuth` the same way; - re-attaches the copy's parent so the rest of the run still sees it in its place in the tree. Two details of `toObjectResolved` are worth knowing. It deletes the property's own `variable` array from the copy before substituting, so a definition is never rewritten by its own expansion; and the runtime passes `ignoreOwnVariables`, so resolution is driven by the value sources it hands in rather than by whatever the item carries. ## The URL takes a different route from the rest of the item The URL is resolved **as one flat string** and then re-parsed. The runtime turns the request's URL object into its string form, runs that whole string through `Property.replaceSubstitutions`, and constructs a fresh `Url` from the result. The source says why in as many words: a URL parser does not handle nested tokens well, so the safe order is to expand first and parse afterwards. That matters when you are reasoning about a broken URL. The thing being expanded at that moment is not "the host field" and "the path field" independently — it is the entire URL as text. A token that spans a structural boundary, or one that expands into something containing a slash or a query separator, is therefore interpreted **after** expansion, by the parse of the resolved string. | Part of the outgoing request | How the runtime resolves it | |---|---| | the URL | flattened to one string, substituted, then re-parsed into a fresh `Url` | | the rest of the item | `toObjectResolved` walks the item's JSON, substituting every string | | the auth | resolved separately into its own `RequestAuth` | | the saved collection | untouched — everything above happens on a copy | ## Why the ordering matters when you are debugging Because expansion happens after the pre-request step and before the send, three things follow that people routinely get wrong: 1. **A value written in a pre-request script affects this request, not merely the next one.** The token is still text when the script runs and is expanded afterwards. 2. **What you saved is not what went out.** The collection file still contains `{{baseUrl}}`; the copy that was sent contained the value. When you compare a failing run against a passing one, compare the *inputs* that fed the expansion, not the file — the file is identical in both. 3. **A leftover token in the sent request is evidence about that moment.** It says the name was unanswered at the instant just before the send, which is a much narrower statement than "the variable is missing" — it may have been written slightly too late, or under a different name. ## A resolved copy, not a rewritten collection The copy-not-mutate design is easy to overlook and explains several observed behaviours. The item in the tree keeps its tokens, so the next iteration of a data-driven run starts from the same unexpanded text and re-expands it with that iteration's values. Nothing accumulates: expansion is performed fresh for each send, never once and cached into the collection. It also explains why exporting or re-saving a collection after a run never captures resolved values. People occasionally expect the file to "learn" the host it just used; it cannot, because the object that carried the resolved host was a throwaway built one step before the send and discarded after. ## What to take away - Expansion is a **runtime action at send time**, not a property of the stored file. - It runs **after** the pre-request step, so writes from that step are already in effect. - The **URL is expanded as a whole string and then re-parsed**; the rest of the item and its auth are resolved as objects. - Everything happens on a **copy**, so the saved collection is unchanged and each send re-expands from the original text.
- Why is the URL expanded as a whole string rather than field by field?Because a URL parser does not deal well with tokens that nest or that straddle structural boundaries. Expanding the flat string first and parsing the result afterwards means the parse sees a real URL, not a template. The runtime does exactly that: it substitutes into the string form and then builds a fresh `Url` from what came back.
- After a run, why does the saved collection still contain the tokens rather than the values it used?Because the runtime resolves onto a copy of the item and sends that. The stored item is never rewritten, which is also what allows each iteration of a data-driven run to start from the same unexpanded text and expand it again with that iteration's values.
saying these in an interview costs you the question
- Says the collection file itself is rewritten with resolved values
- Claims expansion happens before the pre-request step runs
- Thinks each URL field is substituted separately after parsing
- Assumes expansion happens once and is cached across iterations
- Believes the runner expands tokens only in the URL, not elsewhere