From a9a39cf99810bcd6eb790be02c421b5c2cb55549 Mon Sep 17 00:00:00 2001 From: openhands Date: Sat, 28 Feb 2026 14:36:06 +0000 Subject: [PATCH 1/4] Add narrative-first V0 to V1 migration guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This alternative approach restructures the migration guide to lead with a step-by-step narrative walkthrough, with reference tables moved to the end for lookup purposes. Structure: 1. Overview (brief architecture change + benefits accordion) 2. Migration Walkthrough (6 steps with V0→V1 examples) - Step 1: Create and Start a Conversation - Step 2: Track Conversation Startup - Step 3: Work with Events and Messages - Step 4: Manage Sandbox Lifecycle - Step 5: Access the Agent Server - Step 6: Handle Files and Workspace 3. Quick Reference: Endpoint Mapping (tables) 4. Agent Server API Reference 5. Known Gaps and Workarounds 6. Authentication Summary 7. Additional Resources Based on feedback from OpenHands/OpenHands#12578. Co-authored-by: openhands --- docs.json | 3 +- openhands/usage/api/migration.mdx | 512 ++++++++++++++++++++++++++++++ 2 files changed, 514 insertions(+), 1 deletion(-) create mode 100644 openhands/usage/api/migration.mdx diff --git a/docs.json b/docs.json index 8233742c5..ad36c4128 100644 --- a/docs.json +++ b/docs.json @@ -94,7 +94,8 @@ "group": "Cloud API", "pages": [ "openhands/usage/cloud/cloud-api", - "openhands/usage/api/v1" + "openhands/usage/api/v1", + "openhands/usage/api/migration" ] } ] diff --git a/openhands/usage/api/migration.mdx b/openhands/usage/api/migration.mdx new file mode 100644 index 000000000..f2315b84a --- /dev/null +++ b/openhands/usage/api/migration.mdx @@ -0,0 +1,512 @@ +--- +title: V0 to V1 Migration Guide +description: Complete guide for migrating from OpenHands V0 APIs to V1 APIs +--- + + + The V0 API (`/api/conversations`) is deprecated and scheduled for removal on **April 1, 2026**. + Please migrate to the V1 API as soon as possible. + + +## Overview + +This guide walks you through migrating from OpenHands V0 APIs to the new V1 APIs. We'll take you step-by-step through a typical migration, showing exactly what changes and why. + +### What's Different in V1? + +The most important change: **Conversations and Sandboxes are now decoupled**. + +In V0, creating a conversation automatically created and managed the underlying sandbox (runtime environment). In V1, sandboxes are independent resources you can: +- Pause and resume without losing state +- Reuse across multiple conversations +- Manage with explicit lifecycle controls + +V1 also introduces two levels of API: +- **App Server API** (`app.all-hands.dev`) - Manage conversations and sandboxes (this guide's focus) +- **Agent Server API** (per-sandbox URL) - Direct access to workspace and runtime operations + + + +| Theme | Key Result | +|-------|------------| +| **Reliability** | 61% reduction in system failures — eliminated entire classes of infrastructure errors | +| **Performance** | Lighter-weight runtime with co-located execution — removes inter-pod communication overhead | +| **Developer Experience** | Modern API patterns — dedicated search, filtering, batch operations, webhooks, and flexible sandbox controls | + +**Reliability Details** +- 61% reduction in system-attributable failures (78.0 → 30.0 errors per 1k conversations) +- Eliminated infrastructure errors from inter-pod communication +- Event-sourced state enables replay-based recovery + +**Performance Details** +- Lighter-weight agent server reduces resource overhead +- Independent sandbox lifecycle for better resource control + +**Developer Experience Details** +- Pause/resume sandboxes, explicit status tracking (`STARTING`, `RUNNING`, `PAUSED`, `ERROR`) +- Dedicated search and count endpoints +- Batch operations and enhanced filtering +- Webhook support for lifecycle events +- Stream conversation updates from creation + + + +--- + +## Migration Walkthrough + +Follow along as we migrate a typical V0 integration to V1. Each step shows your existing V0 code and the V1 equivalent. + +### Step 1: Create and Start a Conversation + +**V0 Approach** + +In V0, you called a single endpoint and got a conversation ID immediately: + +```bash +curl -X POST "https://app.all-hands.dev/api/conversations" \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "initial_user_msg": "Check the README.md file", + "repository": "yourusername/your-repo" + }' +``` + +Response: +```json +{ + "status": "ok", + "conversation_id": "abc1234" +} +``` + +**V1 Approach** + +In V1, conversation startup is asynchronous—you get a start task that tracks initialization: + +```bash +curl -X POST "https://app.all-hands.dev/api/v1/app-conversations" \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "initial_message": { + "content": [{"type": "text", "text": "Check the README.md file"}] + }, + "selected_repository": "yourusername/your-repo" + }' +``` + +Response: +```json +{ + "id": "start-task-uuid", + "status": "STARTING", + "conversation_id": null, + "sandbox_id": null, + "created_at": "2025-01-15T10:30:00Z" +} +``` + + +**Key Changes:** +- Endpoint: `/api/conversations` → `/api/v1/app-conversations` +- Field: `initial_user_msg` (string) → `initial_message` (object with content array) +- Field: `repository` → `selected_repository` +- The conversation ID isn't immediately available—see Step 2 + + +--- + +### Step 2: Track Conversation Startup + +In V0, your conversation was ready immediately. In V1, you need to wait for initialization to complete. You have two options: + +**Option A: Poll for Readiness** + +```bash +curl "https://app.all-hands.dev/api/v1/app-conversations/start-tasks?id={start_task_id}" \ + -H "Authorization: Bearer YOUR_API_KEY" +``` + +Response when ready: +```json +{ + "id": "start-task-uuid", + "status": "READY", + "conversation_id": "550e8400-e29b-41d4-a716-446655440000", + "sandbox_id": "sandbox-abc123", + "created_at": "2025-01-15T10:30:00Z" +} +``` + +The `status` field tells you where things stand: +- `STARTING` → Still initializing, keep polling +- `READY` → Conversation is ready, use `conversation_id` for subsequent calls +- `ERROR` → Something went wrong, check the `error` field + +**Option B: Stream from the Start** + +For real-time updates (recommended for user-facing apps), use the streaming endpoint: + +```bash +curl -N "https://app.all-hands.dev/api/v1/app-conversations/stream-start" \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "initial_message": { + "content": [{"type": "text", "text": "Check the README.md file"}] + }, + "selected_repository": "yourusername/your-repo" + }' +``` + +This streams server-sent events as the conversation initializes and begins processing. You'll receive status updates and can show progress to users immediately. + + +Streaming is recommended for user-facing applications where you want to show progress immediately rather than waiting for initialization to complete. + + +--- + +### Step 3: Work with Events and Messages + +Once your conversation is ready, here's how to send messages and retrieve events. + +**Sending Messages** + + + + ```bash + curl -X POST "https://app.all-hands.dev/api/conversations/{id}/messages" \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"message": "Now check the package.json"}' + ``` + + + ```bash + curl -X POST "https://app.all-hands.dev/api/v1/conversation/{id}/events" \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "content": [{"type": "text", "text": "Now check the package.json"}] + }' + ``` + + + +**Retrieving Events** + +V1 introduces dedicated search and count endpoints with enhanced filtering: + + + + ```bash + # Get all events + curl "https://app.all-hands.dev/api/conversations/{id}/events" \ + -H "Authorization: Bearer YOUR_API_KEY" + ``` + + + ```bash + # Search events with filtering + curl "https://app.all-hands.dev/api/v1/conversation/{id}/events/search?limit=50" \ + -H "Authorization: Bearer YOUR_API_KEY" + + # Get event count (useful for pagination) + curl "https://app.all-hands.dev/api/v1/conversation/{id}/events/count" \ + -H "Authorization: Bearer YOUR_API_KEY" + + # Batch get specific events by ID + curl "https://app.all-hands.dev/api/v1/conversation/{id}/events?event_id=evt1&event_id=evt2" \ + -H "Authorization: Bearer YOUR_API_KEY" + ``` + + + +--- + +### Step 4: Manage Sandbox Lifecycle + +This is entirely new in V1. You now have explicit control over sandbox resources. + +**Search for Your Sandboxes** + +```bash +curl "https://app.all-hands.dev/api/v1/sandboxes/search?limit=10" \ + -H "Authorization: Bearer YOUR_API_KEY" +``` + +**Pause a Sandbox** (stop billing, preserve state) + +```bash +curl -X POST "https://app.all-hands.dev/api/v1/sandboxes/{sandbox_id}/pause" \ + -H "Authorization: Bearer YOUR_API_KEY" +``` + +**Resume a Sandbox** (continue where you left off) + +```bash +curl -X POST "https://app.all-hands.dev/api/v1/sandboxes/{sandbox_id}/resume" \ + -H "Authorization: Bearer YOUR_API_KEY" +``` + +**Delete a Sandbox** (clean up resources) + +```bash +curl -X DELETE "https://app.all-hands.dev/api/v1/sandboxes/{sandbox_id}" \ + -H "Authorization: Bearer YOUR_API_KEY" +``` + + +**Sandbox Status Values:** +- `STARTING` - Sandbox is initializing +- `RUNNING` - Sandbox is active and ready +- `PAUSED` - Sandbox is suspended (no billing) +- `ERROR` - Something went wrong +- `MISSING` - Sandbox no longer exists + + +--- + +### Step 5: Access the Agent Server (When Needed) + +For direct workspace operations (file uploads, bash commands, git operations), you'll use the Agent Server API. This runs on each sandbox and requires a different authentication method. + +**Getting the Agent Server URL** + +First, get your sandbox details: + +```bash +curl "https://app.all-hands.dev/api/v1/sandboxes?id={sandbox_id}" \ + -H "Authorization: Bearer YOUR_API_KEY" +``` + +Response: +```json +{ + "id": "sandbox-abc123", + "status": "RUNNING", + "session_api_key": "session-key-xyz", + "exposed_urls": { + "AGENT_SERVER": "https://sandbox-abc123.runtime.all-hands.dev", + "VSCODE": "https://vscode-abc123.runtime.all-hands.dev" + } +} +``` + +**Making Agent Server Calls** + +Use the `session_api_key` with the `X-Session-API-Key` header: + +```bash +# Execute a bash command +curl -X POST "https://sandbox-abc123.runtime.all-hands.dev/api/bash/execute_bash_command" \ + -H "X-Session-API-Key: session-key-xyz" \ + -H "Content-Type: application/json" \ + -d '{"command": "ls -la"}' + +# Download a file +curl "https://sandbox-abc123.runtime.all-hands.dev/api/file/download/workspace/README.md" \ + -H "X-Session-API-Key: session-key-xyz" + +# Upload a file +curl -X POST "https://sandbox-abc123.runtime.all-hands.dev/api/file/upload/workspace/config.json" \ + -H "X-Session-API-Key: session-key-xyz" \ + -F "file=@local-config.json" +``` + + +The Agent Server's OpenAPI spec is available at `{agent_server_url}/openapi.json` for complete endpoint documentation. + + +--- + +### Step 6: Handle Files and Workspace + +File operations have moved from the App Server to the Agent Server in V1. + +| V0 Operation | V1 Equivalent | +|--------------|---------------| +| `GET /api/conversations/{id}/list-files` | Agent Server: `POST /api/bash/execute_bash_command` with `ls` | +| `GET /api/conversations/{id}/select-file` | Agent Server: `GET /api/file/download/{path}` | +| `POST /api/conversations/{id}/upload-files` | Agent Server: `POST /api/file/upload/{path}` | +| `GET /api/conversations/{id}/zip-directory` | Agent Server: `GET /api/file/download/{path}` for individual files | + +**Example: List Files in Workspace** + +```bash +# Get Agent Server URL first (see Step 5), then: +curl -X POST "https://sandbox-abc123.runtime.all-hands.dev/api/bash/execute_bash_command" \ + -H "X-Session-API-Key: session-key-xyz" \ + -H "Content-Type: application/json" \ + -d '{"command": "find /workspace -type f -name \"*.py\" | head -20"}' +``` + +**Alternative: Use the Agent SDK** + +The [OpenHands Agent SDK](https://docs.openhands.dev/sdk) provides convenient methods that work with both V0 and V1: + +```python +from openhands import Workspace + +workspace = Workspace(conversation_id="your-conversation-id") +files = workspace.list_files() +content = workspace.read_file("README.md") +workspace.write_file("output.txt", "Hello, world!") +``` + +--- + +## Quick Reference: Endpoint Mapping + +Use this section to look up specific endpoint mappings when you need them. + +### Conversation Lifecycle + +| Operation | V0 Endpoint | V1 Endpoint | +|-----------|-------------|-------------| +| Create conversation | `POST /api/conversations` | `POST /api/v1/app-conversations` | +| Start (streaming) | `POST /api/conversations/{id}/start` | `POST /api/v1/app-conversations/stream-start` | +| Search conversations | `GET /api/conversations` | `GET /api/v1/app-conversations/search` | +| Get by ID | `GET /api/conversations/{id}` | `GET /api/v1/app-conversations?id={id}` | +| Get count | N/A | `GET /api/v1/app-conversations/count` | +| Update | `PATCH /api/conversations/{id}` | `PATCH /api/v1/app-conversations/{id}` | +| Delete | `DELETE /api/conversations/{id}` | `DELETE /api/v1/app-conversations/{id}` | + +### Events & Messages + +| Operation | V0 Endpoint | V1 Endpoint | +|-----------|-------------|-------------| +| Send message | `POST /api/conversations/{id}/messages` | `POST /api/v1/conversation/{id}/events` | +| Get events | `GET /api/conversations/{id}/events` | `GET /api/v1/conversation/{id}/events/search` | +| Get event count | N/A | `GET /api/v1/conversation/{id}/events/count` | +| Batch get events | N/A | `GET /api/v1/conversation/{id}/events` | + +### Sandbox Management (New in V1) + +| Operation | V1 Endpoint | +|-----------|-------------| +| Create sandbox | `POST /api/v1/sandboxes` | +| Search sandboxes | `GET /api/v1/sandboxes/search` | +| Get sandboxes | `GET /api/v1/sandboxes` | +| Pause | `POST /api/v1/sandboxes/{id}/pause` | +| Resume | `POST /api/v1/sandboxes/{id}/resume` | +| Delete | `DELETE /api/v1/sandboxes/{id}` | + +### Development Tools + +| Operation | V0 Endpoint | V1 Equivalent | +|-----------|-------------|---------------| +| Get VSCode URL | `GET /api/conversations/{id}/vscode-url` | Get sandbox, find `VSCODE` in `exposed_urls` | +| Get web hosts | `GET /api/conversations/{id}/web-hosts` | Get sandbox, find `AGENT_SERVER` in `exposed_urls` | + +### Health & Status (Unchanged) + +| Operation | Endpoint | +|-----------|----------| +| Health check | `GET /health` | +| Alive check | `GET /alive` | +| Ready check | `GET /ready` | + +--- + +## Agent Server API Reference + +The Agent Server runs on each sandbox and provides direct access to workspace operations. + +### Authentication + +Use the `session_api_key` from your sandbox info with the `X-Session-API-Key` header. + +### Available Endpoints + + + +| Endpoint | Description | +|----------|-------------| +| `POST /api/file/upload/{path}` | Upload files to workspace | +| `GET /api/file/download/{path}` | Download individual files | +| `GET /api/file/download-trajectory/{conversation_id}` | Download conversation trajectory | + + + + + +| Endpoint | Description | +|----------|-------------| +| `GET /api/git/changes/{path}` | Get git changes | +| `GET /api/git/diff/{path}` | Get git diff | + + + + + +| Endpoint | Description | +|----------|-------------| +| `POST /api/bash/execute_bash_command` | Execute bash command (blocking) | +| `POST /api/bash/start_bash_command` | Start bash command (async) | +| `GET /api/bash/bash_events/search` | Search bash events | + + + + + +| Endpoint | Description | +|----------|-------------| +| `POST /api/conversations/{id}/events` | Send user message | +| `GET /api/conversations/{id}/events/search` | Search events | +| `GET /api/conversations/{id}/events/count` | Count events | +| `POST /api/conversations/{id}/pause` | Pause conversation | +| `POST /api/conversations/{id}/run` | Resume/run conversation | +| `DELETE /api/conversations/{id}` | Delete conversation | + + + + + +| Endpoint | Description | +|----------|-------------| +| `GET /api/vscode/url` | Get VSCode URL | +| `GET /api/vscode/status` | Check VSCode status | +| `GET /api/desktop/url` | Get desktop URL | +| `GET /api/tools/` | List available tools | + + + + +The Agent Server requires an active (running) sandbox. For operations on paused or inactive sandboxes, use the App Server API. + + +--- + +## Known Gaps and Workarounds + +Some V0 capabilities don't have direct V1 App Server equivalents yet: + +| Gap | V0 Endpoint | Workaround | +|-----|-------------|------------| +| Download workspace as zip | `GET /api/conversations/{id}/zip-directory` | Use Agent Server to download individual files, or Agent SDK `workspace.get_workspace_zip()` | +| Get trajectory (inactive runtime) | `GET /api/conversations/{id}/trajectory` | Trajectory available while sandbox is active via Agent Server | +| List workspace files | `GET /api/conversations/{id}/list-files` | Use Agent Server `POST /api/bash/execute_bash_command` with `ls`, or Agent SDK `workspace.list_files()` | + +--- + +## Authentication Summary + +| API | Authentication | +|-----|----------------| +| **App Server** (both V0 and V1) | API key via `Authorization: Bearer YOUR_API_KEY` header | +| **Agent Server** | Session API key via `X-Session-API-Key: {session_api_key}` header | + +API keys are created via the settings page or `/api/keys` endpoint and work for both V0 and V1 App Server APIs. + +--- + +## Additional Resources + +- [V1 REST API Overview](/openhands/usage/api/v1) +- [Cloud API Guide](/openhands/usage/cloud/cloud-api) +- [OpenHands Agent SDK](https://docs.openhands.dev/sdk) — Python SDK with `workspace.read_file()`, `workspace.write_file()`, `workspace.list_files()`, and more +- [V1 API Swagger Docs](https://app.all-hands.dev/docs) — Interactive API documentation From 828018497044af87be60dbcafb72ce9507dd63ab Mon Sep 17 00:00:00 2001 From: openhands Date: Mon, 2 Mar 2026 13:32:00 +0000 Subject: [PATCH 2/4] Add OpenAPI JSON links and Agent Server docs instructions - Add link to App Server OpenAPI spec (openapi.json) in Additional Resources - Add new section explaining how to access Agent Server Swagger docs and OpenAPI spec - Includes step-by-step instructions for getting Agent Server URL from sandbox Co-authored-by: openhands --- openhands/usage/api/migration.mdx | 40 +++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/openhands/usage/api/migration.mdx b/openhands/usage/api/migration.mdx index f2315b84a..ffa767e8d 100644 --- a/openhands/usage/api/migration.mdx +++ b/openhands/usage/api/migration.mdx @@ -479,6 +479,45 @@ Use the `session_api_key` from your sandbox info with the `X-Session-API-Key` he The Agent Server requires an active (running) sandbox. For operations on paused or inactive sandboxes, use the App Server API. +### Accessing Agent Server API Documentation + +The Agent Server provides its own Swagger UI and OpenAPI specification, but you need a running sandbox to access them. + +**Step 1: Get the Agent Server URL** + +First, retrieve your sandbox info to get the Agent Server URL (see [Step 5](#step-5-access-the-agent-server-directly) above): + +```bash +curl "https://app.all-hands.dev/api/v1/sandboxes?id={sandbox_id}" \ + -H "Authorization: Bearer YOUR_API_KEY" +``` + +From the response, extract the `AGENT_SERVER` URL from `exposed_urls`. + +**Step 2: Access the Documentation** + +Once you have the Agent Server URL, you can access: + +| Resource | URL | +|----------|-----| +| Swagger UI | `{agent_server_url}/docs` | +| OpenAPI JSON | `{agent_server_url}/openapi.json` | + +For example, if your Agent Server URL is `https://sandbox-abc123.runtime.all-hands.dev`: + +```bash +# View the OpenAPI spec +curl "https://sandbox-abc123.runtime.all-hands.dev/openapi.json" \ + -H "X-Session-API-Key: session-key-xyz" + +# Or open in browser for interactive Swagger UI: +# https://sandbox-abc123.runtime.all-hands.dev/docs +``` + + +The Agent Server's Swagger UI doesn't require authentication to view, but you'll need the `X-Session-API-Key` header to execute requests. + + --- ## Known Gaps and Workarounds @@ -510,3 +549,4 @@ API keys are created via the settings page or `/api/keys` endpoint and work for - [Cloud API Guide](/openhands/usage/cloud/cloud-api) - [OpenHands Agent SDK](https://docs.openhands.dev/sdk) — Python SDK with `workspace.read_file()`, `workspace.write_file()`, `workspace.list_files()`, and more - [V1 API Swagger Docs](https://app.all-hands.dev/docs) — Interactive API documentation +- [V1 API OpenAPI Spec](https://app.all-hands.dev/openapi.json) — OpenAPI JSON specification for the App Server From ed18b0b3bde6ef54b5bb5e9808456010fa245a73 Mon Sep 17 00:00:00 2001 From: openhands Date: Wed, 4 Mar 2026 20:02:35 +0000 Subject: [PATCH 3/4] Clarify that V0 requires WebSocket for sending messages while V1 uses REST - Added note highlighting that V1's REST endpoint on Agent Server is a big improvement over V0's WebSocket requirement - Updated Step 3 examples to show Socket.IO code for V0 vs simple curl for V1 - Corrected V1 endpoint to use Agent Server URL with X-Session-API-Key - Added 'run: true' option explanation for auto-starting agent loop - Updated Quick Reference table to reflect WebSocket vs REST difference --- openhands/usage/api/migration.mdx | 33 ++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/openhands/usage/api/migration.mdx b/openhands/usage/api/migration.mdx index ffa767e8d..25e03c299 100644 --- a/openhands/usage/api/migration.mdx +++ b/openhands/usage/api/migration.mdx @@ -175,24 +175,39 @@ Once your conversation is ready, here's how to send messages and retrieve events **Sending Messages** + +**Big improvement in V1:** In V0, sending messages to a running conversation typically requires establishing a WebSocket (Socket.IO) connection. V1 provides a simple REST endpoint on the Agent Server, making programmatic message sending much easier. + + - ```bash - curl -X POST "https://app.all-hands.dev/api/conversations/{id}/messages" \ - -H "Authorization: Bearer YOUR_API_KEY" \ - -H "Content-Type: application/json" \ - -d '{"message": "Now check the package.json"}' + V0 requires a WebSocket connection to send messages to a running conversation. While a REST endpoint exists, the standard approach uses Socket.IO: + + ```javascript + // V0 requires Socket.IO WebSocket connection + const socket = io(serverUrl, { + query: { conversation_id: id, latest_event_id: -1 } + }); + socket.emit("oh_user_action", { + action: "message", + args: { content: "Now check the package.json" } + }); ``` + V1 uses a simple REST call to the Agent Server (see [Step 5](#step-5-access-the-agent-server-when-needed) for how to get the Agent Server URL): + ```bash - curl -X POST "https://app.all-hands.dev/api/v1/conversation/{id}/events" \ - -H "Authorization: Bearer YOUR_API_KEY" \ + curl -X POST "{agent_server_url}/api/conversations/{id}/events" \ + -H "X-Session-API-Key: {session_api_key}" \ -H "Content-Type: application/json" \ -d '{ - "content": [{"type": "text", "text": "Now check the package.json"}] + "content": [{"type": "text", "text": "Now check the package.json"}], + "run": true }' ``` + + The `run: true` option automatically starts the agent loop to process your message. @@ -379,7 +394,7 @@ Use this section to look up specific endpoint mappings when you need them. | Operation | V0 Endpoint | V1 Endpoint | |-----------|-------------|-------------| -| Send message | `POST /api/conversations/{id}/messages` | `POST /api/v1/conversation/{id}/events` | +| Send message | WebSocket (Socket.IO) required | Agent Server: `POST /api/conversations/{id}/events` | | Get events | `GET /api/conversations/{id}/events` | `GET /api/v1/conversation/{id}/events/search` | | Get event count | N/A | `GET /api/v1/conversation/{id}/events/count` | | Batch get events | N/A | `GET /api/v1/conversation/{id}/events` | From 6373bfcfaf0a9443c88c3a23b7a44b51c3c04944 Mon Sep 17 00:00:00 2001 From: openhands Date: Wed, 4 Mar 2026 20:33:23 +0000 Subject: [PATCH 4/4] Add Step 7: Monitor Conversations and Fetch Events - Document V0 trajectory endpoint vs V1 events search API - Show V1 advantages: filtering, pagination, incremental fetches - Include Python examples for bulk event fetching and real-time polling - Add trajectory to Quick Reference endpoint mapping table - Highlight that V1 events API works without active sandbox --- openhands/usage/api/migration.mdx | 133 +++++++++++++++++++++++++++++- 1 file changed, 132 insertions(+), 1 deletion(-) diff --git a/openhands/usage/api/migration.mdx b/openhands/usage/api/migration.mdx index 25e03c299..4efb7cc14 100644 --- a/openhands/usage/api/migration.mdx +++ b/openhands/usage/api/migration.mdx @@ -374,6 +374,136 @@ workspace.write_file("output.txt", "Hello, world!") --- +### Step 7: Monitor Conversations and Fetch Events + +If you're building custom dashboards, CI/CD integrations, or alternative visualizations, you'll need to fetch conversation events. V1 provides a much more flexible approach than V0. + +**V0 Approach: Trajectory Download** + +In V0, you fetched all events at once using the trajectory endpoint: + +```bash +curl "https://app.all-hands.dev/api/conversations/{id}/trajectory" \ + -H "Authorization: Bearer YOUR_API_KEY" +``` + +This returned the entire conversation history in a single payload—simple, but inefficient for large conversations or real-time monitoring. + +**V1 Approach: Events Search API** + +V1 introduces a powerful events search endpoint with filtering, pagination, and sorting: + +```bash +curl "https://app.all-hands.dev/api/v1/conversation/{id}/events/search?limit=100&sort_order=TIMESTAMP_DESC" \ + -H "Authorization: Bearer YOUR_API_KEY" +``` + +Response: +```json +{ + "items": [ + {"id": "evt-123", "kind": "MessageEvent", "timestamp": "2025-01-15T10:30:05Z", ...}, + {"id": "evt-122", "kind": "ActionEvent", "timestamp": "2025-01-15T10:30:03Z", ...} + ], + "next_page_id": "page_xyz" +} +``` + +**Available filters:** +- `limit` - Max results per page (up to 100) +- `sort_order` - `TIMESTAMP` (oldest first) or `TIMESTAMP_DESC` (newest first) +- `timestamp__gte` - Events at or after this time +- `timestamp__lt` - Events before this time +- `kind__eq` - Filter by event type (`MessageEvent`, `ActionEvent`, `ObservationEvent`, etc.) +- `page_id` - For pagination through large result sets + +**Example: Fetch All Events (Bulk)** + +To fetch all events in a conversation (equivalent to V0 trajectory): + +```python +import requests + +def fetch_all_events(conversation_id: str, api_key: str) -> list: + """Fetch all events from a conversation.""" + base_url = "https://app.all-hands.dev" + headers = {"Authorization": f"Bearer {api_key}"} + all_events = [] + page_id = None + + while True: + params = {"limit": 100, "sort_order": "TIMESTAMP"} + if page_id: + params["page_id"] = page_id + + response = requests.get( + f"{base_url}/api/v1/conversation/{conversation_id}/events/search", + params=params, + headers=headers + ) + data = response.json() + all_events.extend(data["items"]) + + page_id = data.get("next_page_id") + if not page_id: + break + + return all_events +``` + +**Example: Efficient Polling for Real-Time Monitoring** + +For dashboards or live displays, poll for only new events since your last fetch: + +```python +import requests +import time +from datetime import datetime, timedelta + +def monitor_conversation(conversation_id: str, api_key: str, poll_interval: float = 1.0): + """Poll for new events in real-time.""" + base_url = "https://app.all-hands.dev" + headers = {"Authorization": f"Bearer {api_key}"} + last_event_id = None + last_timestamp = None + + while True: + params = {"limit": 100, "sort_order": "TIMESTAMP"} + if last_timestamp: + params["timestamp__gte"] = last_timestamp + + response = requests.get( + f"{base_url}/api/v1/conversation/{conversation_id}/events/search", + params=params, + headers=headers + ) + + events = response.json()["items"] + + for event in events: + # Skip if we've already seen this event + if event["id"] == last_event_id: + continue + + # Process the new event (your visualization logic here) + print(f"[{event['kind']}] {event.get('timestamp')}") + + last_event_id = event["id"] + last_timestamp = event["timestamp"] + + time.sleep(poll_interval) +``` + + +**Why V1 is better for monitoring:** +- **Incremental fetches** - Only get new events using `timestamp__gte`, not the entire history +- **Flexible filtering** - Filter by event type, time range, or paginate through results +- **Lower latency** - Smaller payloads mean faster responses +- **Works without active sandbox** - App Server events persist even when sandbox is paused + + +--- + ## Quick Reference: Endpoint Mapping Use this section to look up specific endpoint mappings when you need them. @@ -395,7 +525,8 @@ Use this section to look up specific endpoint mappings when you need them. | Operation | V0 Endpoint | V1 Endpoint | |-----------|-------------|-------------| | Send message | WebSocket (Socket.IO) required | Agent Server: `POST /api/conversations/{id}/events` | -| Get events | `GET /api/conversations/{id}/events` | `GET /api/v1/conversation/{id}/events/search` | +| Get trajectory | `GET /api/conversations/{id}/trajectory` | `GET /api/v1/conversation/{id}/events/search` (paginated) | +| Search events | `GET /api/conversations/{id}/events` | `GET /api/v1/conversation/{id}/events/search` | | Get event count | N/A | `GET /api/v1/conversation/{id}/events/count` | | Batch get events | N/A | `GET /api/v1/conversation/{id}/events` |