Net Tinker Help

Leer en español

I want to… — find the right tool

Every name in bold below is written exactly as it appears in the extension, so you can also find it with Ctrl+F here or search for it in the interface.

…change what a request or a response does

All of these are a rule. Pick the rule type from the Rule type dropdown when you create it.

I want to… Use this
Make an endpoint return a 500, a 404, a 401 — an error I can't easily trigger for real Rule type Mock the response (API), then set Status code. See Your first rule.
Return test data for an endpoint that doesn't exist yet Rule type Mock the response (API), write the JSON in Response body. See Your first rule.
Start a mock from what the server really returned, instead of typing a fixture The Mock button in the DevTools panel — see Building rules from real traffic.
Change one field of a real API response and leave the rest alone Rule type Modify the real response body, Mode = Overwrite a JSON field, e.g. Field path user.status.
Swap a piece of text inside a real response Rule type Modify the real response body, Mode = Find and replace.
Add, override or remove a header Rule type Modify headers — see the syntax and the Presets below.
Send an Authorization header the app doesn't send Rule type Modify headers, preset Auth headers. It writes Authorization: Bearer {{token}}, so define a token variable to go with it.
Stop the browser serving from cache so my rule gets a chance to run One-off: ↻ Reload without cache in the popup. For every reload: rule type Modify headers, preset Cache bypass.
Get past a CORS complaint while developing Rule type Modify headers, preset Development CORS — but read Things that look like bugs but aren't first: it does not lift a real browser block.
Point a call at localhost or at another environment Rule type Redirect the request, fill in Redirect to.
Replace staging with local in every URL, keeping the rest Rule type Replace text in the URL (Find / Replace with).
Send every /api/v1/… call to /api/v2/…, keeping the rest of the path Set Compare to regex, capture what you want to keep with parentheses, and write it back with $1, $2… Pattern /api/v1/(.*) and destination /api/v2/$1 is one rule instead of one per endpoint. The pattern tester under the URL field shows you what each group captured, so you can check before saving. Works in Redirect to and in Replace with; $$ writes a literal dollar sign, and a $N with no group behind it becomes nothing rather than staying in the URL.
Force a query parameter the interface doesn't expose (a debug flag, a page size) Rule type Query parameter.
Make a call fail, to see what my error handling does Rule type Block the request.
Test slow network, spinners and loading states Set Latency (ms) in the rule's Network simulation block. It works on any rule type — including a rule that does nothing else.
Test against a flaky network, where every call takes a different amount of time Min (ms) and Max (ms) in the same block. See Random latency.
See what my UI does when a call never answers Simulated timeout (ms). Not the same as a long latency: the request ends up failing. See Simulated timeout.
Make an endpoint fail sometimes, not always — to test retries Chaos mode: set a % errors or a % timeouts and the rest of the requests keep working. See Chaos mode.
See what my UI does if this endpoint returns a 500, but with the real payload and without writing a mock Set Override the real status in the rule's Network simulation block. The request still goes out, the real body still comes back, and only the status code changes. See Overriding the status of a real response.
Send the backend a payload the interface would never send Rule type Modify the request body.
Mock an endpoint and make it slow — or redirect a call and add a header to it Two rules matching the same URL, one for each half: they all apply. See When several rules match the same request.
Affect a single GraphQL operation, not the whole endpoint Any rule type, plus a Body condition such as "operationName":"GetUser". The DevTools panel fills this in for you.
Limit a rule to one tab so it doesn't affect everything I have open The 📌 button next to the rule in the popup — see extra conditions.
I have a cURL — from DevTools, a ticket, the docs — and I want a rule for that call In the rule form, next to Pattern: Paste a cURL. See Pasting a cURL.
Point my whole app at my local backend instead of production Point at another backend, next to the pattern field: two fields and done. See the shortcut.

…find my way around the extension

The options page has two rows of controls, and which row something is on tells you what it affects. The header carries what governs everything: the project you are working in, then Export · Import ▾ · + New rule, and in the corner the master On/Off switch, the 🌐 interface language and the help.

The strip of tabs under it opens what lives inside the active project: 📁 Groups · 🎭 Scenarios · {{ }} Variables · 🌐 Environments · 🎯 Scope · 📡 Captures · 🗑 Bin. One panel is open at a time, and the one you leave open is the one you find open next time. They are in that order on purpose: the first five configure how your rules behave, and the last two configure nothing — 📡 Captures is the record of what your rules did, and 🗑 Bin is what you deleted.

I want to… Use this
Work out why my rule isn't doing anything Why a rule might not be firing — the full checklist, in order.
Check my pattern matches a URL before saving the rule Test URL (optional), right under the pattern in the rule form. See matching.
Know which of my rules wins when two of them match the same URL When several rules match the same request — short answer: they all apply, and where they clash the one lower in the list wins.
See which requests a rule actually caught Record this tab in the popup, then 📡 Captures in the options page. See Seeing what actually happened.
Put back something I have just deleted 🗑 Bin — see The bin: undoing a delete.
Read the extension in another language The 🌐 button in the header, next to the On/Off switch. English and Spanish; Automatic follows the browser. Your choice covers the popup, the options page, the DevTools panel and this help, it is not part of any project, and it does not travel in an export — it is a setting of yours, not of your rules.
Switch a host between Local, Staging and Prod without editing every rule Variables plus Environments.
Turn several rules on or off together Put them in a Group (the Groups panel at the top of the options page) and toggle the group. Or tick several rules in the list — by hand, or with Select all, which acts on whatever the filter is showing — and use the bar that appears. See Acting on what the filter is showing.
Have the five error states of one endpoint ready without typing five mocks 🎭 Scenarios in the options page → + From template. Pick Typical HTTP errors or REST collection, say which URL, and every variant arrives with its rule already written — status, headers and body. None of them is switched on until you pick it.
Find one rule among many The four controls above the list: the search box, and the filters by type, by group and by scenario. See Finding one rule among two hundred.
Mock a whole API I have the spec for, without writing each rule Import in the options page, and pick the openapi.json — see Starting from an OpenAPI / Swagger spec.
Put the response examples I already saved in Postman to work The same Import, with the collection file — see Bringing in a Postman collection. A collection with no saved examples produces no rules at all, and that section says how to save them.
Get my ModHeader profiles back, now that the extension is gone from the store The same Import, with the file you exported from there — see Bringing in a ModHeader profile.
Find out which files Import accepts, without trying them one by one The ▾ next to it lists them. The menu informs, it does not route: every entry opens the same file picker and the file is recognised by its content, so picking the wrong entry breaks nothing. Pressing Import itself still goes straight to the picker, with no menu in between.
Share my setup with a teammate, or move it to another machine Export / Import in the options page. Read what "sanitize on export" covers before sending the file.
Hand someone a trace of what the page did Export HAR in the DevTools panel — see Building rules from real traffic.
Copy a request into a terminal or the console Copy as… in the DevTools panel (cURL or fetch()).
Reuse my rules in my Playwright or Cypress tests Copy as… on the rule's own row (just that one), or Export → the mock buttons in the dialog (all of them, or the selected ones). Read the comments in the snippet before you commit it: they say what was redacted and what could not be translated.
Call the endpoint a rule intercepts, to compare against the real thing The same Copy as… on the row, options cURL and fetch().
Make one rule win over another that also matches Move it with ↑ and ↓: the one lower in the list wins. See When several rules match the same request.
Keep the extension off everything but my own sites Scope in the options page: everywhere, only localhost, or only the domains you list.
Turn a whole navigation into mocks in one go In the Net Tinker DevTools panel, tick the requests you want and press Save as scenario: one mock rule per request, with the status, headers and body that actually came back. Before creating it, the dialog shows you the list of rules that will be born: repeats of the same request are merged into one —keeping the last response— so a poll that fired twenty times does not leave you twenty identical rules. The scenario is created switched off and with a single variant, "As recorded" — activate it when you want it to apply, and duplicate that variant to write the "and now it fails" version on top.
Keep a recorded mock working when the parameters change When you save the recording, tick Ignore the URL parameters. Without it, a rule recorded on /api/orders?page=2 is exactly that URL and answers only that one: the moment your app sends a different parameter — another page, another tracking id — the rule stops applying and nothing explains why. With the box ticked you get one rule per endpoint, and the dialog's summary shows you how many are left before you accept. Your call: if what you are testing is precisely the difference between ?page=1 and ?page=2, leave it unticked.
Know what permissions it asks for, and what for In Scope, at the bottom: What it asks Chrome for, and what for. There are two, counted from the manifest itself — not from a hand-written list that can go stale. And there is the half you cannot see from outside: where the scope does not reach, the extension steps aside and leaves fetch and XMLHttpRequest exactly as they were.
Read a JWT without pasting it into someone else’s website If a variable’s value is a token, its row shows a JWT badge — in red and saying expired when it is, which is usually the answer you are after. Clicking it shows the claims, the algorithm and the expiry date in plain words. It is decoded here, nothing leaves your computer; the signature is not checked, because without the issuer’s key there is no way to.
Stop the extension touching one site that behaves oddly Open the popup on that site and press Exclude this site. It goes to the Never act on these domains list in Scope, which wins whatever the mode is — you keep everything else exactly as it was. Reload the page afterwards: what was already patched stays patched until then.
Turn everything off for a moment without deleting anything The master toggle: On in the popup, Extension enabled in the options page.

Your first rule: which fields actually matter

Press + New rule (in the popup or in the options page). The form opens on Mock the response (API), which is the most common case: answer a request from the browser instead of letting it reach the server. What the form shows up front is short — the rest is folded into four collapsible blocks, described below — and most of it is optional. Here is what a working JSON mock actually needs:

  1. URL / pattern — the only field the extension refuses to save without. By default it is read as Contains, so /api/users is enough to catch every URL with that fragment. See Choosing which requests a rule matches.
  2. Status code — starts at 200. The field is a plain number box with a list of suggestions attached (200, 201, 204, 301, 400, 401, 403, 404, 429, 500, 502, 503…): click it to pick one, or just type any code you want.
  3. Headers — a new mock rule opens with Content-Type: application/json already written in the box, as a real value you can edit or delete: what is in the box is what gets saved. Delete it and the mock comes back as plain text — a legitimate choice, and nothing writes the header back when you save. Opening a rule that already exists never adds it, even when its headers are empty.
  4. Response body — what the caller gets. The { } Format and { } Minify buttons above the box tidy up JSON, and a note underneath tells you whether what you typed parses as JSON at all.

Everything else narrows the rule down or helps you find it later, and lives in four collapsible blocks: Organization (description, tags, group), Advanced matching (source, match type, method), Additional conditions (optional) (header, body and initiator conditions) and Network simulation (latency and status code, which apply to every rule type). A new rule opens with the four of them closed; editing an existing one opens each block that rule actually uses, so a folded block never hides something you set. Two things worth doing before you close the form:

Shortcut: point your whole app at another backend

The most common case of all — I want this to talk to my local server instead of production — does not need you to type a pattern. In the rule form, next to Pattern, there is Point at another backend: write the source host and the target host, press it, and the rule is done — pattern, type Replace text in the URL and both replacement fields.

What it writes is an ordinary rule, so you can edit it afterwards. And it affects every call to that host, which is exactly what you want here: if you only wanted one, use the DevTools panel.

Rule types

Every rule has exactly one type, which decides what happens once it matches a request. The name in bold is the one in the Rule type dropdown, where the nine of them are grouped by what they do to the request — Mock, Modified passthrough, Redirect and Block, the split described in Mock vs. modify & passthrough. The shorter name in brackets is how the same type is labelled in the rule list, the type filter and the captures list. One rule does one of these things — but a request can be governed by several rules at once, so combining two types is a matter of writing two rules. See When several rules match the same request.

Type What it does When to use it
Mock the response (API)
[Response]
The request never reaches the network. You fabricate the status, headers and body yourself. The backend endpoint doesn't exist yet, or you need a specific error/edge case (500, empty list, malformed data) that's hard to trigger for real.
Modify the request body
[Request body]
The request still goes out for real, but with a different body than the page sent. Two modes: replace the whole body, or overwrite one JSON field by dot path (variables.userId) leaving the rest of the body exactly as the page built it. The new value is read as JSON when it is valid JSON (42, true, null, {"a":1}) and as plain text otherwise. If the body isn't JSON — or isn't text at all, like a FormData — the request goes out untouched and the Activity panel says the rule matched but couldn't be applied. Testing how the backend reacts to a payload the UI wouldn't normally send. The one-field mode is what makes this usable with GraphQL: you can change variables.userId without pasting the whole query into the rule — and without the rule going stale the next time the front end adds a field to that query.
Modify headers
[Headers]
Add, override or remove request and/or response headers. The request still goes out for real. One per line: Key: Value overwrites or adds, +Key: Value appends to what is already there, -Key removes. The Presets dropdown fills the boxes in for you with three common sets — Cache bypass, Development CORS and Auth headers — adding to whatever you already wrote and only replacing lines for the same header. Forcing a feature flag header, testing a missing/extra CORS header, faking an auth header locally.
Redirect the request
[Redirect]
Sends the request to a different URL entirely. Pointing a call at a local server or a different environment without touching the app's config.
Block the request
[Cancel]
The request fails outright — fetch rejects, XHR fires an error event. Testing what your UI does when a call fails or a script fails to load.
Query parameter
[Query param]
Add or remove query string parameters. The request still goes out for real. One per line: +key=value adds or overwrites, -key removes. Forcing a query param the UI doesn't expose (a debug flag, a page size).
Replace text in the URL
[Replace]
Replaces every occurrence of a piece of text in the URL, keeping the rest intact. The request still goes out for real. The text you type in Find follows the rule's own match type: with the other three it is matched literally, character for character, so a . only ever matches a real dot. Choose Regex and it becomes a real regular expression, and Replace with can use $1…$N for its groups — a group that matched nothing becomes empty, never a literal $1. Swapping staging for production in a URL without a full redirect rule.
No change (delay only)
[No change]
Does nothing to the request by itself: it goes out exactly as the page built it. It exists so that a rule can carry only a modifier — today, a latency — without also having to change something. Slowing an endpoint down without altering it in any other way.
Modify the real response body
[Response body]
The request goes out completely unchanged; only the body that comes back is transformed before your code sees it. Two modes: Find and replace (literal text, every occurrence) or Overwrite a JSON field (a dot path such as user.status, leaving the rest of the JSON alone). It has limits of its own — see Things that look like bugs but aren't. Flipping one field in a real API response (e.g. a status) without hand-writing a full mock.

Latency is not a rule type

Delaying a request used to be a type of its own, and it no longer is. Latency (ms) lives in the Network simulation block of the rule form and applies to every type: a mock arrives late, a redirect leaves late, a blocked request fails late. Leave the field empty and the rule adds no latency at all; 0 is not the same thing as empty — it is a real, if tiny, wait.

If all you want is to slow an endpoint down, use the No change (delay only) type and give it a latency. Rules that used to be of the old Delay type were converted to exactly that, keeping their milliseconds, the first time you ran this version.

Network simulation: random latency, timeout and chaos

The Network simulation block of the form holds four ways to make a request misbehave, and all four work with any rule type — a mock, a redirect or a No change (delay only) rule take them just the same. Every field is optional, and empty means "not this one". The fixed Latency (ms) is explained just above; here are the other three.

Random latency

Min (ms) and Max (ms), the row underneath the fixed latency. Every request waits a different amount between those two values, both ends included: with 100 and 200 you can get 100, you can get 200 and you can get anything in between, and it is drawn again on every request.

What it's for: a spinner that only flickers, a race between two calls that the same one always wins, a "loading…" that never gets seen. With a fixed latency those bugs either always reproduce or never do; with a range they show up some times and not others, which is how they show up for real.

Simulated timeout

Simulated timeout (ms) waits that long and then makes the request fail, the way a real network timeout does. It sits next to latency in the form and it is not the same thing:

This… …does this to the request
Latency (ms) Succeeds, just late. Your code gets a response.
Simulated timeout (ms) Waits and then fails. Your code gets a network error, not a response.
Type Block the request Fails immediately, without waiting for anything.

Chaos mode

Three fields — % errors, Error status and % timeouts — that roll the dice on the outcome of every request. The success rate is not asked for: it is whatever is left over. With 20 and 10, two requests out of ten get the error, one out of ten gets no response, and the other seven do exactly what the rule says.

With chaos on, two identical reloads don't give the same result. That is the point when you are testing retries and error states, and it is also what makes a forgotten chaos setting look a lot like an intermittent bug in your application. If something misbehaves now and then, check this block before hunting for the bug anywhere else.

Call limit and intermittent failure

The only two fields in the whole extension that count requests. Everything else looks at the request in front of it; these two also look at how many that rule has handled already.

This… …does this to the request
Call limit The first After N calls go through as usual; from the next one on, the rule answers with the Status you set (429 if you leave it empty). It is what a server does when it cuts you off for calling too much.
Intermittent failure Fails the Nth request and lets the rest through: with 1 in every 3, the 3rd, 6th and 9th fail, with the Status you set (500 if you leave it empty). The first request never fails, so you get to see the rule work before you see it break.

Where the count comes from, which is what you need to know before using either:

If you set two at once — or either one together with chaos — the most predictable one wins: first the Call limit, then the Intermittent failure, and Chaos mode last. Once the limit is crossed there is no dice roll left: what you can predict by reading your rule beats what nobody can predict.

To test retries: 1 in every 2 with status 503. The first call works, the second fails, the third works again — that shows whether your code retries and whether the second attempt gets through. With the Call limit at 3 and 429 you see the opposite: what your UI does when something stops working and does not come back.

The page console

Log to the page console when this rule matches is the last checkbox in the block, and the only one that does nothing to the request: it goes out exactly as it would have. What it adds is one [Net Tinker] line per matched request in the page's own console — the rule's name, the method, the URL and what happened to it — right next to your application's own logs. That is the whole point: the same information already lives in 📡 Captures, but in another window, and while you are chasing a bug you are reading the console, not switching tabs.

The page sees it too, and that is worth knowing before you tick it. Net Tinker writes this line from inside the page, so anything the page does with its console — an error reporter, a session recorder, a screen you are sharing — gets it as well. For that reason the line never contains headers, body or variable values: your auth preset is literally Authorization: Bearer {{token}}, and a token has no business in a console. If you need to see what a rule actually applied, that is what the capture detail is for.

Overriding the status of a real response

There are two fields in the form that hold an HTTP status code, and they are not the same thing. It is the easiest confusion to fall into here, so it goes first:

Field What it is Where it lives
Status code The status of a response the extension builds. The request never goes out, so there is no real response anywhere. Only on the Mock the response (API) type, next to the mock's body and headers.
Override the real status Replaces the status of the response that did come back from the server. The real body, the real headers and everything else arrive untouched. In the Network simulation block, on any rule type.

The case it exists for: you want to see what your error handling does with a 500 from that endpoint, but with the real data. With a mock you would have to hand-write the whole body — and then you would no longer be testing against the real response, which is exactly what you wanted to keep.

And like any modifier, it combines: a single rule can override the status and transform the response body, and change a header on it, and arrive late.

Mock vs. modify & passthrough

This is the single most common point of confusion, so it's worth spelling out on its own:

Mock — the request never leaves the browser. Net Tinker builds a fake Response (or fakes the XHR properties) and hands it straight back. The real server never even sees the request.
Modify & passthrough (request body, headers, redirect, query params, replace-in-URL, no change) — the request really goes out over the network, just not quite the way (or not quite when — any rule can carry a latency) the page originally built it. The real server does see it, and its real response comes back (possibly with response headers further modified on the way back, for the "headers" rule type).
Modify the real response body — the request goes out completely unchanged, and only the body coming back gets transformed (find/replace, or overwriting one JSON field) before your code ever reads it. Non-JSON bodies can't be inspected for the "overwrite a field" mode, so they're left untouched rather than silently doing nothing you'd notice.

A practical way to tell them apart while debugging: open DevTools' Network tab. A mocked request never shows up there at all (it never reached the network); a modified-and-passed-through one does, with the rewritten URL/body/headers. The captures list says the same thing in words, per request: "reached the network" or "never reached the network".

This is a property of the request, not of each rule taken alone: as soon as one matching rule mocks it, the request never leaves the browser, whatever the other matching rules do. A mock with a latency is a delayed answer that still never reaches the server — see When several rules match the same request.

Choosing which requests a rule matches

Whatever its type, a rule always starts from one pattern: the Source dropdown decides whether that pattern is compared against the full URL or only the Host, and the Match type dropdown decides how. Both live inside the form's Advanced matching block, which is folded away until you open it:

