From 037c1038c5c571abada5301a8f69b1a8c5632121 Mon Sep 17 00:00:00 2001 From: openhands Date: Mon, 2 Mar 2026 04:32:22 +0000 Subject: [PATCH 1/9] docs: Add conversation status checking documentation for Cloud API Add documentation explaining how to check the status of conversations started via the V1 API: - Step 1: Poll start-tasks endpoint until status is READY - Step 2: Use app_conversation_id to check execution_status - Document sandbox_status and execution_status field values - Include complete polling example in Python This addresses the gap where users could start conversations but had no documented way to programmatically check if the agent completed. Closes OpenHands/docs#364 Co-authored-by: openhands --- openhands/usage/cloud/cloud-api.mdx | 161 ++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) diff --git a/openhands/usage/cloud/cloud-api.mdx b/openhands/usage/cloud/cloud-api.mdx index c8e7bb6d..2982c1bd 100644 --- a/openhands/usage/cloud/cloud-api.mdx +++ b/openhands/usage/cloud/cloud-api.mdx @@ -167,6 +167,167 @@ The endpoint streams a JSON array incrementally. Each element represents a statu Each update is streamed as it occurs, allowing you to provide real-time feedback to users about the conversation startup progress. +### Checking Conversation Status + +After starting a conversation, you can check its status to monitor whether the agent has completed its task. + +#### Step 1: Check Start Task Status + +When you start a conversation, you receive a start task ID. Poll this endpoint until `status` becomes `READY` and `app_conversation_id` is available: + +```bash +curl -X GET "https://app.all-hands.dev/api/v1/app-conversations/start-tasks?ids=TASK_ID" \ + -H "Authorization: Bearer YOUR_API_KEY" +``` + +**Response:** +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "status": "READY", + "app_conversation_id": "660e8400-e29b-41d4-a716-446655440001", + "sandbox_id": "sandbox-abc123" +} +``` + +#### Step 2: Check Conversation Execution Status + +Once you have the `app_conversation_id`, check whether the agent has finished its task: + + + + ```bash + curl -X GET "https://app.all-hands.dev/api/v1/app-conversations?ids=CONVERSATION_ID" \ + -H "Authorization: Bearer YOUR_API_KEY" + ``` + + + ```python + import requests + + api_key = "YOUR_API_KEY" + conversation_id = "YOUR_CONVERSATION_ID" + + headers = { + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json" + } + + response = requests.get( + "https://app.all-hands.dev/api/v1/app-conversations", + headers=headers, + params={"ids": conversation_id} + ) + conversations = response.json() + + if conversations: + conv = conversations[0] + print(f"Sandbox Status: {conv.get('sandbox_status')}") + print(f"Execution Status: {conv.get('execution_status')}") + ``` + + + +**Response:** +```json +[ + { + "id": "660e8400-e29b-41d4-a716-446655440001", + "sandbox_status": "RUNNING", + "execution_status": "finished", + "selected_repository": "yourusername/your-repo", + "title": "Fix README" + } +] +``` + +#### Status Fields + +**`sandbox_status`** - The state of the sandbox environment: +- `STARTING` - Sandbox is being created +- `RUNNING` - Sandbox is active +- `PAUSED` - Sandbox is paused (due to rate limits or user action) +- `ERROR` - Sandbox encountered an error +- `MISSING` - Sandbox was deleted + +**`execution_status`** - The state of the agent's task (available when sandbox is `RUNNING`): +- `idle` - Agent is ready to receive tasks +- `running` - Agent is actively working +- `paused` - Execution is paused +- `waiting_for_confirmation` - Agent is waiting for user confirmation +- `finished` - Agent has completed the task +- `error` - Agent encountered an error +- `stuck` - Agent is stuck and unable to proceed + +#### Complete Polling Example + +Here's a complete example that starts a conversation and polls until completion: + +```python +import requests +import time + +api_key = "YOUR_API_KEY" +base_url = "https://app.all-hands.dev" + +headers = { + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json" +} + +# Start a conversation +print("Starting conversation...") +start_response = requests.post( + f"{base_url}/api/v1/app-conversations", + headers=headers, + json={ + "initial_message": { + "content": [{"type": "text", "text": "Your task here"}] + }, + "selected_repository": "yourusername/your-repo" + } +) +start_task = start_response.json() +task_id = start_task["id"] +print(f"Start task ID: {task_id}") + +# Poll start task until conversation is ready +conversation_id = None +while not conversation_id: + task_response = requests.get( + f"{base_url}/api/v1/app-conversations/start-tasks", + headers=headers, + params={"ids": task_id} + ) + tasks = task_response.json() + if tasks and tasks[0].get("status") == "READY": + conversation_id = tasks[0].get("app_conversation_id") + print(f"Conversation ready: {base_url}/conversations/{conversation_id}") + else: + print(f"Start task status: {tasks[0].get('status') if tasks else 'unknown'}") + time.sleep(5) + +# Poll conversation until agent finishes +while True: + conv_response = requests.get( + f"{base_url}/api/v1/app-conversations", + headers=headers, + params={"ids": conversation_id} + ) + conversations = conv_response.json() + + if conversations: + conv = conversations[0] + exec_status = conv.get("execution_status") + print(f"Execution status: {exec_status}") + + if exec_status in ["finished", "error", "stuck"]: + print(f"Conversation completed with status: {exec_status}") + break + + time.sleep(30) +``` + ## Rate Limits If you have too many conversations running at once, older conversations will be paused to limit the number of concurrent conversations. From 50332cfe81f43f50d2b68496d81d5ed0d6980f31 Mon Sep 17 00:00:00 2001 From: openhands Date: Mon, 2 Mar 2026 04:37:22 +0000 Subject: [PATCH 2/9] docs: Add note about using batch_get for reliability The search endpoint has a known issue (OpenHands/OpenHands#13121). Recommend using batch_get by ID when the conversation ID is known. Co-authored-by: openhands --- openhands/usage/cloud/cloud-api.mdx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openhands/usage/cloud/cloud-api.mdx b/openhands/usage/cloud/cloud-api.mdx index 2982c1bd..b3612883 100644 --- a/openhands/usage/cloud/cloud-api.mdx +++ b/openhands/usage/cloud/cloud-api.mdx @@ -171,6 +171,11 @@ Each update is streamed as it occurs, allowing you to provide real-time feedback After starting a conversation, you can check its status to monitor whether the agent has completed its task. + + If you know the conversation ID, use the batch get endpoint (`GET /api/v1/app-conversations?ids=...`) + to retrieve status directly. This is the most reliable method. + + #### Step 1: Check Start Task Status When you start a conversation, you receive a start task ID. Poll this endpoint until `status` becomes `READY` and `app_conversation_id` is available: From ce17ca1a4c85f5edb669ec3578eddc2bfbbd33a5 Mon Sep 17 00:00:00 2001 From: openhands Date: Mon, 2 Mar 2026 04:42:02 +0000 Subject: [PATCH 3/9] docs: Add 'Listing All Conversations' section with search endpoint Documents the search endpoint with correct response format: - Returns 'items' array (not 'results') - Shows pagination with next_page_id Co-authored-by: openhands --- openhands/usage/cloud/cloud-api.mdx | 52 +++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/openhands/usage/cloud/cloud-api.mdx b/openhands/usage/cloud/cloud-api.mdx index b3612883..6a6913c0 100644 --- a/openhands/usage/cloud/cloud-api.mdx +++ b/openhands/usage/cloud/cloud-api.mdx @@ -333,6 +333,58 @@ while True: time.sleep(30) ``` +### Listing All Conversations + +To list all your conversations, use the search endpoint: + + + + ```bash + curl -X GET "https://app.all-hands.dev/api/v1/app-conversations/search?limit=20" \ + -H "Authorization: Bearer YOUR_API_KEY" + ``` + + + ```python + import requests + + api_key = "YOUR_API_KEY" + headers = {"Authorization": f"Bearer {api_key}"} + + response = requests.get( + "https://app.all-hands.dev/api/v1/app-conversations/search", + headers=headers, + params={"limit": 20} + ) + result = response.json() + + for conv in result.get("items", []): + print(f"ID: {conv['id']}, Status: {conv.get('execution_status')}") + ``` + + + +**Response:** +```json +{ + "items": [ + { + "id": "660e8400-e29b-41d4-a716-446655440001", + "sandbox_status": "RUNNING", + "execution_status": "finished", + "selected_repository": "yourusername/your-repo", + "title": "Fix README" + } + ], + "next_page_id": null +} +``` + + + The search endpoint returns conversations in the `items` array. Use `next_page_id` + for pagination if you have more conversations than the `limit`. + + ## Rate Limits If you have too many conversations running at once, older conversations will be paused to limit the number of concurrent conversations. From e9db133fc6eea53920cae8b7cfcbcffc9fd3b91f Mon Sep 17 00:00:00 2001 From: openhands Date: Mon, 2 Mar 2026 04:42:16 +0000 Subject: [PATCH 4/9] docs: Remove unnecessary note about batch_get The search endpoint works correctly - my earlier testing had a bug where I was checking for 'results' instead of 'items'. Co-authored-by: openhands --- openhands/usage/cloud/cloud-api.mdx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/openhands/usage/cloud/cloud-api.mdx b/openhands/usage/cloud/cloud-api.mdx index 6a6913c0..8856b257 100644 --- a/openhands/usage/cloud/cloud-api.mdx +++ b/openhands/usage/cloud/cloud-api.mdx @@ -171,11 +171,6 @@ Each update is streamed as it occurs, allowing you to provide real-time feedback After starting a conversation, you can check its status to monitor whether the agent has completed its task. - - If you know the conversation ID, use the batch get endpoint (`GET /api/v1/app-conversations?ids=...`) - to retrieve status directly. This is the most reliable method. - - #### Step 1: Check Start Task Status When you start a conversation, you receive a start task ID. Poll this endpoint until `status` becomes `READY` and `app_conversation_id` is available: From ddfe9beaed608efd262cf0830bd1992bc3da84d0 Mon Sep 17 00:00:00 2001 From: openhands Date: Mon, 2 Mar 2026 12:29:29 +0000 Subject: [PATCH 5/9] Add timeout handling to polling loops in cloud-api documentation - Add max_attempts counter to start task polling loop (5 min timeout) - Add max_attempts counter to conversation polling loop (1 hour timeout) - Handle timeout cases with appropriate error messages Co-authored-by: openhands --- openhands/usage/cloud/cloud-api.mdx | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/openhands/usage/cloud/cloud-api.mdx b/openhands/usage/cloud/cloud-api.mdx index 8856b257..73836dca 100644 --- a/openhands/usage/cloud/cloud-api.mdx +++ b/openhands/usage/cloud/cloud-api.mdx @@ -291,9 +291,11 @@ start_task = start_response.json() task_id = start_task["id"] print(f"Start task ID: {task_id}") -# Poll start task until conversation is ready +# Poll start task until conversation is ready (with timeout) conversation_id = None -while not conversation_id: +max_attempts = 60 # 5 minutes with 5-second intervals +attempts = 0 +while not conversation_id and attempts < max_attempts: task_response = requests.get( f"{base_url}/api/v1/app-conversations/start-tasks", headers=headers, @@ -306,9 +308,16 @@ while not conversation_id: else: print(f"Start task status: {tasks[0].get('status') if tasks else 'unknown'}") time.sleep(5) + attempts += 1 -# Poll conversation until agent finishes -while True: +if not conversation_id: + print("Timeout waiting for conversation to start") + exit(1) + +# Poll conversation until agent finishes (with timeout) +max_attempts = 120 # 1 hour with 30-second intervals +attempts = 0 +while attempts < max_attempts: conv_response = requests.get( f"{base_url}/api/v1/app-conversations", headers=headers, @@ -326,6 +335,9 @@ while True: break time.sleep(30) + attempts += 1 +else: + print("Timeout waiting for conversation to complete") ``` ### Listing All Conversations From dc99c815d0eb76efc8af998fef2176976337d758 Mon Sep 17 00:00:00 2001 From: openhands Date: Mon, 2 Mar 2026 12:35:44 +0000 Subject: [PATCH 6/9] fix: Address review feedback for conversation status checking - Add Note warning about production error handling at start of section - Add raise_for_status() for HTTP error handling in all examples - Use safe list access with len() checks to prevent IndexError - Improve status documentation with actions for each state - Add terminal states Note explaining which states exit polling loops - Handle waiting_for_confirmation state in Complete Polling Example - Handle ERROR status in start task polling loop Co-authored-by: openhands --- openhands/usage/cloud/cloud-api.mdx | 78 ++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 23 deletions(-) diff --git a/openhands/usage/cloud/cloud-api.mdx b/openhands/usage/cloud/cloud-api.mdx index 73836dca..2f2adc1f 100644 --- a/openhands/usage/cloud/cloud-api.mdx +++ b/openhands/usage/cloud/cloud-api.mdx @@ -171,6 +171,11 @@ Each update is streamed as it occurs, allowing you to provide real-time feedback After starting a conversation, you can check its status to monitor whether the agent has completed its task. + + The examples below show basic polling patterns. For production use, add proper error handling, + exponential backoff, and handle network failures gracefully. + + #### Step 1: Check Start Task Status When you start a conversation, you receive a start task ID. Poll this endpoint until `status` becomes `READY` and `app_conversation_id` is available: @@ -218,12 +223,15 @@ Once you have the `app_conversation_id`, check whether the agent has finished it headers=headers, params={"ids": conversation_id} ) + response.raise_for_status() # Raise exception for HTTP errors conversations = response.json() - if conversations: + if conversations and len(conversations) > 0: conv = conversations[0] print(f"Sandbox Status: {conv.get('sandbox_status')}") print(f"Execution Status: {conv.get('execution_status')}") + else: + print("Conversation not found") ``` @@ -244,20 +252,25 @@ Once you have the `app_conversation_id`, check whether the agent has finished it #### Status Fields **`sandbox_status`** - The state of the sandbox environment: -- `STARTING` - Sandbox is being created -- `RUNNING` - Sandbox is active -- `PAUSED` - Sandbox is paused (due to rate limits or user action) -- `ERROR` - Sandbox encountered an error -- `MISSING` - Sandbox was deleted +- `STARTING` - Sandbox is being created. **Action:** Continue polling. +- `RUNNING` - Sandbox is active. **Action:** Check `execution_status` for task progress. +- `PAUSED` - Sandbox is paused (due to rate limits or user action). **Action:** The sandbox will resume automatically when resources are available, or resume manually via the UI. +- `ERROR` - Sandbox encountered an error. **Action:** This is a terminal state. Check conversation details in the UI for error information. +- `MISSING` - Sandbox was deleted. **Action:** This is a terminal state. Start a new conversation if needed. **`execution_status`** - The state of the agent's task (available when sandbox is `RUNNING`): -- `idle` - Agent is ready to receive tasks -- `running` - Agent is actively working -- `paused` - Execution is paused -- `waiting_for_confirmation` - Agent is waiting for user confirmation -- `finished` - Agent has completed the task -- `error` - Agent encountered an error -- `stuck` - Agent is stuck and unable to proceed +- `idle` - Agent is ready to receive tasks. **Action:** Continue polling if task was recently submitted. +- `running` - Agent is actively working. **Action:** Continue polling. +- `paused` - Execution is paused. **Action:** Continue polling; will resume automatically. +- `waiting_for_confirmation` - Agent is waiting for user confirmation. **Action:** This is a blocking state. The agent needs user input via the UI to proceed. Your polling loop should treat this as a terminal state or alert the user. +- `finished` - Agent has completed the task. **Action:** Terminal state. Task is done successfully. +- `error` - Agent encountered an error. **Action:** Terminal state. Check conversation in UI for error details. +- `stuck` - Agent is stuck and unable to proceed. **Action:** Terminal state. Manual intervention may be required. + + + **Terminal states** that should exit your polling loop: `finished`, `error`, `stuck`, `waiting_for_confirmation`. + The `waiting_for_confirmation` state requires user action through the UI before the agent can continue. + #### Complete Polling Example @@ -287,6 +300,7 @@ start_response = requests.post( "selected_repository": "yourusername/your-repo" } ) +start_response.raise_for_status() start_task = start_response.json() task_id = start_task["id"] print(f"Start task ID: {task_id}") @@ -301,12 +315,18 @@ while not conversation_id and attempts < max_attempts: headers=headers, params={"ids": task_id} ) + task_response.raise_for_status() tasks = task_response.json() - if tasks and tasks[0].get("status") == "READY": + + if tasks and len(tasks) > 0 and tasks[0].get("status") == "READY": conversation_id = tasks[0].get("app_conversation_id") print(f"Conversation ready: {base_url}/conversations/{conversation_id}") + elif tasks and len(tasks) > 0 and tasks[0].get("status") == "ERROR": + print(f"Start task failed with error") + exit(1) else: - print(f"Start task status: {tasks[0].get('status') if tasks else 'unknown'}") + status = tasks[0].get("status") if tasks and len(tasks) > 0 else "no response" + print(f"Start task status: {status}") time.sleep(5) attempts += 1 @@ -315,6 +335,7 @@ if not conversation_id: exit(1) # Poll conversation until agent finishes (with timeout) +# Terminal states: finished, error, stuck, waiting_for_confirmation max_attempts = 120 # 1 hour with 30-second intervals attempts = 0 while attempts < max_attempts: @@ -323,16 +344,27 @@ while attempts < max_attempts: headers=headers, params={"ids": conversation_id} ) + conv_response.raise_for_status() conversations = conv_response.json() - if conversations: - conv = conversations[0] - exec_status = conv.get("execution_status") - print(f"Execution status: {exec_status}") - - if exec_status in ["finished", "error", "stuck"]: - print(f"Conversation completed with status: {exec_status}") - break + if not conversations or len(conversations) == 0: + print("Warning: Conversation not found") + time.sleep(30) + attempts += 1 + continue + + conv = conversations[0] + exec_status = conv.get("execution_status") + print(f"Execution status: {exec_status}") + + # Check for terminal states + if exec_status in ["finished", "error", "stuck"]: + print(f"Conversation completed with status: {exec_status}") + break + elif exec_status == "waiting_for_confirmation": + print("Agent is waiting for user confirmation in the UI") + print(f"Visit: {base_url}/conversations/{conversation_id}") + break time.sleep(30) attempts += 1 From 4aadec436ec8bfb44de0b44ea76b9b57dca05eff Mon Sep 17 00:00:00 2001 From: openhands Date: Mon, 2 Mar 2026 12:56:56 +0000 Subject: [PATCH 7/9] fix: address remaining review comments on polling example - Use pythonic conditionals (remove redundant len() checks) - Show API error details instead of vague message - Check sandbox_status for ERROR/MISSING states before exec_status - Add exit(1) on timeout failure - Remove redundant conversations length check Co-authored-by: openhands --- openhands/usage/cloud/cloud-api.mdx | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/openhands/usage/cloud/cloud-api.mdx b/openhands/usage/cloud/cloud-api.mdx index 2f2adc1f..deecad18 100644 --- a/openhands/usage/cloud/cloud-api.mdx +++ b/openhands/usage/cloud/cloud-api.mdx @@ -318,14 +318,14 @@ while not conversation_id and attempts < max_attempts: task_response.raise_for_status() tasks = task_response.json() - if tasks and len(tasks) > 0 and tasks[0].get("status") == "READY": + if tasks and tasks[0].get("status") == "READY": conversation_id = tasks[0].get("app_conversation_id") print(f"Conversation ready: {base_url}/conversations/{conversation_id}") - elif tasks and len(tasks) > 0 and tasks[0].get("status") == "ERROR": - print(f"Start task failed with error") + elif tasks and tasks[0].get("status") == "ERROR": + print(f"Start task failed: {tasks[0].get('error', 'Unknown error')}") exit(1) else: - status = tasks[0].get("status") if tasks and len(tasks) > 0 else "no response" + status = tasks[0].get("status") if tasks else "no response" print(f"Start task status: {status}") time.sleep(5) attempts += 1 @@ -347,14 +347,21 @@ while attempts < max_attempts: conv_response.raise_for_status() conversations = conv_response.json() - if not conversations or len(conversations) == 0: + if not conversations: print("Warning: Conversation not found") time.sleep(30) attempts += 1 continue conv = conversations[0] + sandbox_status = conv.get("sandbox_status") exec_status = conv.get("execution_status") + + # Check sandbox health first + if sandbox_status in ["ERROR", "MISSING"]: + print(f"Sandbox failed with status: {sandbox_status}") + exit(1) + print(f"Execution status: {exec_status}") # Check for terminal states @@ -370,6 +377,7 @@ while attempts < max_attempts: attempts += 1 else: print("Timeout waiting for conversation to complete") + exit(1) ``` ### Listing All Conversations From dfe8b54ddf53674e1c83b598dedaca3ae8560059 Mon Sep 17 00:00:00 2001 From: openhands Date: Mon, 2 Mar 2026 13:05:06 +0000 Subject: [PATCH 8/9] fix: Address additional review comments - Use pythonic conditional in Step 2 example (remove redundant len()) - Add raise_for_status() to Listing example for consistency Co-authored-by: openhands --- openhands/usage/cloud/cloud-api.mdx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openhands/usage/cloud/cloud-api.mdx b/openhands/usage/cloud/cloud-api.mdx index deecad18..12ceaf22 100644 --- a/openhands/usage/cloud/cloud-api.mdx +++ b/openhands/usage/cloud/cloud-api.mdx @@ -226,7 +226,7 @@ Once you have the `app_conversation_id`, check whether the agent has finished it response.raise_for_status() # Raise exception for HTTP errors conversations = response.json() - if conversations and len(conversations) > 0: + if conversations: conv = conversations[0] print(f"Sandbox Status: {conv.get('sandbox_status')}") print(f"Execution Status: {conv.get('execution_status')}") @@ -403,6 +403,7 @@ To list all your conversations, use the search endpoint: headers=headers, params={"limit": 20} ) + response.raise_for_status() result = response.json() for conv in result.get("items", []): From f23c4424ba333dcf979946e3810dd3af5413129f Mon Sep 17 00:00:00 2001 From: openhands Date: Mon, 2 Mar 2026 16:03:02 +0000 Subject: [PATCH 9/9] docs: Add missing SETTING_UP_SKILLS status to Cloud API documentation Added the SETTING_UP_SKILLS status which was discovered during API testing. This status appears when the agent is configuring skills and tools after the sandbox is ready but before the conversation is fully ready. Co-authored-by: openhands --- openhands/usage/cloud/cloud-api.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/openhands/usage/cloud/cloud-api.mdx b/openhands/usage/cloud/cloud-api.mdx index 12ceaf22..6229e336 100644 --- a/openhands/usage/cloud/cloud-api.mdx +++ b/openhands/usage/cloud/cloud-api.mdx @@ -127,6 +127,7 @@ The `status` field indicates the current state of the conversation startup proce - `WORKING` - Initial processing - `WAITING_FOR_SANDBOX` - Waiting for sandbox to be ready - `PREPARING_REPOSITORY` - Cloning and setting up the repository +- `SETTING_UP_SKILLS` - Configuring agent skills and tools - `READY` - Conversation is ready to use - `ERROR` - An error occurred during startup