A local-first, Git-friendly API client that you can run in your browser — like Postman, but every workspace is a plain folder of JSON and Markdown files you can commit, diff, and share.
- HTTP & GraphQL requests with params, headers, body modes (JSON, text, form-urlencoded, multipart form-data with file fields, binary file) and GraphQL query/variables editors. File bodies are referenced by path and read at send time; Content-Type is guessed from the extension.
- Binary-safe responses with a Download button; non-text responses (images, PDFs, archives) survive intact.
- Streaming protocols — WebSocket, Socket.IO, and MCP (Streamable HTTP)
request types. Connections are made through the local server (so
{{variables}}, headers, auth, and the cookie jar apply), with a live message log and reusable saved messages/emits. MCP requests browse a server's tools and call them via a form generated from each tool's JSON Schema. - cURL import/export: paste a cURL command into a collection to create a request, or copy any request as a runnable cURL command with variables resolved.
- Postman import: bring in a Postman collection or environment export
(Collection v2.1.0) — folders become collections, requests keep their
URLs, params, headers, auth and bodies, descriptions become Markdown docs,
and environment variables import (Postman secrets land in the gitignored
.local.json). - Scripts: Postman-compatible pre-request and post-response (test)
scripts with a
pm.*API — set environment variables from a response, tweak the outgoing request, and writepm.test/pm.expectassertions. Works at the request and collection level. - Request management: hover menu on sidebar items with Rename, Copy, Paste (across collections), Duplicate, and Delete; unsaved changes highlight the Save button and prompt before navigating away.
- Cookies: an automatic per-workspace cookie jar captures
Set-Cookiefrom responses and attaches matching cookies to later requests (honoring domain/path/expiry/secure); a manager lets you view and clear them, and scripts can read them viapm.cookies. - Auth helpers: Bearer token, Basic auth, API key (header or query).
- Environments (workspace-level) with
{{variable}}interpolation in URLs, params, headers, auth fields, and bodies. The active environment is stored outside the workspace so it never pollutes your repo. - Secret variables: mark a variable 🔒 and its value is written to a
gitignored
<env>.local.jsoninstead of the shared environment file — the shared file only declares the name, so teammates see exactly which secrets they need to fill in locally. - Markdown docs on every request (stored as a sibling
.mdfile, so it renders on GitHub) and on every collection. - Workspaces are folders you choose anywhere on disk via the native
system folder picker (or by typing a path) — collaborate by making a
workspace a Git repo and pushing it to GitHub. On Linux the picker uses
zenityorkdialogif installed. - MCP server: AI agents can connect over MCP to query, build, run, and manage workspaces — collections, requests, environments, execution, and Postman import — via a Streamable-HTTP endpoint on the same server.
- Requests are executed by the local Node server (no CORS problems), which returns status, headers, body, timing, and size.
- Getting started
- Workspace layout on disk
- Collaborating via Git
- Importing from Postman
- Scripting (pre-request & tests)
- Cookies
- MCP server (for AI agents)
- Keyboard shortcuts
- Stack
- Contributing
- Releases
- License
npm install
npm run devOpen http://localhost:5173. The API server runs on http://localhost:3001.
For a production-style single server (serves the built UI and the API from one port):
npm run build
npm start # http://localhost:3001my-workspace/
├── workspace.json # { id, name }
├── environments/
│ ├── dev.json # shared variables + secret variable names
│ └── dev.local.json # secret values — gitignored, per machine
└── collections/
└── users-api/
├── collection.json # { name, description }
└── requests/
├── get-user.json # method, url, params, headers, auth, body…
└── get-user.md # the request's Markdown docs
App-local state lives in ~/.apinotebook/ (the list of registered workspaces
and each workspace's active environment) — nothing machine-specific is ever
written into the workspace folder.
cd my-workspace
git init && git add . && git commit -m "Initial workspace"
gh repo create my-workspace --private --source . --pushTeammates clone the repo, then in API Notebook choose
Open existing folder… and point it at the clone. Pull to get changes,
commit and push to share yours. Keep credentials in variables marked 🔒
secret: their values live in gitignored <env>.local.json files (every
workspace .gitignore includes the rule automatically), so the repo shares
the variable names while each teammate supplies their own values. A cloned
workspace shows unfilled secrets with a "missing" warning, and their
{{tokens}} highlight red until a local value is set.
Export a collection or environment from Postman (Collection format
v2.1.0), then in API Notebook click the ⤓ button in the sidebar's
Collections heading. Choose Import file… to pick a single .json
export, or Import folder… to recursively import every collection/environment
export found in a folder at once. The file type is detected automatically, and
everything imports into the current workspace, so open or create the
workspace you want first.
What gets mapped:
- Folders → folders. Postman folders import as nestable folders inside the collection, preserving the original hierarchy. Requests sitting at the collection root stay at the root of a collection named after the Postman collection.
- Requests keep their method, URL (the query string is split into the
Params editor), headers, and auth (Bearer, Basic, API key). Bodies map
across raw JSON/text,
x-www-form-urlencoded, multipart form-data (including file fields, by path), binary file, and GraphQL. - Descriptions → Markdown docs on the request.
- Scripts: request pre-request/test scripts import into the request's Pre-request / Tests tabs. Collection- and folder-level scripts import too and run in the execution chain (collection → folder(s) → request), so they still run around every request (see Scripting). Scripts may use Postman APIs this app doesn't implement; they import as-is and surface any errors at run time.
- Environments: each
valuesentry becomes a variable. Variables Postman marks assecretare imported as 🔒 secret — the name goes to the shared environment file and the value to the gitignored<env>.local.json.
Automate requests with Postman-compatible JavaScript. Each request has a Pre-request tab (runs before sending) and a Tests tab (runs after the response arrives); collections have the same two, which run around every request in the collection. The most common use is setting environment variables from a response:
// Tests (post-response)
const data = pm.response.json();
pm.environment.set("bearer_token", data.token); // later requests use {{bearer_token}}
pm.test("login ok", () => pm.expect(pm.response.code).to.equal(200));The pm API (a pragmatic subset of Postman's):
pm.environment.get/set/unset/has/toObject— persisted to the active environment. If the variable is 🔒 secret the value goes to the gitignored<env>.local.json; otherwise to the shared file. (Setting a non-secret variable writes its value into the committable file — mark secrets as 🔒.)pm.variables.get/set— a session scope for the current run only (not saved).pm.request(pre-request) — read/modifymethod,url,headers(add/upsert/remove/get) andbody.rawbefore the request is sent.pm.response(tests) —code,status,responseTime,headers.get(),.text(),.json().pm.cookies—get(name),has(name),toObject()for the request's domain, read from the workspace cookie jar.pm.test(name, fn)and a small chai-likepm.expect(.to.equal/eql,.to.be.a/ok,.to.include,.to.have.property/status,.not).console.log/info/warn/error— captured and shown in the response's Tests tab, alongside test results and the variables a run changed.
Scripts run on the local server in a constrained node:vm sandbox (no
require, process, fetch or timers) with a 2-second timeout. This keeps
your own scripts contained on your own machine; it is not a hardened
boundary, so review scripts in collections you import from elsewhere before
running them.
Each workspace has an automatic cookie jar, like a browser. When a response
sends Set-Cookie, the cookie is stored; on later requests the matching
cookies are attached automatically (respecting domain, path, expiry and the
Secure flag). A login request can set a session cookie that subsequent
requests just use — no manual copying.
Click 🍪 Cookies in the top bar to view the jar grouped by domain and
delete individual cookies or clear them all; a response shows a 🍪 count when
it set any. Scripts can read cookies for the request's domain via
pm.cookies.get(name) / has(name) / toObject().
Cookies are stored on your machine only (in ~/.apinotebook/), never inside
the workspace folder, so they are never committed to the repo.
The server exposes a Model Context Protocol
endpoint over Streamable HTTP at http://localhost:3001/mcp, so AI agents
(Claude Code, Claude Desktop, etc.) can drive your workspaces. It runs on the
same server as the app, so just start it (npm run dev or npm start) and add
it to your favourite agent. For example:
claude mcp add --transport http api-notebook http://localhost:3001/mcpTools are multi-workspace — each takes a workspaceId (call
list_workspaces first to discover them; create new workspaces from the app UI,
since that needs a folder picker). The tool set covers full management:
- Query:
list_workspaces,get_workspace_tree,get_collection,get_request,get_environment,list_cookies. - Build & edit:
create_collection,update_collection,create_request,update_request(only the fields you pass change; use{{variable}}for interpolation),create_environment,update_environment,set_active_environment,set_variable. - Run:
execute_request— sends a saved request through the proxy with variable interpolation, pre-request/test scripts, and the cookie jar, and returns the response plus script results and test outcomes. - Import:
import_postman(collection or environment, by file path). - Destructive (flagged to the agent):
delete_request,delete_collection,clear_cookies. These are guarded — calling one withoutconfirm: truereturns a summary of exactly what would be deleted and does nothing; the agent must surface that and re-call withconfirm: trueonce you approve.
The endpoint is unauthenticated, matching the local REST API — it's meant for
localhost use only. (cURL-string import isn't an MCP tool; agents create
requests directly via create_request / update_request.)
Cmd/Ctrl+Enter— send the current requestCmd/Ctrl+S— save the current requestEnterin the URL bar — send
server/— Node + Express + TypeScript. File-based storage, request execution proxy with variable interpolation and a 30s timeout.client/— React + Vite + TypeScript. No state library; talks to the server over a small typed API client (client/src/api.ts).
Contributions are welcome! See CONTRIBUTING.md for how to set
up the project, the branching model, the
PR workflow, and our conventions. main is protected and CI must pass before a
pull request merges. By participating you agree to our
Code of Conduct.
API Notebook is released as source — each version is a Git tag and a
GitHub Release. The whole app
shares one SemVer version (root, client, and server in
lockstep). See CHANGELOG.md for what changed and
RELEASING.md for the release process.
To run a specific release:
git fetch --tags
git checkout vX.Y.Z
npm install && npm run build
npm start # http://localhost:3001MIT © Tapiwa Muzira
