There are two ways to reach a Parachute vault. MCP is for AI agents — connect Claude (or any MCP client) and it gets typed tools (query-notes, create-note, …) plus a connect-time briefing about your vault. The HTTP API is for everything else: a cron job that files a daily note, a CI step that records a deploy, a Python script that pulls your reading list. This guide is the HTTP path.
Brand-new vault? Run the set-up-your-vault starter prompt first to give it some structure, then come back here to automate against it.
The whole happy path is three steps: mint a token, point at /vault/<name>/api, send a request.
1. Mint a token
Scripts authenticate with a hub-issued JWT, scoped to exactly what the script needs. Mint one on the machine running your hub:
# Read-only token for the "default" vault, short-lived (expires in 1h)
parachute auth mint-token --scope vault:default:read --ephemeral
That prints a token to stdout — pipe it, capture it, or copy it. --ephemeral is the right default for scripting: the token lives 1 hour, long enough to mint → write your script → run it, short enough that a leaked scripting token isn't a standing liability. Drop --ephemeral for the 90-day default, or set an explicit lifetime with --expires-in <seconds> for a long-running service.
Scope to the least you need. A script that only reads gets vault:default:read; one that writes gets vault:default:write (which inherits read). The verb maps to HTTP method: GET needs read, POST/PATCH/DELETE need write. Replace default with your vault's name.
# Capture into a shell variable for the examples below
export TOKEN=$(parachute auth mint-token --scope vault:default:read --ephemeral)
export VAULT=https://your-hub.example/vault/default # or http://localhost:1939/vault/default
$VAULT is your hub's origin plus /vault/<name>. The hub is the front door — it proxies /vault/<name>/* through to the vault. Local scripts can go through the hub on http://localhost:1939/vault/<name>, or hit the vault directly on http://localhost:1940/vault/<name> (the port the API reference uses in its examples). Remote scripts use your public hub URL — only the hub is exposed publicly.
2. The API in 30 seconds
- Auth:
Authorization: Bearer <token>(orX-API-Key, or?key=for URL-only clients). - Responses are camelCase (
createdAt,sourceId) and unwrapped — the body is the data itself, not{data: …}. Errors carryerror+message(and oftenerror_type). - Query params are snake_case (
?include_content=true,?tag_match=any) — same vocabulary as the MCP tools, so one concept ports between HTTP and MCP. - Timestamps are ISO-8601 UTC. Every endpoint sends
Access-Control-Allow-Origin: *, so a static page can read your vault (writes still need a token).
3. Read notes
List notes carrying a tag (lean rows by default — pass include_content=true for full bodies):
# bash
curl -s "$VAULT/api/notes?tag=reading&sort=desc&limit=10" \
-H "Authorization: Bearer $TOKEN"
# Python (stdlib only)
import os, urllib.request, json
vault, token = os.environ["VAULT"], os.environ["TOKEN"]
req = urllib.request.Request(
f"{vault}/api/notes?tag=reading&include_content=true&limit=10",
headers={"Authorization": f"Bearer {token}"},
)
notes = json.load(urllib.request.urlopen(req))
for n in notes:
print(n["id"], "—", n.get("path") or "(no path)")
// JavaScript / TypeScript (fetch)
const vault = process.env.VAULT, token = process.env.TOKEN;
const res = await fetch(`${vault}/api/notes?tag=reading&limit=10`, {
headers: { Authorization: `Bearer ${token}` },
});
const notes = await res.json();
console.log(notes.map((n) => n.path));
Other filters compose on the same endpoint: search=query (full-text), path_prefix=Projects/, meta[status][eq]=open (metadata operators), near[note_id]=<id> (graph neighborhood), format=graph&include_links=true (a {nodes, edges} payload ready for d3/cytoscape). Use cursor for "what changed since I last checked" polling — with sort=asc (cursor mode rejects sort=desc).
4. Create a note
A write token (vault:default:write) POSTs to the same path. content is the main field (it defaults to an empty string if you omit it); path, tags, metadata, and typed links are optional:
# bash — file a dated log note
curl -s "$VAULT/api/notes" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"content": "Deployed hub 0.6.1 to prod.",
"path": "Logs/2026-05-30-deploy",
"tags": ["log", "deploy"],
"metadata": { "service": "hub", "version": "0.6.1" }
}'
# Python — append today's standup
import os, urllib.request, json
vault, token = os.environ["VAULT"], os.environ["TOKEN"]
body = json.dumps({
"content": "Standup: shipped the scope-drop fix, starting the scripting guide.",
"tags": ["standup"],
"metadata": {"date": "2026-05-30"},
}).encode()
req = urllib.request.Request(f"{vault}/api/notes", data=body, method="POST",
headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"})
print(json.load(urllib.request.urlopen(req))["id"])
Batch up to 500 notes atomically by posting { "notes": [ … ] } — a mid-batch failure rolls every prior insert back. [[Wikilinks]] in content auto-resolve to links when their target exists.
Designing your vault: tags, schemas, paths
Vault has no opinions about how you organize — it's the engine, not the schema. That freedom is the trap: a script (or an agent) can only find later what you make findable now. Three primitives, and when to reach for each:
- Tags are the primary organizing axis. They're flat labels (
reading,standup,deploy) that a note can carry many of. Filter by them (?tag=…), and an agent browses your vault by listing tags first. Prefer a small, reused vocabulary over one-off tags. - Paths are for identity and hierarchy — a note's stable address (
Projects/Parachute/roadmap), unique per vault, used for wikilink resolution. Use a path when a note is a singular thing you'll link to or update by name. Use tags when you want to group notes you'll query as a set. Rule of thumb: one path, many tags. Don't encode queryable facets into the path — that's what tags and metadata are for. - Schemas turn a tag into a typed shape. Declaring fields on a tag (e.g.
readinghasauthor: string,status: enum,rating: number indexed) lets you filter on metadata efficiently (meta[rating][gte]=4) and tells an agent what a "reading" note should contain. Mark a fieldindexedwhen you'll filter or sort on it.
A useful pattern: declare a _default tag whose fields apply to every note (a universal parent), then give specific tags their own fields on top — child tags inherit the parent's. Start loose (everything freeform), add a schema to a tag once you find yourself querying its notes the same way twice. Schemas are defined over MCP (update-tag) or the /api/tags endpoint; see the tag data model for the full convention.
No-code automation: the runner
If your "script" is really "run a prompt on a schedule," you may not need to write one at all. Parachute Runner treats your vault as the job substrate: write a note tagged job with YAML frontmatter (a schedule, a model, allowed tools, an output path), save it, and the runner polls for it and spawns claude -p against it on cadence. The job is just a note — you edit it in any client, and its runs land back in the vault. Reach for a hand-written script when you need custom logic; reach for the runner when you need an agent on a timer.
Full reference
This guide is the on-ramp. The complete endpoint catalog — every filter, the note/link/attachment shapes, cursor pagination, graph queries, tag and config endpoints — lives in the vault HTTP API reference. The auth model covers the OAuth flow, token shapes, and scope semantics in depth.