Match type How the pattern is read
Contains (the default) Matches if the pattern appears anywhere in the URL/host. /api/users catches every URL with that fragment in it.
Equals Matches only if the URL/host is exactly the pattern, character for character.
Regex The pattern is a JavaScript regular expression, tested against the URL/host. It doesn't have to match the whole thing unless you anchor it with ^/$.
Wildcard (* ?) A middle ground between "Contains" and "Regex": * stands for any run of characters (including none) and ? for exactly one character. Everything else is literal, so you don't have to escape anything — a . is a real dot, not "any character". Like Regex it isn't anchored either: the pattern only has to occur somewhere in the URL/host.
https://*.example.com/api/v?/users in Wildcard mode matches https://api.example.com/api/v2/users and https://staging.example.com/api/v3/users, but not https://example.com/api/v10/users — ? is exactly one character.

Rather than guessing, paste a real URL into Test URL (optional), the box right under the pattern. It answers ✓ Matches or ✗ Doesn't match as you type, tells you what it compared against (the whole URL or just the host), and warns you if a Regex pattern doesn't compile. Nothing is saved and no request is made. It decides with the pattern, the match type, the source and the exclusions; the method and the header/body/initiator conditions it names instead of judging — a URL carries no method and no body, so it lists them as things it could not check rather than pretending it did.

Everything under /api/ except the health check

A pattern that is broad enough to be useful is usually broad enough to catch something you didn't want: the health check your dashboard polls every five seconds, the login call, the one request that already carries a real token. That's what Except URLs containing (one per line) is for — it sits just under the three dropdowns in Advanced matching. Write one pattern per line; if any of them appears in the request's URL, the rule leaves that request alone.

Pattern /api/ with /api/health and /api/auth/ as exclusions: every API call is mocked except the health check and anything under auth.

Two things about this box are deliberate, and worth knowing before you fight it:

The Test URL box knows about exclusions: an excluded URL answers ✗ Excluded by “…” and names the line responsible, instead of claiming a match that would never happen.

On top of the pattern, a rule can carry extra conditions, under Additional conditions (optional) in the form — except Method, which sits with the other two dropdowns under Advanced matching. They are all optional, and all of them have to hold at once (AND, never OR):

Finding one rule among two hundred

There are four controls above the list, and they combine: what's left is what satisfies all of them at once.

Each rule shows its group at the start of its row, in a tag before the name. If the group is disabled the tag carries a ⚠ and the row is dimmed. Rules with no group carry no tag at all: to find them, use the filter.

What to keep in mind when filtering. The order of the list is the priority order (the lowest matching rule wins), and filtering does not change it: what remains keeps its real order. But a rule the filter hides still competes, so with a filter on, "the last one I can see" isn't necessarily "the one that wins". That's why, as soon as a filter hides rules, a line appears above the list saying how many of how many are showing and reminding you of this — with a button to clear all four controls at once. If you're deciding priorities with the ↑ and ↓ arrows, clear the filters first.

Acting on what the filter is showing

Next to the search box and the three filters there are two more buttons, Select all and Select none, and they act on whatever the filter is showing: with a filter on, only on those rules; with no filter, on all of them. The ☑ in each group, over in the Groups panel, respects the filter the same way. A line under the two buttons says so in plain text rather than leaving it to a tooltip — which does not exist on touch, and does not show up when you tab to the button.

A selection deliberately survives a filter change: searching, selecting, searching for something else, selecting more and then acting on the union is the whole point of those buttons. So the counter in the selection bar carries a denominator and also says what is out of sight — "30 of 200 selected · 30 outside the filter" — and deleting asks with the subtraction done: "Delete 30 of your 200 rules? 170 will remain." A single number can be misread two opposite ways ("I thought that was all of them", "I thought that was just what's on screen") and the subtraction closes both. The ✕ in the bar clears the whole selection, which is what makes it different from Select none.

When several rules match the same request

Nothing stops two rules from matching the same request, and when that happens all of them apply — not just the first one. That is what lets you redirect a call and add a header to it, without trying to cram both into a single rule.

They are applied in list order, from top to bottom, and each rule only has a say in the part of the request its own type is about: a headers rule contributes header changes, a mock contributes the answer, and any of them can contribute a latency on top. When two rules speak about the same thing, the one lower in the list wins. So the order of the rule list is the priority — use the ↑ and ↓ buttons on a rule to move it above or below another one, and what your request ends up doing really does change.

Two matching rules both… What the request ends up doing
…mock the response The lower one answers: its status, its headers, its body. The upper one is replaced outright, not blended into it.
…rewrite the URL (redirect, query parameter, replace text) Both, chained: each one rewrites the URL the previous one produced. "Redirect to localhost" plus "add ?debug=1" gives you a localhost URL with the parameter on it, not one or the other.
…modify headers Both sets of lines, run one after the other in list order — so for the same header name the later line wins, exactly as two lines inside a single rule would.
…modify the real response body Both, in order: the lower rule transforms whatever the upper one produced.
…set a latency, or modify the request body The lower one's value. A latency of 0 ms and an empty body are real answers, not "no opinion".
One rule that mocks /api/users with a 500 and carries a 2000 ms latency gives you a 500 that takes two seconds to arrive. Two separate rules — one mocking, one only adding the latency — give you exactly the same thing, which is handy when you want to switch the slowness off on its own.

Two combinations are settled by a rule of their own rather than by list order:

To see which rules governed a given request rather than reasoning it out, record the tab and open the captures list: a request that matched three rules gets a ⚡ line for each of them, and the toolbar badge counts rules applied, not requests.

Projects: separate jobs that never mix

A project is everything you have set up for one job: its rules, its groups, its scenarios, its variables and its environments. What matters is not that they sit together, but that only the active project applies: rules in the other ones never touch a request, even when they are switched on.

There is one from the moment you install the extension, and while it is the only one you need do nothing: its name sits at the top, next to the page title, and everything you have is in it. Click it to switch projects, create another, rename it or delete it. The popup grows a Project dropdown as soon as there is more than one.

You are working on a client's API and someone asks you to look at a bug in another one. Create a new project, set up the four rules you need there, and when you come back to the first one it is exactly as you left it — nothing switched off, nothing deleted, and neither job's rules leaking into the other.

Everything you create lands in the active project, imports included: a pack, an OpenAPI spec, a Postman collection or a HAR all come in where you are working.

And what you export is the project you are in, only that: its rules, groups, scenarios, variables and environments. Nothing from your other projects travels in the file.

The pack says which project it came from (its name and its scope), and when you import it you choose where it goes: into the project you are in, or into one with that name — created if you don't have it, and if you do, what you import is added to whatever is already there. That is how you move a client from one machine to another in one piece. The destination always starts at "this project": creating one is your decision, never a side effect. If the pack came from the main project — the one with no name of its own — you are asked nothing, because there is no name to offer you.

What else belongs to each project, which tends to surprise the first time:

