Copy this to your AI agent's system prompt:
# Agent Prompt for PaperPod
You have access to PaperPod - isolated compute for AI agents.
## Setup
1. POST https://paperpod.dev/login with {"email":"user@email.com"}
2. User clicks magic link → provides token (pp_sess_..., valid 30 days)
## CLI (recommended if you can shell out)
npm install -g @paperpod/cli
ppod login pp_sess_...
ppod help # discover all commands
ppod exec "python script.py"
## HTTP (for one-shot tasks)
curl -X POST https://paperpod.dev/execute \
-H "Authorization: Bearer pp_sess_..." \
-d '{"code": "ls -la", "language": "shell"}'
Update: npm update -g @paperpod/cli
Pricing: $0.0001/sec compute, $0.02/1K neurons AI. $5 free.
Docs: https://paperpod.dev/SKILL.md
# 1. Get token curl -X POST https://paperpod.dev/login -d '{"email":"you@email.com"}' # Check email → click magic link → copy token # 2. Install & login npm install -g @paperpod/cli ppod login <your-token> # 3. Try it ppod exec "echo hello world" ppod status
New accounts get $5 free credits (~14 hours). Top up via Stripe or USDC.
curl -X POST https://paperpod.dev/execute \ -H "Authorization: Bearer <your-token>" \ -d '{"code": "print(1+1)", "language": "python"}'
Login once with your email, get a PaperPod token (15 days), use it everywhere. New accounts get $5 in free credits (~14 hours).
# Request a magic link curl -X POST https://paperpod.dev/login \ -d '{"email": "you@email.com"}' # Click link in email → success page shows your token # Token format: pp_sess_abc123...
| Method | How | Best for |
|---|---|---|
| CLI login | ppod login pp_sess_... |
Interactive use |
| Env var | export PAPERPOD_TOKEN=pp_sess_... |
Scripts, CI/CD |
| Header | Authorization: Bearer pp_sess_... |
HTTP one-shots |
| Authorization: Payment | MPP credential | Wallet login and MPP payment retries |
When credits run low, top up via MPP using Tempo or Stripe. Use X-Session-Token for your PaperPod auth token because Authorization is reserved for MPP payment credentials:
# Via CLI ppod balance # check credits # Initial HTTP request (an MPP-aware client handles the 402 + retry) curl -X POST https://paperpod.dev/topup \ -H "X-Session-Token: pp_sess_..." \ -H "Content-Type: application/json" \ -d '{"tier": "starter"}' # returns an MPP payment challenge
★ Recommended for agents and developers
Handles streaming, sessions, and reconnection automatically. HTTP for one-shot tasks.
# Install npm install -g @paperpod/cli # Login (saves token to ~/.paperpod/config.json) ppod login pp_sess_... # Discover all commands ppod help
| Command | Description |
|---|---|
| Sandbox | |
| ppod exec <cmd> | Run shell command |
| ppod write <path> [file] | Write file |
| ppod read <path> | Read file |
| ppod ls <path> | List directory |
| ppod watch <path> | Watch filesystem changes (use /workspace for now) |
| Processes | |
| ppod start <cmd> | Start background process |
| ppod ps | List processes |
| ppod kill <id> | Stop process |
| ppod expose <port> | Get public URL |
| Browser (use browser: prefix or short form) | |
| ppod browser:screenshot <url> | Capture webpage |
| ppod browser:pdf <url> | Generate PDF |
| ppod browser:scrape <url> [sel] | Scrape elements (default: body) |
| ppod browser:markdown <url> | Extract markdown |
| ppod browser:content <url> | Get rendered HTML |
| ppod browser:test <url> <json> | Playwright assertions |
| ppod browser:acquire | Acquire reusable session |
| ppod browser:connect <id> | Connect to existing session |
| ppod browser:sessions | List active sessions |
| ppod browser:limits | Check browser limits |
| ppod <cmd> --help | Per-command help |
| AI | |
| ppod ai <prompt> | Text generation |
| ppod ai:embed <text> | Generate embeddings |
| ppod ai:image <prompt> | Generate image |
| ppod ai:transcribe <file> | Transcribe audio |
| ppod ai:models | List available AI models |
| Code | |
| ppod interpret <code> | Rich output (charts) |
| Memory | |
| ppod mem:write <path> | Persist data |
| ppod mem:read <path> | Read persisted data |
| ppod mem:ls | List memory files |
| ppod mem:rm <path> | Delete file |
| ppod mem:usage | Check quota |
| Account | |
| ppod balance | Check credits |
| ppod status | Connection info |
| ppod help | Show all commands |
Update CLI: npm update -g @paperpod/cli
# Execute code ppod exec "python -c 'print(2+2)'" ppod exec "npm init -y && npm install express" # Start server + expose ppod start "python -m http.server 8080 --bind 0.0.0.0" ppod expose 8080 # → https://8080-xxx.paperpod.work # Browser automation ppod screenshot https://example.com -o page.png # Persistent storage echo '{"step":3}' | ppod memory write state.json ppod memory read state.json # Watch filesystem changes (use /workspace for now) ppod watch /workspace --include "*.txt"
Save files, cache results, and resume work. 10MB per user. Survives sandbox resets.
# CLI ppod memory write state.json # reads from stdin ppod memory read state.json ppod memory ls ppod memory rm state.json ppod memory usage
| Endpoint | Description |
|---|---|
| POST /memory/write | Write file { path, content } |
| POST /memory/read | Read file { path } |
| POST /memory/list | List files { prefix? } |
| POST /memory/delete | Delete file { path } |
| POST /memory/usage | Get storage usage stats |
For one-shot tasks or when CLI isn't available. All endpoints use POST for consistency.
Execute code synchronously and return the result.
Execute code with streaming output via Server-Sent Events.
Code interpreter with rich output. Matplotlib figures returned as base64 PNG.
Write a file to the sandbox. Params: path*, content*, sessionId
Read a file from the sandbox. Params: path*, sessionId
List directory contents. Params: path*, sessionId
Watch filesystem changes over Server-Sent Events. Params: path*, recursive, include, exclude, sessionId
Use /workspace for now. Passing workspace currently resolves to /workspace/workspace and fails.
Create a directory. Params: path*, sessionId
Start a background process. Params: command*, processId, sessionId
List all running processes. Params: sessionId
Get process status. Params: processId*, sessionId
Kill a process. Params: processId*, sessionId
Expose a port and get a public preview URL.
{ "url": "https://8080-{id}-p8080_v1.paperpod.work" }
Headless Chrome on Cloudflare's edge. Screenshots, PDFs, scraping. $0.0001/second
Capture screenshot of a URL. Returns PNG image.
Generate PDF of a URL. Params: url*, format (A4/Letter), landscape
Extract markdown content from a URL. Params: url*
Extract HTML elements using CSS selector. Params: url*, selector*
Get fully rendered HTML content of a URL. Params: url*
50+ AI models on Cloudflare's GPUs. LLMs, embeddings, images, audio. $0.02/1K neurons
Text generation with LLMs (Llama, Qwen, Mistral, DeepSeek, etc.)
Generate text embeddings. Params: text* (string or array), model
Generate images from text (FLUX). Params: prompt*, width, height, num_steps
Transcribe audio to text (Whisper). Params: audio* (base64)
List available AI models. No auth required.
Full API schema in JSON format. No auth required.
Human-readable documentation (this page). No auth required.
Health check endpoint. No auth required.
The CLI uses WebSocket internally. Use WebSocket directly only for custom integrations or when you need raw control.
Note: Browser WebSocket clients cannot set custom headers. This is for server-side clients (Node.js, MCP tools).
Deprecated: Use POST /login to get a PaperPod token, then connect directly to /ws with Authorization: Bearer header.
WebSocket upgrade endpoint. Use Authorization: Bearer header.
All WebSocket messages follow a consistent pattern:
msg.type → message type (connected, result, exit, error, etc.) msg.id → correlation ID (matches your request id) msg.timestamp → unix timestamp msg.data.* → payload fields
Always access payload via msg.data.X — no guessing between msg.X vs msg.data.X.
| Type | Description |
|---|---|
| help | Get full API schema |
| ping | Health check / keep-alive |
| exec | Run shell command (with optional streaming) |
| interpret | Run code with rich output (charts, images) |
| read | Read file contents |
| write | Write file (auto-creates parent directories) |
| writeMany | Bulk write multiple files in one operation |
| list | List directory contents |
| watch | Watch filesystem changes (use /workspace for now) |
| process | Manage processes (start/list/get/stop) |
| expose | Expose port (preview URLs at *.paperpod.work) |
| memory_write | Write to persistent storage |
| memory_read | Read from persistent storage |
| memory_list | List files in persistent storage |
| memory_delete | Delete from persistent storage |
| memory_usage | Check storage quota usage |
| balance | Check remaining credits and session usage |
| browser_screenshot | Capture screenshot of URL |
| browser_pdf | Generate PDF of URL |
| browser_markdown | Extract markdown from URL |
| browser_scrape | Scrape elements with CSS selector |
| browser_content | Get rendered HTML content |
| ai_generate | Text generation with LLMs |
| ai_embed | Generate text embeddings |
| ai_image | Generate images from text |
| ai_transcribe | Transcribe audio to text |
# 1) Login and get PaperPod token curl -X POST https://paperpod.dev/login \ -d '{"email": "you@email.com"}' # → Click magic link → get pp_sess_... # 2) Connect from Node.js import WebSocket from 'ws'; const ws = new WebSocket('wss://paperpod.dev/ws', { headers: { 'Authorization': `Bearer ${token}` } }); # 3) Discover the WS schema ws.send(JSON.stringify({ type: 'help' })); # 4) Run commands / start processes / use memory ws.send(JSON.stringify({ type: 'exec', command: 'python train.py', stream: true })); ws.send(JSON.stringify({ type: 'memory_write', path: 'state.json', content: '...' })); ws.send(JSON.stringify({ type: 'expose', port: 8080 }));
New accounts: $5 in free credits (~14 hours of compute). Top up anytime via Stripe or x402.
$0.0001/second — everything included: Agent Memory, preview URLs, file I/O. No surprises.
| Operation | Rate | Notes |
|---|---|---|
| /execute | $0.0001/sec | Code execution |
| /process/* | $0.0001/sec | Background processes |
| /browser/* | $0.0001/sec | Screenshots, PDFs, scrape |
| /ai/generate | $0.02/1K neurons | Llama, Qwen, Mistral, etc. |
| /ai/embed | $0.02/1K neurons | BGE, text embeddings |
| /ai/image | $0.02/1K neurons | FLUX image generation |
| /ai/transcribe | $0.02/1K neurons | Whisper speech-to-text |
| /files/*, /memory/* | Free (included) | |
x402 upto scheme: authorize a maximum spend, we deduct actual usage. WebSocket bills per-operation, not per-connection.
A curated, safe-by-default toolchain for agents — without exposing low-level network or system attack surfaces.
Three supported languages: python, javascript, and shell
Python
ppod exec "print(2+2)" --lang python # 4
JavaScript
ppod exec "console.log(2+2)" --lang js # 4
Shell
ppod exec "ls -la" --lang shell # drwxr-xr-x ...
# Chain commands ppod exec "git clone https://... && cd repo && npm i" --lang shell # Pipe output ppod exec "cat data.json | jq '.users'" --lang shell # Process files ppod exec "ffmpeg -i in.mp4 -vn out.mp3" --lang shell
# stderr output - ffmpeg, dig, gh output to stderr ppod exec "ffmpeg -version 2>&1 | head -1" --lang shell # Use /tmp or /workspace for files ppod exec "echo 'hello' > /tmp/test.txt" --lang shell # Set timeout for long tasks ppod exec "npm run build" --lang shell --timeout 60000
| Issue | Solution |
|---|---|
| pip issues | All work: pip, pip3, python3 -m pip |
| Empty stdout | Check result.stderr — ffmpeg, dig, gh output to stderr |
| Command timeout | Add timeout=60 param or increase timeout in API request |
| File not found | Working dir is /workspace — relative paths like file.txt resolve there |
| Quote escaping | Use list args ["cmd", "arg"] instead of shell=True |
PaperPod automatically detects helpful situations and sends suggestion messages:
// When a server starts listening on a port, you receive: { "type": "suggestion", "data": { "message": "I detected a server on port 8080. Use expose to get a public URL.", "category": "port_detected", "action": { "type": "expose", "port": 8080 } } }
Process start responses include hints for next steps:
{
"success": true,
"processId": "my-server",
"hints": {
"exposePort": "Use { type: 'expose', port: <port> } to get a public URL",
"checkStatus": "Use { type: 'process', action: 'get', processId: 'my-server' }",
"stopProcess": "Use { type: 'process', action: 'stop', processId: 'my-server' }",
"bindAddress": "Ensure server binds to 0.0.0.0 (not localhost)"
}
}
Error responses include actions and URLs:
{
"error": "Credits exhausted. Top up at https://paperpod.dev/topup",
"code": "OUT_OF_CREDITS",
"action": "TOP_UP",
"topupUrl": "https://paperpod.dev/topup",
"agentInstruction": "Call POST /topup with {tier} or {amountCents}. Use X-Session-Token for PaperPod auth and let an MPP client answer the payment challenge."
}
Outbound network calls work in the sandbox:
# ✓ DNS lookups run("dig +short google.com") # ✓ Git clone run("git clone --depth 1 https://github.com/user/repo /tmp/repo") # ✓ HTTP requests run("curl -s https://api.example.com | jq .")
| Tool | Example |
|---|---|
| ripgrep | rg --line-number "def " /tmp/project |
| jq | cat data.json | jq '.users[].name' |
| ffmpeg | ffmpeg -f lavfi -i sine=frequency=440:duration=3 tone.wav |
| imagemagick | convert -size 200x100 gradient:blue-red gradient.png |
| pandoc | pandoc doc.md -o doc.html |
| sqlite3 | sqlite3 app.db "SELECT * FROM users" |
| tar | tar -czvf backup.tar.gz /tmp/files |
| tree | tree /workspace -L 2 |