# How to Interact with the pi-web API: A Complete Developer's Guide

> Learn how to interact with the pi-web API using REST endpoints. Programmatically manage AI agent sessions, send commands, and handle files with this developer's guide.

- Repository: [Alex Yang/pi-web](https://github.com/agegr/pi-web)
- Tags: how-to-guide
- Published: 2026-08-09

---

**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`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/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:

1. **Create a session** – `POST /api/agent/new` triggers `startRpcSession()` in [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts)
2. **Send commands** – `POST /api/agent/{id}` retrieves the wrapper via `rpc-manager.getRpcSession(id)` or resolves the session file via `resolveSessionPath`
3. **Query state** – `GET /api/agent/{id}` returns current agent configuration including model and thinking level
4. **Manage files** – `GET /api/files/...` handles filesystem operations through `app/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`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/app/api/worktrees/route.ts) |
| `GET /api/models` | List available LLM models and default configuration | [`app/api/models/route.ts`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/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:

```javascript
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`](https://github.com/agegr/pi-web/blob/main/app/api/agent/new/route.ts) implementation creates a temporary key, invokes `startRpcSession()` from [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/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}`:

```javascript
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`](https://github.com/agegr/pi-web/blob/main/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`:

```javascript
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`](https://github.com/agegr/pi-web/blob/main/app/api/sessions/route.ts) file delegates to `listAllSessions` in [`lib/session-reader.ts`](https://github.com/agegr/pi-web/blob/main/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:

```javascript
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`](https://github.com/agegr/pi-web/blob/main/lib/file-access.ts), then returns directory entries filtered for ignored patterns.

### Reading File Contents

Retrieve file content with syntax highlighting metadata:

```javascript
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:

```javascript
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`](https://github.com/agegr/pi-web/blob/main/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:

1. Resolves the absolute path from URL segments
2. Validates against `getAllowedFileRoots()` using `isFilePathAllowed`
3. 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/new` and interact via `POST /api/agent/{id}`, with [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts) handling agent lifecycle
- **State Inspection**: Query running sessions using `GET /api/sessions` and `GET /api/agent/{id}` through [`lib/session-reader.ts`](https://github.com/agegr/pi-web/blob/main/lib/session-reader.ts)
- **File Operations**: Access files through `/api/files/**` with granular control via `type` parameters, secured by [`lib/file-access.ts`](https://github.com/agegr/pi-web/blob/main/lib/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`](https://github.com/agegr/pi-web/blob/main/lib/session-reader.ts) resolves the file path via `resolveSessionPath`, while [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/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.