Deleting a project takes everything with it, and it does not go to the trash. That project's rules, groups, scenarios, variables and environments all go at once; the confirmation tells you how many before it happens. The main project cannot be deleted —it is where everything lands— but it can be emptied and renamed.

One hint for when something is not applying: if a rule you are sure you created is nowhere to be found, look at the name up top. It is almost always in the other project. That is the first place to check, even before the ones in Why a rule may not be applying.

Using variables

Define a name/value pair once (the "{{ }} Variables" button in the options page) and reuse it as {{name}} anywhere you'd otherwise retype the same string: a rule's body, a header's value, a redirect URL, a query param's value, the replacement text of a "Replace text in the URL" rule, and both the replacement text and the new JSON field value of a "Modify the real response body" rule.

Define apiHost = api.staging.example.com, then use https://{{apiHost}}/v2/users as a redirect target. Change the environment once, in one place, instead of hunting down every rule that hardcoded the old host.

A name that doesn't match any defined variable is left exactly as written — {{typo}} shows up literally in the request or response, rather than silently disappearing, so a mistake is obvious instead of a mystery. Variables are not substituted into a rule's matching fields (the URL pattern, or a header/body/initiator condition) — those compare against the real, live request, so a variable there wouldn't mean anything.

Reading and copying what a JWT carries

When a variable's value looks like a JWT, a JWT badge appears next to it. Pressing it opens the viewer: the header and the claims, with the expiry as a wall-clock date rather than the number of seconds the token carries.

The viewer has two buttons, and they do deliberately different things: Copy token copies the token as it is, ready to paste into an Authorization header; Copy claims copies the JSON of the header and payload, which is what you paste into a ticket or a chat.

The viewer does not check the signature, and it cannot: that needs the server's key. It tells you what the token says, not whether the server will accept it.

Environments

An environment (the "🌐 Environments" button, next to Variables) is a named set of overrides on top of your variables — Local, Staging, Prod, whatever you need. It only has to override what actually changes between environments; anything it doesn't mention keeps the variable's own base value.

With apiHost = local.test as the base value, a "Prod" environment can override just apiHost = api.example.com and leave every other variable alone.

Pick the active environment from the star button next to it in the options page, or from the Environment dropdown in the popup (only shown once at least one environment exists). With no active environment, every variable simply uses its own base value — exactly like before environments existed.

Scenarios: several versions of the same API

A scenario groups several named variants of the same API, of which exactly one is active: Login → Success / Invalid credentials / Backend down. A rule can belong to a variant, and the rules of a variant that isn't active are not applied — the engine never even looks at them.

Exclusion is per API, not global. Two scenarios can be in different states at the same time: Login on "Backend down" while Payments stays on "Success". That is the difference from keeping loose rules and switching them on and off by hand, which is what this replaces.

To show how your app behaves when login fails you don't touch a single rule: switch the "Login" scenario's active variant to "Invalid credentials" and reload.

You create and organise them on the options page, under 🎭 Scenarios; you switch the active variant there or — handier while you are testing — from the popup, which shows one row per scenario. A rule is assigned to a variant from its own form.

If you'd rather not start from an empty list, + From template leaves the scenario built, with its variants and their rules already written: the typical HTTP errors, or the skeleton of a REST collection. And scenarios travel in the export file along with the rules, so you share them with your team like everything else; on import, one that collides with a scenario you already have asks whether to overwrite, duplicate or skip, just like rules and groups.

Picking a variant that no longer exists — because someone deleted it — stores "none" rather than a dangling pointer: the rule stops belonging to anyone and behaves like a loose rule.

Deleting a scenario, or a single variant, asks what to do with its rules instead of deciding for you. The dialog says how many live inside and offers two ways out: Keep the rules, disabled — they come out of their variant and are left switched off, so none of them starts firing on its own now that nothing governs it — or delete them too, which cannot be undone. With no rules inside, the question is the short one. It is the same treatment deleting a Group with rules in it already got.

Writing a variable from a response

The other way round from everything above: instead of putting a value into a request, a rule can take a value out of the response and store it in one of your variables. From then on that variable interpolates everywhere the previous section lists, so the typical case is one rule: extract token from your /login response into the JWT variable, and every rule sending Authorization: Bearer {{JWT}} starts sending the freshly issued token — no more copying it by hand.

It lives in the "Write a variable from the response" collapsible block of the rule form, and it does nothing until you pick a variable: none of your existing rules capture anything. There are two ways to locate the value:

A rule on /api/login that changes nothing about the response ("No change" is fine), with Write into = {{JWT}} and Field path = data.token. Then another rule on /api/, type "Modify headers", adding Authorization: Bearer {{JWT}}. Log in once and the rest of the session is authenticated.

Where the value ends up, which is worth knowing before you rely on it: in the browser session's memory. It is not written to disk, it never appears in an exported file — and there is no checkbox to include it — and it is gone when you close the browser. Next time you open it the variable is back to whatever your variables list says (usually nothing) until a response refills it: logging in again is enough. The "Clear captured values" button in the Variables panel discards them right away, without waiting for anything to close.

A captured value wins over the one written in your list and over an environment's override, but only inside the environment it was captured in: a token taken in "Staging" is not used once you switch to "Production", where the variable goes back to its usual value.

One limitation to keep in mind: the value is not available to requests fired at that same instant. It travels through the extension's background first — milliseconds, which the normal case (log in, then everything else) doesn't notice; what does notice is firing two requests at once and expecting the second one to already carry the first one's token.

A failed extraction does no harm: the response reaches the page whole and untouched, and the Activity panel marks that request saying which of the two things happened — that there was nothing to extract there (the path or the pattern found nothing in that response), or that it could not be extracted (the body isn't JSON, the regular expression is invalid, the field exists but is an object). Binary and streaming bodies are never read, and that is reported too.

Why a rule might not be firing

First, a quick check that saves a lot of time: the extension's toolbar icon carries a badge with the number of rules applied on the current tab. If it's empty after reloading, nothing matched at all and the list below is where to look. If it's counting, the rule is firing and the surprise is in what it does, not in whether it runs.

And a shortcut for half of this list. Open the rule and paste the request into Test URL: besides telling you whether the pattern matches, the box lists what would stop that rule from applying —the master switch, its own, its group, its scenario variant, a rule type the engine no longer applies— and, separately, what it can't decide from a URL: the method, the header, body and initiator conditions, the scope, and the tab it may be pinned to. Whenever there is something to explain, the box ends by saying what it can't see, which is precisely the tail of this list.

Check these in order — they cover every reason a rule can silently fail to apply:

  1. The master toggle (On at the top of the popup, Extension enabled in the options page) is off. Nothing applies at all while it is.
  2. The rule's own enable/disable toggle is off.
  3. The rule belongs to a group that's disabled. A disabled group silently disables every rule inside it — in the rule list, those rules show a ⚠ next to the group's name.
  4. The rule is pinned to a specific tab (📌) and you're testing from a different one.
  5. This site is outside the configured scope. This is the most misleading one, because it has nothing to do with the rule: if Scope (options page) is set to "Only localhost" or "Only these domains", then on every other site no rule applies at all. The toolbar icon says so — hover it and you'll read "out of scope here" — and in that mode the popup warns too. Watch out for the empty-list case: "Only these domains" with no domain listed allows nothing, anywhere.
  6. The request's HTTP method doesn't match the rule's configured Method (if it's not set to "Any").
  7. The URL/host pattern doesn't actually match — check the match type as well as the pattern itself, and paste the real URL into Test URL (optional) to settle it. If the match type is Regex, a typo that makes the pattern invalid means the rule matches nothing rather than throwing an error — the test box flags that case explicitly. Another common one: writing * or ? in a pattern still set to "Contains" or "Equals", where they are just literal characters — those only mean anything in Wildcard mode.
  8. The rule has an exclusion pattern that matches this URL. It's the easiest one to overlook, because the positive pattern does match and everything looks right: check Except URLs containing inside Advanced matching — the block opens by itself when the rule has any, and the folded summary says how many. Pasting the URL into Test URL (optional) settles it instantly: it names the line that excludes it.
  9. There's a Header condition, Body condition or Initiator domain attached to the rule, and the current request doesn't satisfy it. For header and body conditions on XHR requests specifically: a redirect/query-param/ replace-in-URL rule can never fire based on either, because the URL is decided before the page has set its headers or called send(body) — this only works for the "mock", "block", "no change", "modify request body" and "modify headers" rule types on XHR (it works for every rule type on fetch). A body condition also only ever matches plain text (JSON, form-encoded, plain strings) — FormData/Blob/binary bodies are never read for it.
  10. The request isn't fetch or XMLHttpRequest. Net Tinker can only intercept those two — it cannot affect <img>, <script src>, CSS, fonts, or any other resource type when it comes to applying a rule (the DevTools capture panel can still see all of those, it just can't act on them). Careful with the reverse reading: what counts is how the request was made, not what came back. An SVG or a PNG fetched with fetch() is fetch, and can be mocked.
  11. The request didn't come from the page, but from its service worker. Plenty of sites register a service worker (you'll see it under DevTools → Application → Service Workers) that downloads part of the content on its own. Those requests look ordinary — the browser counts them as fetch and they show up in the Network tab like any other — but Net Tinker works from inside the page, and a service worker is a separate place no extension of this kind can reach. A rule on that URL is created, looks enabled, and never fires. There is no fix: that's how the browser works, not a bug. The DevTools panel does recognise those requests and tells you before you lose an afternoon to it. The same goes for whatever the page's workers request, for navigator.sendBeacon, for WebSockets, and for preloads the browser resolves by itself.
  12. The request never happened, because the browser answered it from its own cache. There is nothing to intercept, and it looks exactly like a rule that doesn't work. Use ↻ Reload without cache in the popup to reload that tab ignoring the cache, or add a Modify headers rule with the Cache bypass preset so it happens on every reload.
  13. Another matching rule is overruling it. Rules don't shadow each other — they all apply — but where two of them speak about the same thing, the one lower in the list wins, and a Block the request rule beats everything. So the rule can be firing perfectly and still leave no trace: reorder the list, or narrow the other rule's pattern. See When several rules match the same request.
  14. The rule has a type this version doesn't know how to apply. It can happen after importing a file written by a newer version, or if an update left a rule half-migrated. That rule alone is skipped, exactly as if it hadn't matched — never answered with an empty mock, and any other rule that matched the same request still applies — and the row is flagged in the captures list with ⚠ plus the unrecognised type name, so the situation is visible instead of looking like ordinary traffic. Reopen the rule and pick a type again.
  15. A Modify the real response body rule matched, but the response was a live stream it can't buffer, so it was deliberately left alone. See Things that look like bugs but aren't; the captures list says "matched but not applied — streaming response" for exactly this.
  16. You have two copies of Net Tinker installed at once — typically the Web Store one plus an unpacked development build. Both patch the same page, and whichever answers first takes the request, so the other copy sees nothing: no badge, no line in the captures list, and the rule you are looking at appears to do nothing even though a rule did run. The giveaway is that the request clearly was intercepted while the extension you're watching shows no activity at all. Disable one of the two.

Seeing what actually happened

Guessing whether a rule ran is the slow way. There are three places that tell you, each answering a different question:

The captures list is off until you ask for it. Open the popup on the tab you want to watch and switch on Record this tab — only that tab's requests are captured. Then open the list with 📡 Captures in the options page (or View captures in the popup), which shows the Captured requests panel.

Each row tells you, in this order: the page, the status, the time, and then what Net Tinker did with it.

Unlike the DevTools panel, these captures are stored, so they're still there after you close the tab or the browser. The list keeps the 200 most recent and Clear empties it. Recording stops on its own if you close the tab being recorded.

The bin: undoing a delete

🗑 Bin keeps your last deletions so you can put them back. What it undoes is the gesture, not the item: deleting two hundred rules over a filter is one entry, and restoring it brings the two hundred back at once. It is also why deleting a group or a scenario with its rules inside comes back whole — the rules and their container went in together.

Every way of deleting is in there, and each row says which one it was:

It holds the last ten deletions, and at most 1 MB in total. Older ones drop off as new ones arrive — there is no expiry by days, on purpose: an extension's background worker is stopped after seconds of inactivity, so a bin that emptied itself "after a week" would only really empty itself the next time you happened to open the browser. Counting deletions needs no clock.

The 1 MB cap is there because ten deletions of one rule weigh nothing and ten of two hundred rules with mock bodies inside do. And a single deletion that on its own would not fit is not stored at all — the extension says so before deleting, so you can export first. An undo people believe they have and don't is worse than no undo.

Two things restoring will not do quietly, and it tells you when either happens:

The bin is per project: it lists what you deleted in the project you are in, and what you deleted in another one is waiting in that one. Deleting a project itself does not go to the bin — it takes its rules, groups, scenarios, variables, environments, captures and its own bin with it, for good. The confirmation says so, and lists what you are about to lose item by item.

Things that look like bugs but aren't

Net Tinker works by patching fetch/XMLHttpRequest inside the page's own JavaScript. That approach has a few unavoidable side effects:

Pasting a cURL to create the rule

cURL is the format everything is already in: every browser's DevTools has Copy as cURL, every API's documentation pastes examples in cURL, and whoever reports a bug sends you a cURL. In the rule form, next to Pattern, the Paste a cURL button opens a box to paste it into.

A cURL describes a request, not a response, so this fills in what the rule matches — the URL, the match mode and the method — and you write the answer. The dialog says so, and it shows you what it understood before applying anything.

The body and the headers, if you want them. Below the summary, the dialog asks what else to use from the cURL, and by default it uses nothing:

The pattern ends up as the whole URL, in Equals mode. If that URL carries parameters that change on every call — a ?t=1725381000 — the dialog warns you: trim them in the pattern field or the rule will never match again.

