Project-native HTTP client for VS Code. API collections live inside project in plain JSON files which can be committed, reviewed, and shared like any other code.
No account. No cloud.
Most HTTP clients store requests in the cloud or in a proprietary binary format. Pitman stores everything in a .http/ folder next to existing code:
- Commit requests with code. Collections are readable JSON. Can be
diffed, reviewed in PRs, rolled back with git. - Keep secrets local. Private environment files (tokens, API keys) are
.gitignored by default. - No account required. Not even to get started.
Pre-built
.vsixbinaries are available on the Releases page. Download the latest.vsixand install it via Extensions panel → ··· → Install from VSIX… or:code --install-extension pitman-<version>.vsix
Then, click the Pitman icon in the Activity Bar, or open the Command Palette and run Pitman: Open.
Pitman creates a .http/ directory in workspace and opens the request editor. If no workspace folder is open, it falls back to VS Code's global storage and points out where.
The default collection ships with one request — send it to confirm everything is working.
The editor is split into two panes.
| Tab | What to edit |
|---|---|
| Params | Query parameters |
| Headers | Request headers |
| Auth | None / Bearer Token / Basic / API Key |
| Body | None / JSON / Text / Form URL-encoded / Multipart |
| Docs | Freeform markdown notes for the request |
The response panel appears after the first request is sent and can be closed at any time.
| Tab | Meaning |
|---|---|
| Preview | Smart preview: JSON with syntax highlighting, HTML in a sandboxed iframe, XML formatted, plain text as-is |
| Raw | Exact response body |
| Headers | Request headers sent and response headers received, in separate sections |
| Timing | Status, duration, size, content-type — plus chain step timings when chaining is used |
| Tests | Assertion results (coming soon) |
| History | 10 most recent entries for this request |
Select a collection, a request, and an environment. Edit the method and URL. Click Send.
Variables in URLs, headers, params, auth fields, and bodies are resolved from the active environment before the request is sent.
| Button | What it does |
|---|---|
| New | Create a blank request in the active collection |
| Dup | Duplicate the active request |
| Del | Delete the active request (requires confirmation) |
| Save | Save edits to the collection file |
An orange ● dot appears next to the request name when there are unsaved changes. Switching requests or collections while dirty asks for confirmation first.
| Type | Notes |
|---|---|
| JSON | Textarea with one-click pretty-print and inline validation |
| Text | Plain textarea |
| Form URL-encoded | Key / value table |
| Multipart | Mixed text and file fields. File paths are stored; contents are read at send time and never written to disk. |
Collections are JSON files in .http/collections/. Each collection has its own set of associated environments.
{
"name": "Example API",
"environments": ["local", "staging"],
"requests": [...]
}Open with the Manage button next to the Collection dropdown.
- Create, rename, duplicate, or delete collections
- Open the raw JSON file directly in VS Code
- Renaming moves the file to a new slug (
Example API→example-api.json) - Deleting is permanent and requires confirmation
Environments hold the variables that are substituted into requests — base URLs, API keys, feature flags. Each environment is scoped to the collection it was created in.
Each logical environment is backed by two optional files:
| File | Committed? | Purpose |
|---|---|---|
local.env.json |
Yes | Shared variables safe to commit |
local.private.env.json |
No | Secrets — tokens, passwords, API keys |
When both files exist, private variables are merged on top of public ones. Conflicts resolve in favour of private. The environment dropdown shows one entry (local) — the split is transparent.
Use {{variableName}} anywhere in a URL, header, param, auth field, or body:
{{baseUrl}}/orders/{{orderId}}
With baseUrl = https://api.example.com/api/v1 and orderId = 42 this resolves to:
https://api.example.com/api/v1/orders/42
Open with the Manage button next to the Environment dropdown.
- Create, rename, or delete environments
- Edit public variables in a plain key/value table
- Edit private variables in a masked key/value table
- Remove an environment from a collection without deleting the files
- Associate an existing environment with a collection
- Open either environment file directly in VS Code
- Private env files are created automatically when saved in private variables
Set verifyTls to false in Settings (or directly in settings.json) when working against a server with a self-signed certificate. TLS control is per-request and does not affect the rest of VS Code.
Pitman can run one request automatically as a dependency of another. Values extracted from the upstream response — body fields, headers, cookies — are substituted into the dependent request before it is sent.
This is useful whenever one request needs session data, a token, or any other value that only exists after a previous request has run.
Use {{req(Name).accessor}} anywhere that {{variableName}} is accepted — URLs, headers, params, auth fields, bodies.
| Expression | What it extracts |
|---|---|
{{req(Login).body.session}} |
Top-level field from a JSON response body |
{{req(Login).body.players.0.id}} |
Nested / array path (dot notation, zero-indexed) |
{{req(Login).cookie(session)}} |
Named cookie value from Set-Cookie, URL-decoded |
{{req(Login).header(x-request-id)}} |
Any response header |
{{req(Login).header(set-cookie).match(token=[^;]+)}} |
First regex match on a header value |
{{req(Login).status}} |
HTTP status code as a string |
When Send is clicked, Pitman:
- Scans all fields of the current request for
{{req(...)...}}expressions. - Runs each referenced upstream request (recursively resolving its own dependencies first).
- Caches each upstream response — if two requests in the same chain both depend on Login, Login runs once.
- Substitutes the extracted values into the current request's fields.
- Sends the current request with the fully resolved values.
Upstream responses are never written to disk or to the environment. They exist only for the duration of that Send operation.
A Project request that depends on Login:
{
"session": "{{req(Login).body.session}}"
}Cookie: {{req(Login).cookie(session)}}
Clicking Send runs Login first, extracts session from the response body and session from the Set-Cookie header, substitutes both into Project, then sends it.
If Task depends on Project which depends on Login, all three run in the correct order automatically. Circular dependencies are detected before any request is sent and reported as a clear error.
| Chain expressions | Environment variables | |
|---|---|---|
| Stored on disk | No | Yes (.env.json) |
| Available across sessions | No | Yes |
| Refreshed every Send | Yes | No |
| Good for | Session tokens, one-time codes, dynamic IDs | Base URLs, static API keys, shared config |
The Pitman icon in the Activity Bar opens a lightweight navigator showing:
- Collections and their requests
- Environments
- Recent request history
Clicking a request opens the editor and selects that collection, request, and the first associated environment. Clicking an environment selects it in the active editor.
Every sent request is recorded in .http/history/history.jsonl. The file is gitignored.
Open the History panel from the toolbar button to:
- Browse all past requests, newest first
- Filter by HTTP status class (2xx / 3xx / 4xx / 5xx) or URL text
- Inspect request headers, response headers, status, timing, and a body preview
- Re-run any entry
- Clear history (requires confirmation)
The History tab in the response panel shows the 10 most recent entries for the currently selected request.
| Data | Stored? |
|---|---|
| Request method and resolved URL | Yes |
| Request headers | Yes, with sensitive headers replaced by [redacted] |
| Response status and headers | Yes, with sensitive headers replaced by [redacted] |
| Response body | First historyBodyPreviewLimit characters (default 20 000) |
| Multipart file contents | No — only the file path |
| Chain step responses | No — upstream responses are ephemeral |
| Private environment variable values | No — resolved at send time only |
Sensitive headers are configured in settings.json under redactHeaders. Default list: authorization, cookie, set-cookie, x-api-key.
Click ⚙ in the toolbar. Changes are saved to .http/settings.json.
| Setting | Default | Description |
|---|---|---|
defaultCollection |
"default" |
Collection selected when the editor opens |
defaultEnvironment |
"local" |
Environment selected when the editor opens |
timeoutMs |
30000 |
Request timeout in milliseconds |
followRedirects |
true |
Follow HTTP 3xx redirects automatically |
verifyTls |
true |
Verify TLS certificates (false for self-signed certs) |
historyBodyPreviewLimit |
20000 |
Maximum characters of response body stored in history |
redactHeaders |
see above | Header names stored as [redacted] in history |
Unknown keys already present in settings.json are preserved when saving through the UI.
.http/
collections/
default.json # Committed
environments/
.gitignore # Ignores *.private.env.json automatically
local.env.json # Committed — shared base URL, feature flags
local.private.env.json # Gitignored — tokens, passwords, secrets
history/
.gitignore # Ignores everything in history/
history.jsonl # Gitignored — local request log
settings.json # Committed — project-level Pitman config
Pitman writes its own .gitignore files inside .http/. No need to touch the root .gitignore.
Checkout this repository, then:
npm installnpm run watch # esbuild watch and tsc type-check in parallelPress F5 in VS Code to launch the Extension Development Host with the extension loaded. Open any project folder, then click the Pitman icon or run Pitman: Open from the Command Palette.
Extension-host logs appear in View → Output → Pitman.
This flow is used when needed to test the packaged extension as a real install rather than in the development host.
One-time build:
npm run package # production bundle
npm run vsix # produces pitman-<version>.vsixInstall the VSIX:
code --install-extension pitman-<version>.vsixOr via the UI: Extensions panel → ··· menu → Install from VSIX… → select the file → reload VS Code.
npm run bump # bump patch version, no git tag
npm run package
npm run vsix
code --install-extension pitman-<version>.vsix --forcenpm run release # bump patch version and creates a git tag
npm run package
npm run vsix
code --install-extension pitman-<version>.vsix --forcenpm testRequires VS Code to be installed — the test runner launches a real extension host. The suite covers:
- Pure logic:
slugify,uniqueRequestId,resolveVariables,isSensitiveHeader,redactHeaders,generateHistoryId - Collection CRUD (create, rename, duplicate, delete)
- Request CRUD (create, save, duplicate, delete)
- Environment CRUD with public/private file splitting and merge semantics
- Settings persistence and unknown-key preservation
- History append, newest-first load, and clear
- Chain expression parsing, value extraction, dependency collection, cycle detection