API tester
It exists for one job: firing a request at the project you already have open and reading the answer. Postman still owns collections, mocks and teams — this is the thing you reach for instead of leaving your editor.

Open it from the send icon in the panel header, the send icon on a project row, the tray menu, or Ctrl+Shift+A from anywhere.
The base URL comes from the project
Opening a project scans its .env files — the root plus one level down, so monorepos work — and turns each one into an environment: .env → local, .env.stage → stage, and so on.
Which key holds the base URL differs from project to project, so the scanner knows the spellings these projects actually use — VITE_SERVER_URL, VITE_APP_BASE_URL, NUXT_PUBLIC_BASE_URL, EXPO_PUBLIC_API_BASE, AUTH_SERVICE_URL — and falls back to any *_API_URL-shaped key pointing at an http(s):// value.
Commented-out lines count too. A # VITE_SERVER_URL=https://…-stg-api… is offered as an alternative, never picked automatically.
Everything is editable by hand in ⚙ Environments, which also lists every candidate the scanner found so you can see why it chose what it chose.
Scratch tabs
Ctrl+Shift+A opens a tab that saves nothing: no file, no history row. If the clipboard holds a URL or a curl command it is already filled in.
Copy → shortcut → Ctrl+Enter is the whole loop.
If the test turns out to be worth keeping, Save to project… promotes the scratch tab into a real saved request.
Saved requests live in the project
So they travel with the code and can be committed:
<project>/.xtop/api/requests/<slug>.json
<project>/.xtop/api/environments.jsonRun history and secret values are the exception — they stay in the app's own data folder. The project file records that a request uses ; what the token is never reaches a commit.
After a login request, 🔑 Save token to picks the token out of the response and stores it for that environment.
Nested folders in the sidebar
A request stores its folder as one flat string, and a Postman import joins nested folders with " / " — so Auth / Admin / Login is read as three levels, not one folder with an odd name.
The sidebar builds a real tree from that: folders collapse and expand, remember whether they were open, and a closed folder still shows the count of everything underneath it.
The request
- Method and URL, with
resolved live underneath. Any name that has no value is flagged in red before you send, rather than failing as a 404 you have to diagnose. - Params, Headers — key/value rows, each individually enable-able. A disabled row is struck through and kept, so you can toggle a filter on and off without retyping it.
- Body — JSON (with a beautify button), form, or none.
- Auth — bearer, basic, or an API key in a header or a query parameter.
The response
The status pill, the total time, the time to first byte, and the size, then the body in one of three views:
- Pretty — a JSON tree with folding and a Find in response filter.
- Raw — the body exactly as it came back.
- Headers — every response header.
Also here: copy the body, save it to a file, the redirect count, and a warning when a very large body was truncated for display.
History

The History tab in the sidebar lists past runs for this project — method, path, status (green or red), duration and time. Clicking one loads it back. Clear history empties it; it is stored in the app data folder, never in your repository.
Import and export
- cURL in and out — paste a
curlcommand to build a request, or export the current one to paste into a terminal or a ticket. - Postman v2.1 import — brings a collection in, nested folders included.
Per-environment TLS
Local HTTPS with a self-signed certificate (.test hosts, Herd, Valet) fails certificate validation. Each environment has its own ignore TLS errors switch, so turning it on for local does not weaken prod.
Shortcuts
| Key | Action |
|---|---|
| Ctrl+Enter | Send |
| Ctrl+T | New scratch tab |
| Ctrl+W | Close tab |
| Ctrl+S | Save |