Building rules from real traffic

Open DevTools on any page and look for the "Net Tinker" panel (next to Elements, Console, Network, etc.). It lists every request the page makes while the panel is open — not just fetch/XHR, every resource type — so you can find the exact call you want to work with. The header shows Recording, and clicking it switches to Paused if you'd rather freeze the list; tick Preserve log to keep entries across navigations, and Clear empties it.

  1. Each row has a "+ Rule…" dropdown plus a dedicated Mock button. Picking a type opens the rule editor pre-filled with that request's real URL (as an Equals match, so it only affects that exact call) and its method. The dropdown offers every rule type except the mock, which has its own button.
  2. A rule built this way arrives disabled unless it's a mock or a block — those two are complete the moment they're created, while the rest are still empty skeletons, and enabling an empty "redirect" or "response body" rule would break the very endpoint you're debugging. Fill it in, then switch it on. The panel says which of the two happened each time.
  3. Mock goes further: it also copies the request's real status code, response headers and response body into the new rule, so you start from what the server actually returned instead of typing a fixture by hand.
  4. A ⚡ mark next to a row means one of your currently-enabled rules would intercept that exact request — evaluated live against your current rules, not against whatever was active when the request actually happened.
  5. Each row also has a "Copy as…" option to copy the request as a cURL command or a fetch() snippet, for pasting into a terminal or the console.
  6. Export HAR saves the requests currently visible in the list as a standard .har file, for handing to someone else or loading into another tool. The dialog lists the sensitive values it found and offers Redact credentials (always on when the dialog opens) and Include response bodies (always off, capped at 1 MB each, because bodies are what makes the file heavy). Both boxes are reset every time it opens, so the safe option is never left off from a previous export. A HAR carries real request headers, Authorization included, plus cookies — treat the file like a credential.
  7. For a GraphQL request (detected automatically and shown as its operation name next to the URL), creating a rule from it also fills in a Body condition requiring that operation's name — because a GraphQL endpoint serves every operation at the same URL, a rule matched on URL alone would otherwise apply to all of them. You can see and edit this in the opened rule (it's just the same body condition described above, pre-filled for you). The panel's filter box also searches by operation name.
  8. The panel lists everything the page does, but not everything can be mocked. Build a rule from a row that can't be, and the panel warns you before you take it for granted: the rule is created, but it will never fire. There are two separate reasons, and it tells you which one is yours:
    • How it was requested. An image from an <img>, a <script src>, a stylesheet or a font never goes through fetch/XMLHttpRequest, so there is nothing to intercept. Careful though: what counts is how it was requested, not what came back — an SVG or a PNG fetched with fetch() can be mocked, which is why they stay in the list.
    • Who requested it. If the site's own service worker made the call rather than the page, Net Tinker cannot reach in there — and this is the treacherous one, because the row looks exactly like an ordinary request. The panel names the script responsible so you can check it under Application → Service Workers.
    The "Mockable only" checkbox (on by default) hides the rows of both cases in one go. Uncheck it to see everything: you can still copy those requests as cURL, compare them and export them to HAR, all of which works for any request.

Captures shown in this panel live only in memory while DevTools is open — closing DevTools clears them, and nothing is written to storage until you explicitly turn a captured request into a rule or export a HAR. That is the difference from the captures list in the options page, which is stored on purpose.

Repeated requests, and how long everything takes

When the same request — same method, same URL — goes out several times in a row, the row carries an ×N badge. That is what turns "this feels slow" into "this is being asked for four times", which is a different conversation. The Only repeated checkbox leaves just those in the list.

The panel header also summarises the timings of what has been captured: P50, P90, P95 and P99 and the share of errors. Two things worth knowing to read it:

Searching inside the bodies

The header's search box filters by URL. The Search bodies button repeats that same search inside what travelled: useful when what you remember is not the path but an id, an error message or the name of a GraphQL operation.

Bodies are fetched from the browser one at a time and only when you ask, so over a long list it takes a moment. It is the same price you pay to look at one body, multiplied by the rows.

Copying a request to another format

Every row has Copy as…, and what it copies depends on what you want it for:

Comparing two responses

The ⇄ button on a row pins it, and the one on another row opens the comparison. No request is sent: it compares two captures you already have.

JSON bodies are compared by field rather than by line, so key order produces no differences, and matches are counted instead of listed. Two controls, inside the dialog itself:

Tokens that appear loose inside a text (a JWT, a provider key) are always masked, regardless of that checkbox. The asymmetry is deliberate: there are no false positives to lose there, and there is a secret to leak.

Starting from an OpenAPI / Swagger spec

If the API you are mocking publishes a spec, you do not have to write the twenty rules by hand. Press Import and pick the openapi.json (or swagger.json): every endpoint, method and response code in it becomes a rule with its URL pattern, its status and its body already filled in. It is the same button you use for your own export files, for a Postman collection and for a ModHeader profile: the spec is recognised by its own version stamp, and the ▾ next to the button lists everything it accepts.

What is supported

Where each mock's body comes from

In this order, and it matters:

  1. the example the spec gives for that response;
  2. the first of its named examples (the others are listed as not imported, by name);
  3. the example on the schema;
  4. a skeleton generated from the schema — the right shape with filler values ("string", 0, true), except for formats your code actually parses (dates, UUIDs, e-mails), which get a plausible value so they don't break on arrival.

That last one is not data from the API: it is invented by this extension. Any rule carrying a generated body says so in its own description, so you can still tell weeks later. Edit it into something realistic before you trust what you see on screen.

How it is organised

Choosing which endpoints come in

Nothing is created until you say so, and it is not all-or-nothing. A dialog first lists what the spec has — the same dialog a Postman collection gets, so the question is not answered differently depending on the file — with one row per endpoint, with its method, the URL it will match and how many rules and variants it would create — plus a filter, select all / select none over whatever the filter is showing, and a running count of what your ticks add up to. Rows come grouped by the spec's tags, in the order the spec declares them, with the tag written into the row so the filter finds it: on a 300-endpoint spec, typing a tag and pressing select all brings in that resource and nothing else.

Everything starts ticked, because importing destroys nothing — it all arrives disabled — and getting to "just this one endpoint" is three gestures away. What you do not tick is never created: the spec is walked twice, and the first pass only reads and counts. This is not "import it all and delete the rest".

What it tells you it could not do

Once you continue, the import dialog lists what did not survive the translation, with the reason: $refs pointing at another file, schemas that refer to themselves, oneOf alternatives where only the first was used, responses declared as default or 4XX (there is no concrete status code to use, and picking one would be inventing the answer), formats like image/png or text/csv for which no body can be made up, and response headers that declare a type rather than a value. Anything that concerns one particular response is also written into that rule's description, so it is still there once the dialog is closed.

Re-importing a newer version of the same spec is safe: the rules are recognised as already existing (same type, method and pattern) and you choose whether to overwrite, duplicate or skip them, exactly as with any other import. A field-by-field diff that preserves your local edits does not exist yet.

