How to Interact with the pi-web API: A Complete Developer's Guide
The pi-web API exposes REST endpoints under /app/api/... that let you create AI agent sessions, send commands, and manage files programmatically using standard HTTP requests.
The agegr/pi-web repository provides a web interface for the Pi coding agent. Its REST API allows developers to integrate Pi sessions into custom workflows, automate code generation tasks, and build alternative front-ends without modifying the core application.
Understanding the pi-web API Architecture
All API endpoints are implemented as Next.js API routes located under app/api/.... The server-side logic relies on two primary libraries that handle session lifecycle and state management.
lib/rpc-manager.ts starts and caches AgentSession wrappers, mapping session IDs to live in-process agents. When you create or interact with a session, this library either returns an existing wrapper or initializes a new one via startRpcSession().
lib/session-reader.ts scans the ~/.pi/agent/sessions directory for session files, caches path-to-ID lookups, and constructs UI-friendly session contexts. This enables the API to resolve session metadata quickly without blocking the main process.
The typical request flow works as follows:
- Create a session –
POST /api/agent/newtriggersstartRpcSession()inlib/rpc-manager.ts - Send commands –
POST /api/agent/{id}retrieves the wrapper viarpc-manager.getRpcSession(id)or resolves the session file viaresolveSessionPath - Query state –
GET /api/agent/{id}returns current agent configuration including model and thinking level - Manage files –
GET /api/files/...handles filesystem operations throughapp/api/files/[...path]/route.ts
Essential API Endpoints for pi-web Integration
The following endpoints form the core of the pi-web API:
| Method & Path | Purpose | Implementation |
|---|---|---|
POST /api/agent/new |
Create a new pi session with optional initial prompt | app/api/agent/new/route.ts |
POST /api/agent/{id} |
Send commands (prompt, fork, ensure_session) to an existing session |
app/api/agent/[id]/route.ts |
GET /api/agent/{id} |
Retrieve current agent state (model, thinking level, etc.) | app/api/agent/[id]/route.ts |
GET /api/sessions |
List all sessions and currently running session IDs | app/api/sessions/route.ts |
GET /api/files/** |
File operations via type query parameter (list, read, download, meta, preview, watch) |
app/api/files/[...path]/route.ts |
POST /api/files/** |
Upload files with conflict resolution strategies | app/api/files/[...path]/route.ts |
GET /api/worktrees |
Enumerate git worktrees linked to the project | app/api/worktrees/route.ts |
GET /api/models |
List available LLM models and default configuration | app/api/models/route.ts |
POST /api/auth/login/{provider} |
Initiate OAuth or API-key authentication flows | app/api/auth/login/[provider]/route.ts |
All routes verify requests originate from trusted origins using isApiRequestAllowed and enforce filesystem boundaries through lib/file-access.ts.
Step-by-Step pi-web API Examples
The following JavaScript examples demonstrate how to interact with the pi-web API from a browser console or Node.js script. Set BASE_URL to your pi-web host (default: http://localhost:30141).
Creating a New Session
To create a session, send a POST request to /api/agent/new with the working directory, optional tool list, and initial message:
const BASE_URL = "http://localhost:30141";
async function createSession(cwd, firstPrompt) {
const resp = await fetch(`${BASE_URL}/api/agent/new`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
cwd, // absolute server path, e.g., "/home/user/project"
type: "prompt", // send prompt immediately
message: firstPrompt, // optional initial message
toolNames: ["bash"], // enable bash tool
thinkingLevel: "medium" // set reasoning depth
})
});
const data = await resp.json();
if (!resp.ok) throw new Error(data.error);
console.log("Session ID:", data.sessionId);
console.log("Model:", data.model);
return data.sessionId;
}
createSession("/home/user/project", "List files in this directory");
The app/api/agent/new/route.ts implementation creates a temporary key, invokes startRpcSession() from lib/rpc-manager.ts, and returns the persistent session ID along with model configuration.
Sending Commands to Existing Sessions
Once you have a session ID, send commands via POST /api/agent/{id}:
async function sendCommand(sessionId, command) {
const resp = await fetch(`${BASE_URL}/api/agent/${sessionId}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(command)
});
const data = await resp.json();
if (!resp.ok) throw new Error(data.error);
return data.data;
}
// Send a prompt
sendCommand("f2c9c1d0-...", {type: "prompt", message: "Explain this function"});
// Fork the conversation
sendCommand("f2c9c1d0-...", {type: "fork"});
According to app/api/agent/[id]/route.ts, this endpoint first checks rpc-manager.getRpcSession(id) for a live wrapper. If none exists, it calls resolveSessionPath from lib/session-reader.ts to locate the session file before starting a new wrapper.
Listing All Sessions
Retrieve session metadata and running status via GET /api/sessions:
async function listSessions() {
const resp = await fetch(`${BASE_URL}/api/sessions`);
const {sessions, runningSessionIds} = await resp.json();
return sessions.map(s => ({
id: s.id,
cwd: s.cwd,
name: s.name,
running: runningSessionIds.includes(s.id)
}));
}
The app/api/sessions/route.ts file delegates to listAllSessions in lib/session-reader.ts, which caches the directory scan of ~/.pi/agent/sessions for performance.
Browsing Directories
Use the type=list query parameter to enumerate files:
async function listDirectory(pathSegments) {
const path = pathSegments.map(encodeURIComponent).join('/');
const resp = await fetch(`${BASE_URL}/api/files/${path}?type=list`);
const {entries, path: fullPath} = await resp.json();
console.table(entries);
}
listDirectory(["home", "user", "project"]);
The route app/api/files/[...path]/route.ts validates the path against allowed roots using lib/file-access.ts, then returns directory entries filtered for ignored patterns.
Reading File Contents
Retrieve file content with syntax highlighting metadata:
async function readFile(pathSegments) {
const path = pathSegments.map(encodeURIComponent).join('/');
const resp = await fetch(`${BASE_URL}/api/files/${path}?type=read`);
const {content, language, size} = await resp.json();
console.log(`${language} (${size} bytes):`);
console.log(content);
}
The implementation detects language via the EXT_TO_LANGUAGE mapping and reads files as UTF-8 after verifying permissions.
Watching Files for Changes
Stream real-time updates using Server-Sent Events:
const watchUrl = `${BASE_URL}/api/files/${path}?type=watch`;
const eventSource = new EventSource(watchUrl);
eventSource.addEventListener("connected", e => console.log("Watcher ready"));
eventSource.addEventListener("change", e => {
const update = JSON.parse(e.data);
console.log("File changed:", update);
});
This creates a fs.FSWatcher instance that streams events until the client disconnects.
Security Model and File Access Control
The pi-web API implements defense-in-depth for file system access. Before processing any request, routes call isApiRequestAllowed to verify the origin. For file operations, lib/file-access.ts maintains an allowed roots cache that restricts access to whitelisted directories.
When you attempt to read or write files via /api/files/**, the server:
- Resolves the absolute path from URL segments
- Validates against
getAllowedFileRoots()usingisFilePathAllowed - Rejects requests targeting paths outside permitted boundaries with 403 errors
This security model ensures that even if session agents execute arbitrary code, the API layer prevents unauthorized file system traversal.
Summary
- Session Management: Create sessions via
POST /api/agent/newand interact viaPOST /api/agent/{id}, withlib/rpc-manager.tshandling agent lifecycle - State Inspection: Query running sessions using
GET /api/sessionsandGET /api/agent/{id}throughlib/session-reader.ts - File Operations: Access files through
/api/files/**with granular control viatypeparameters, secured bylib/file-access.ts - Security: All endpoints enforce origin validation and filesystem sandboxing before executing operations
- Extensibility: The modular architecture allows integration of worktrees, models, auth providers, and plugins through dedicated routes under
app/api/
Frequently Asked Questions
How does pi-web handle session persistence?
Sessions persist as JSON files in ~/.pi/agent/sessions. When you interact with a session ID, lib/session-reader.ts resolves the file path via resolveSessionPath, while lib/rpc-manager.ts maintains in-process wrappers for active sessions. If the server restarts, it can reconstruct session state from these files, though live agent processes must restart.
What authentication is required to use the pi-web API?
All API routes validate requests through isApiRequestAllowed, which checks the request origin against trusted hosts. Additionally, OAuth and API-key flows are available via POST /api/auth/login/{provider}. File access requires paths to exist within the allowed roots configured in lib/file-access.ts.
Can I stream large files or real-time updates through the API?
Yes. For large files, use GET /api/files/**?type=download to receive a standard streaming response. For real-time monitoring, the type=watch parameter establishes a Server-Sent Events connection that reports file changes via fs.FSWatcher until the client closes the connection.
What is the difference between ensure_session and creating a new session?
The ensure_session command, sent via POST /api/agent/{id}, verifies that a session wrapper exists and initializes one if missing, whereas POST /api/agent/new always creates a distinct session with a unique ID. Use ensure_session when resuming work on an existing session file that may not have an active agent process.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →