Skip to content

Commit 3b8952a

Browse files
docs: Add conversation status checking documentation for Cloud API (#365)
* 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 #364 Co-authored-by: openhands <openhands@all-hands.dev> * 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@all-hands.dev> * 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@all-hands.dev> * 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@all-hands.dev> * 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@all-hands.dev> * 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@all-hands.dev> * 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@all-hands.dev> * 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@all-hands.dev> * 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@all-hands.dev> --------- Co-authored-by: openhands <openhands@all-hands.dev>
1 parent f0656ff commit 3b8952a

1 file changed

Lines changed: 267 additions & 0 deletions

File tree

openhands/usage/cloud/cloud-api.mdx

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ The `status` field indicates the current state of the conversation startup proce
127127
- `WORKING` - Initial processing
128128
- `WAITING_FOR_SANDBOX` - Waiting for sandbox to be ready
129129
- `PREPARING_REPOSITORY` - Cloning and setting up the repository
130+
- `SETTING_UP_SKILLS` - Configuring agent skills and tools
130131
- `READY` - Conversation is ready to use
131132
- `ERROR` - An error occurred during startup
132133

@@ -167,6 +168,272 @@ The endpoint streams a JSON array incrementally. Each element represents a statu
167168

168169
Each update is streamed as it occurs, allowing you to provide real-time feedback to users about the conversation startup progress.
169170

171+
### Checking Conversation Status
172+
173+
After starting a conversation, you can check its status to monitor whether the agent has completed its task.
174+
175+
<Note>
176+
The examples below show basic polling patterns. For production use, add proper error handling,
177+
exponential backoff, and handle network failures gracefully.
178+
</Note>
179+
180+
#### Step 1: Check Start Task Status
181+
182+
When you start a conversation, you receive a start task ID. Poll this endpoint until `status` becomes `READY` and `app_conversation_id` is available:
183+
184+
```bash
185+
curl -X GET "https://app.all-hands.dev/api/v1/app-conversations/start-tasks?ids=TASK_ID" \
186+
-H "Authorization: Bearer YOUR_API_KEY"
187+
```
188+
189+
**Response:**
190+
```json
191+
{
192+
"id": "550e8400-e29b-41d4-a716-446655440000",
193+
"status": "READY",
194+
"app_conversation_id": "660e8400-e29b-41d4-a716-446655440001",
195+
"sandbox_id": "sandbox-abc123"
196+
}
197+
```
198+
199+
#### Step 2: Check Conversation Execution Status
200+
201+
Once you have the `app_conversation_id`, check whether the agent has finished its task:
202+
203+
<Tabs>
204+
<Tab title="cURL">
205+
```bash
206+
curl -X GET "https://app.all-hands.dev/api/v1/app-conversations?ids=CONVERSATION_ID" \
207+
-H "Authorization: Bearer YOUR_API_KEY"
208+
```
209+
</Tab>
210+
<Tab title="Python (with requests)">
211+
```python
212+
import requests
213+
214+
api_key = "YOUR_API_KEY"
215+
conversation_id = "YOUR_CONVERSATION_ID"
216+
217+
headers = {
218+
"Authorization": f"Bearer {api_key}",
219+
"Content-Type": "application/json"
220+
}
221+
222+
response = requests.get(
223+
"https://app.all-hands.dev/api/v1/app-conversations",
224+
headers=headers,
225+
params={"ids": conversation_id}
226+
)
227+
response.raise_for_status() # Raise exception for HTTP errors
228+
conversations = response.json()
229+
230+
if conversations:
231+
conv = conversations[0]
232+
print(f"Sandbox Status: {conv.get('sandbox_status')}")
233+
print(f"Execution Status: {conv.get('execution_status')}")
234+
else:
235+
print("Conversation not found")
236+
```
237+
</Tab>
238+
</Tabs>
239+
240+
**Response:**
241+
```json
242+
[
243+
{
244+
"id": "660e8400-e29b-41d4-a716-446655440001",
245+
"sandbox_status": "RUNNING",
246+
"execution_status": "finished",
247+
"selected_repository": "yourusername/your-repo",
248+
"title": "Fix README"
249+
}
250+
]
251+
```
252+
253+
#### Status Fields
254+
255+
**`sandbox_status`** - The state of the sandbox environment:
256+
- `STARTING` - Sandbox is being created. **Action:** Continue polling.
257+
- `RUNNING` - Sandbox is active. **Action:** Check `execution_status` for task progress.
258+
- `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.
259+
- `ERROR` - Sandbox encountered an error. **Action:** This is a terminal state. Check conversation details in the UI for error information.
260+
- `MISSING` - Sandbox was deleted. **Action:** This is a terminal state. Start a new conversation if needed.
261+
262+
**`execution_status`** - The state of the agent's task (available when sandbox is `RUNNING`):
263+
- `idle` - Agent is ready to receive tasks. **Action:** Continue polling if task was recently submitted.
264+
- `running` - Agent is actively working. **Action:** Continue polling.
265+
- `paused` - Execution is paused. **Action:** Continue polling; will resume automatically.
266+
- `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.
267+
- `finished` - Agent has completed the task. **Action:** Terminal state. Task is done successfully.
268+
- `error` - Agent encountered an error. **Action:** Terminal state. Check conversation in UI for error details.
269+
- `stuck` - Agent is stuck and unable to proceed. **Action:** Terminal state. Manual intervention may be required.
270+
271+
<Note>
272+
**Terminal states** that should exit your polling loop: `finished`, `error`, `stuck`, `waiting_for_confirmation`.
273+
The `waiting_for_confirmation` state requires user action through the UI before the agent can continue.
274+
</Note>
275+
276+
#### Complete Polling Example
277+
278+
Here's a complete example that starts a conversation and polls until completion:
279+
280+
```python
281+
import requests
282+
import time
283+
284+
api_key = "YOUR_API_KEY"
285+
base_url = "https://app.all-hands.dev"
286+
287+
headers = {
288+
"Authorization": f"Bearer {api_key}",
289+
"Content-Type": "application/json"
290+
}
291+
292+
# Start a conversation
293+
print("Starting conversation...")
294+
start_response = requests.post(
295+
f"{base_url}/api/v1/app-conversations",
296+
headers=headers,
297+
json={
298+
"initial_message": {
299+
"content": [{"type": "text", "text": "Your task here"}]
300+
},
301+
"selected_repository": "yourusername/your-repo"
302+
}
303+
)
304+
start_response.raise_for_status()
305+
start_task = start_response.json()
306+
task_id = start_task["id"]
307+
print(f"Start task ID: {task_id}")
308+
309+
# Poll start task until conversation is ready (with timeout)
310+
conversation_id = None
311+
max_attempts = 60 # 5 minutes with 5-second intervals
312+
attempts = 0
313+
while not conversation_id and attempts < max_attempts:
314+
task_response = requests.get(
315+
f"{base_url}/api/v1/app-conversations/start-tasks",
316+
headers=headers,
317+
params={"ids": task_id}
318+
)
319+
task_response.raise_for_status()
320+
tasks = task_response.json()
321+
322+
if tasks and tasks[0].get("status") == "READY":
323+
conversation_id = tasks[0].get("app_conversation_id")
324+
print(f"Conversation ready: {base_url}/conversations/{conversation_id}")
325+
elif tasks and tasks[0].get("status") == "ERROR":
326+
print(f"Start task failed: {tasks[0].get('error', 'Unknown error')}")
327+
exit(1)
328+
else:
329+
status = tasks[0].get("status") if tasks else "no response"
330+
print(f"Start task status: {status}")
331+
time.sleep(5)
332+
attempts += 1
333+
334+
if not conversation_id:
335+
print("Timeout waiting for conversation to start")
336+
exit(1)
337+
338+
# Poll conversation until agent finishes (with timeout)
339+
# Terminal states: finished, error, stuck, waiting_for_confirmation
340+
max_attempts = 120 # 1 hour with 30-second intervals
341+
attempts = 0
342+
while attempts < max_attempts:
343+
conv_response = requests.get(
344+
f"{base_url}/api/v1/app-conversations",
345+
headers=headers,
346+
params={"ids": conversation_id}
347+
)
348+
conv_response.raise_for_status()
349+
conversations = conv_response.json()
350+
351+
if not conversations:
352+
print("Warning: Conversation not found")
353+
time.sleep(30)
354+
attempts += 1
355+
continue
356+
357+
conv = conversations[0]
358+
sandbox_status = conv.get("sandbox_status")
359+
exec_status = conv.get("execution_status")
360+
361+
# Check sandbox health first
362+
if sandbox_status in ["ERROR", "MISSING"]:
363+
print(f"Sandbox failed with status: {sandbox_status}")
364+
exit(1)
365+
366+
print(f"Execution status: {exec_status}")
367+
368+
# Check for terminal states
369+
if exec_status in ["finished", "error", "stuck"]:
370+
print(f"Conversation completed with status: {exec_status}")
371+
break
372+
elif exec_status == "waiting_for_confirmation":
373+
print("Agent is waiting for user confirmation in the UI")
374+
print(f"Visit: {base_url}/conversations/{conversation_id}")
375+
break
376+
377+
time.sleep(30)
378+
attempts += 1
379+
else:
380+
print("Timeout waiting for conversation to complete")
381+
exit(1)
382+
```
383+
384+
### Listing All Conversations
385+
386+
To list all your conversations, use the search endpoint:
387+
388+
<Tabs>
389+
<Tab title="cURL">
390+
```bash
391+
curl -X GET "https://app.all-hands.dev/api/v1/app-conversations/search?limit=20" \
392+
-H "Authorization: Bearer YOUR_API_KEY"
393+
```
394+
</Tab>
395+
<Tab title="Python (with requests)">
396+
```python
397+
import requests
398+
399+
api_key = "YOUR_API_KEY"
400+
headers = {"Authorization": f"Bearer {api_key}"}
401+
402+
response = requests.get(
403+
"https://app.all-hands.dev/api/v1/app-conversations/search",
404+
headers=headers,
405+
params={"limit": 20}
406+
)
407+
response.raise_for_status()
408+
result = response.json()
409+
410+
for conv in result.get("items", []):
411+
print(f"ID: {conv['id']}, Status: {conv.get('execution_status')}")
412+
```
413+
</Tab>
414+
</Tabs>
415+
416+
**Response:**
417+
```json
418+
{
419+
"items": [
420+
{
421+
"id": "660e8400-e29b-41d4-a716-446655440001",
422+
"sandbox_status": "RUNNING",
423+
"execution_status": "finished",
424+
"selected_repository": "yourusername/your-repo",
425+
"title": "Fix README"
426+
}
427+
],
428+
"next_page_id": null
429+
}
430+
```
431+
432+
<Note>
433+
The search endpoint returns conversations in the `items` array. Use `next_page_id`
434+
for pagination if you have more conversations than the `limit`.
435+
</Note>
436+
170437
## Rate Limits
171438

172439
If you have too many conversations running at once, older conversations will be paused to limit the number of concurrent conversations.

0 commit comments

Comments
 (0)