What has changed since last time

When you import something you had already imported — the same spec a version later, the team's pack, a Postman collection — the dialog adds a summary of what changed against the rules you already have:

The extension does not need to have kept the previous spec, and it does not keep it: the comparison is between what you are bringing now and the rules in front of you.

Bringing in a Postman collection

A Postman collection goes in through the same Import button and is recognised by its own seal, just like a spec. What becomes rules is not the requests — this extension sends nothing, it answers — but the saved response examples each request carries: every one of them has a real status code, real headers and a real body, and that is a mock field by field. Unlike a spec, nothing here is generated: what you see in the rule is what the server actually answered the day the example was saved.

With no saved examples nothing comes out, and that is the common case

Saving an example is optional in Postman and plenty of people never do it, so it is entirely possible to import a two-hundred-request collection and get no rules at all. That is not a failure: there was no response to copy. The fix is in Postman, not here — open the request, send it, and use Save Response → Save as example. That stores the real response inside the collection. Repeat for every request you want to mock and export the collection again.

The importer tells you this in those words and in aggregate — how many requests came with nothing, and a few of their names — rather than repeating the same line two hundred times.

Several examples of one request become a scenario

This is what makes the fit a good one: in Postman it is common to save several named examples of the same request — "Success", "401 Unauthorized", "500" — and that is literally a scenario, one variant per example. The variant names are the ones you typed over there, so the correspondence is not invented, and the first successful (2xx) one is left active. With a single example no scenario is built: the request stays a plain rule, because a scenario with one variant is a box with one thing in it.

How it is organised

What does not come across, and why

Anything concerning one particular example is also written into that rule's own description, so it is still there once you close the dialog.

Variables are resolved at import time, and the link is lost

This is the one thing about this importer you could reasonably assume works one way when it works another, so it gets its own heading. The syntax is the same in both places ({{name}}), but a rule's pattern is never interpolated: it is compared against real requests, so a pattern with {{baseUrl}} inside it would be a dead rule. That is why the variables appearing in a URL are replaced by their value at import time, using whatever value the collection carries.

The consequence, which is the part to remember: changing the variable afterwards does not move the rules. The pattern no longer mentions it, so pointing the collection at another environment means editing those patterns. In values — the mock's body and headers — they are kept written as {{name}} and still interpolate on every request, like any variable here.

And two things about the names:

Which versions are accepted

Choosing which requests come in

Before anything is built you get the same dialog a spec gets: one row per request, with its method, the already-resolved URL it will match and how much it would create — its rules, and its scenario with its variants if it carries several examples — plus a filter, Select all / Select none over whatever the filter is showing, and a running count of what your ticks add up to. What you do not tick is never built.

Requests with no usable example are not in that list, because there is nothing to choose about them — but they still count in the note above, which is where their absence matters.

Re-importing a newer version of the same collection is safe: the rules are recognised as already existing (same type, method and pattern) and you choose whether to overwrite, duplicate or skip them, exactly as with any other import.

There is no journey back

A scenario cannot be exported as a Postman collection, and that is not a to-do: a rule is not a request. Of the nine types only the mock would come close, and for "modify headers" or "block the request" Postman simply has no concept to put them in. On top of that, a rule's pattern is a regular expression, and a regular expression cannot be turned back into the URL it came from: an exporter would have to invent one, and that invented URL is exactly what someone would send at a real server. To take an endpoint to another tool there is Copy as… (cURL or fetch()), which works on the URL you are actually looking at.

Importing a HAR capture

A HAR is traffic that actually happened: what was requested, and what the server answered, with its exact status, headers and body. You record one in the DevTools Network tab (right-click → Save all as HAR) and it comes in through the same Import button, recognised on its own.

What it is actually for, and what sets it apart from the Activity panel: traffic captured on another machine. The panel only sees what goes through this browser; a HAR arrives by email from the laptop of whoever reproduced the bug, from a customer's machine, or from a CI run. Each request becomes a mock with the answer it got that day.

What comes in and what doesn't:

Rules are created disabled, inside a group named after the host, so you can turn on the ones you want —or delete them all together— without the app answering yesterday's session the moment the import finishes.

A HAR carries credentials inside. Session cookies, Authorization headers and tokens inside response JSON travel as they were, and the imported rules keep them — they have to, or the mock would be useless. On import you are told how many rules carry them and of what kind. Keep it in mind before exporting your pack and sharing it: that is where the sanitizer lives, and where it should be ticked (what it covers and what it doesn't).

Bringing in a ModHeader profile

ModHeader left the Chrome and Edge stores in July 2026, and a lot of people were left with their exported profiles and nothing to read them. That file goes in through the same Import button and is recognised on its own: each profile becomes a Modify headers rule, which is the type that covers its whole central case. Both the array of profiles its exporter writes and a single loose profile are accepted, because its own importer accepted both.

What becomes what

Entries that were disabled in ModHeader are not brought over switched off: to its own engine an unticked header did not exist, so importing it would have invented a rule that was never there.

Everything arrives disabled, and here it matters more than elsewhere

ModHeader applied one profile at a time — the selected one, plus any you had on always on. Switching on everything imported at once would assemble header combinations you never had together; and a profile that did not filter by URL becomes a rule that matches everything. Both of those are exactly "breaking the site you are debugging". The profiles that were on always on are flagged at import as the first candidates to enable, which is the information that separates "I want this one live now" from the rest of the batch.

What does not convert, and why

Everything below is reported at import, profile by profile and with its reason:

A profile from which nothing can be converted — because it only held things from that list — produces no rule at all, and that is reported too.

Nothing is sanitized on import, unlike on export: an exported file gets shared, but an imported one is your own data coming back to your own machine. Redacting an Authorization here would protect nobody and would leave a rule that does not work, with nowhere to recover the value from.

What "sanitize on export" does (and doesn't) cover

Export writes your rules and groups to a JSON file, together with your variables and environments. Exporting everything includes every variable you have; exporting a selection of rules only includes the variables those rules actually reference by name, and prunes each environment down to the same set. Which environment is currently active is a local preference and is never part of the file.

Because that file gets shared, the export goes through a dialog first. It lists what it found that looks like a credential, and ticks Replace credentials with __REDACTED__ for you when there is anything to hide. Sanitizing looks for names that suggest a credential — anything containing auth, token, secret, password, passwd, apikey, api_key, api-key, credential, signature, cookie or session — and replaces the matching value with __REDACTED__. It checks:

It deliberately does not touch:

Treat sanitize-on-export as a safety net against the most common accidental leak, not as a guarantee that an exported file contains no secrets — review what you're sharing before you send it.

On the way back in, Import lets you pick what to bring over and warns you when a rule already exists (same type, method and pattern), offering to import it as a copy, skip it or overwrite the existing one — with the same choice, separately, for variables whose value differs from yours. It is the same path an OpenAPI spec, a Postman collection and a ModHeader profile take, which is why none of them has an import mechanism of its own.