Skip to main content
Upload a document, wait for structuring, then point your agent at the files.

Prerequisites

You need a Shelv API key. Pick whichever path suits your workflow:
No browser required — ideal for AI agents and coding agents (Claude Code, Codex, OpenClaw).
# Register
curl -X POST https://api.shelv.dev/v1/auth/register \
  -H "Content-Type: application/json" \
  -d '{"name": "Alice", "email": "alice@example.com", "password": "your-secure-password"}'

# Check your email for the 6-digit code, then verify
curl -X POST https://api.shelv.dev/v1/auth/verify-email \
  -H "Content-Type: application/json" \
  -d '{"email": "alice@example.com", "code": "123456"}'
The verify response contains your API key. Save it:
export SHELV_API_KEY="sk_live_..."
See the full Authentication guide for details.
Set environment variables:
export SHELV_API_URL="https://api.shelv.dev"
export SHELV_API_KEY="sk_your_api_key"
const SHELV_API_URL = process.env.SHELV_API_URL ?? "https://api.shelv.dev";
const SHELV_API_KEY = process.env.SHELV_API_KEY!;
import os

shelv_api_url = os.environ.get("SHELV_API_URL", "https://api.shelv.dev")
shelv_api_key = os.environ["SHELV_API_KEY"]

1. Upload a document

curl -X POST "$SHELV_API_URL/v1/shelves" \
  -H "Authorization: Bearer $SHELV_API_KEY" \
  -F "file=@contract.pdf" \
  -F "name=My Contract"
import { readFile } from "node:fs/promises";

const form = new FormData();
form.append("file", new Blob([await readFile("contract.pdf")]), "contract.pdf");
form.append("name", "My Contract");

const res = await fetch(`${SHELV_API_URL}/v1/shelves`, {
  method: "POST",
  headers: { Authorization: `Bearer ${SHELV_API_KEY}` },
  body: form,
});
const shelf = await res.json();
import requests

with open("contract.pdf", "rb") as f:
    res = requests.post(
        f"{shelv_api_url}/v1/shelves",
        headers={"Authorization": f"Bearer {shelv_api_key}"},
        files={"file": ("contract.pdf", f)},
        data={"name": "My Contract"},
    )
shelf = res.json()
Optional fields:
  • template=book|legal-contract|charter-party|academic-paper
  • review=true to pause in review before finalization
Save the returned shelf public ID:
export SHELF_PUBLIC_ID="shf_0123456789abcdef01234567"
const SHELF_PUBLIC_ID = shelf.publicId; // "shf_0123456789abcdef01234567"
shelf_public_id = shelf["publicId"]  # "shf_0123456789abcdef01234567"

2. Wait for completion

Common flow: uploading -> parsing -> structuring -> verifying -> ready If review=true, status stops at review until you call POST /v1/shelves/{shelfPublicId}/approve.

3. Browse the filesystem

When status is ready or review:
curl "$SHELV_API_URL/v1/shelves/$SHELF_PUBLIC_ID/tree" \
  -H "Authorization: Bearer $SHELV_API_KEY"
const res = await fetch(`${SHELV_API_URL}/v1/shelves/${SHELF_PUBLIC_ID}/tree`, {
  headers: { Authorization: `Bearer ${SHELV_API_KEY}` },
});
const tree = await res.json();
import requests

res = requests.get(
    f"{shelv_api_url}/v1/shelves/{shelf_public_id}/tree",
    headers={"Authorization": f"Bearer {shelv_api_key}"},
)
tree = res.json()

4. Read a file

curl "$SHELV_API_URL/v1/shelves/$SHELF_PUBLIC_ID/files/clauses/force-majeure.md" \
  -H "Authorization: Bearer $SHELV_API_KEY"
const res = await fetch(
  `${SHELV_API_URL}/v1/shelves/${SHELF_PUBLIC_ID}/files/clauses/force-majeure.md`,
  { headers: { Authorization: `Bearer ${SHELV_API_KEY}` } },
);
const markdown = await res.text();
import requests

res = requests.get(
    f"{shelv_api_url}/v1/shelves/{shelf_public_id}/files/clauses/force-majeure.md",
    headers={"Authorization": f"Bearer {shelv_api_key}"},
)
markdown = res.text

5. Choose how your agent accesses files

  • @shelv/mcpMCP server for MCP clients (source)
  • OpenClaw skill — upload, poll, and hydrate from OpenClaw
  • GET /v1/shelves/{shelfPublicId}/tree for full tree access
  • GET /v1/shelves/{shelfPublicId}/files/* for individual file reads
  • GET /v1/shelves/{shelfPublicId}/archive-url for snapshots
  • GET /v1/helpers/download.sh for archive pull
  • @shelv/adapters for sandbox hydration and snapshots (source)
Already set up a webhook? You can skip polling entirely — see step 2.

Next steps

Shelves

Understand lifecycle and status-gated endpoints.

Authentication

Lock down API key and route auth behavior.

Webhooks

Receive signed lifecycle events.

Archive Download

Pull portable snapshots for CI and offline jobs.

MCP Server

Connect Claude Code, Cursor, or any MCP client.