Skip to content

Add OpenHands Cloud API V1 skill and minimal Python client#48

Open
enyst wants to merge 42 commits intomainfrom
openhands/openhands-api-v1-skill
Open

Add OpenHands Cloud API V1 skill and minimal Python client#48
enyst wants to merge 42 commits intomainfrom
openhands/openhands-api-v1-skill

Conversation

@enyst
Copy link
Collaborator

@enyst enyst commented Feb 14, 2026

Summary

Adds a new openhands-api-v1 skill that documents the OpenHands Cloud V1 API (app server + sandbox agent-server) and provides minimal, copyable clients for common operations.

Included:

  • skills/openhands_api_v1/SKILL.md
    • Auth model and key concepts (app server Bearer token vs agent-server X-Session-API-Key)
    • How to obtain agent_server_url + session_api_key
    • Common endpoints + usage notes
    • Clear section on counting events with 3 options (app-server count, agent-server count, trajectory zip fallback)
  • Clients:
    • Python (httpx): skills/openhands_api_v1/scripts/openhands_api_v1.py
    • TypeScript (fetch): skills/openhands_api_v1/scripts/openhands_api_v1.ts
  • References:
    • skills/openhands_api_v1/references/README.md (also points to route locations in OpenHands/OpenHands)
  • Skill README:
    • skills/openhands_api_v1/README.md

What’s covered by the minimal clients

App server (/api/v1, Bearer auth)

  • GET /users/me
  • GET /app-conversations/search
  • GET /app-conversations?ids=...
  • POST /app-conversations (start conversation; may create a sandbox)
  • GET /app-conversations/start-tasks?ids=...
  • GET /conversation/{id}/events/search
  • GET /conversation/{id}/events/count (may be flaky in some deployments)
  • GET /app-conversations/{id}/download (trajectory zip)

Agent server ({agent_server_url}/api, session auth)

  • GET /conversations/{id}/events/search (supports optional filters and sort_order=TIMESTAMP(_DESC))
  • GET /conversations/{id}/events/count (reliable count alternative)
  • POST /bash/execute_bash_command

Notes

  • Modeled after the reference clients in https://github.com/enyst/llm-playground (particularly openhands-api-client-v1).
  • Starting a conversation (POST /api/v1/app-conversations) can create a sandbox and may incur cost; the skill calls this out.
  • Agent-server event IDs are UUIDs; don’t infer counts from the “last event id”. Use count endpoints.

Co-authored-by: openhands openhands@all-hands.dev

@openhands-ai
Copy link

openhands-ai bot commented Feb 14, 2026

Looks like there are a few issues preventing this PR from being merged!

  • GitHub Actions are failing:
    • Check README.md in Skills

If you'd like me to help, just leave a comment, like

@OpenHands please fix the failing actions on PR #48 at branch `openhands/openhands-api-v1-skill`

Feel free to include any additional details that might help me get this PR into a better state.

You can manage your notification settings

@enyst enyst marked this pull request as ready for review February 14, 2026 22:52
@juanmichelini
Copy link
Collaborator

@OpenHands please compare this one to #12 are they tackling the same or different prolbems? are they different enough that they won't step into each other toes when triggered?

@openhands-ai
Copy link

openhands-ai bot commented Feb 18, 2026

I'm on it! juanmichelini can track my progress at all-hands.dev

@enyst
Copy link
Collaborator Author

enyst commented Feb 18, 2026

Good point! I checked they say V0, respectively V1, in the description, and they have different triggers.

Idk… Idk if it’s too late for V0 actually - I was thinking that for the moment, GitHub/Slack resolvers still use it.

Also, we have a migration V0->V1 issue somewhere, it asks for docs comparing the two and documenting for people how to migrate, so idk, teaching the agent about them then let it test how you execute a thing with V0 and the same thing with V1 could be part of writing or checking the docs we will publish.

That said, I’m fine with removing V0… We really should use only V1 in normal use, and more importantly, I think maybe we should recommend to users only V1… 🤔

@enyst
Copy link
Collaborator Author

enyst commented Feb 18, 2026

@OpenHands Read all comments in this PR and investigate what you need to answer juanmichelini 's question. Including look at the linked PR diff and this one.

Answer it directly on github in a comment. Note that it will be rendered as markdown.

@openhands-ai
Copy link

openhands-ai bot commented Feb 18, 2026

I'm on it! enyst can track my progress at all-hands.dev

Copy link
Collaborator Author

enyst commented Feb 18, 2026

@juanmichelini

PR #12 (merged) adds the openhands-api skill for the legacy V0 OpenHands routes (/api/...), with Bearer auth and minimal clients. It’s primarily useful for older/legacy integrations (e.g. some current resolvers).

This PR (#48) adds openhands-api-v1 for the Cloud V1 routes (/api/v1/...) plus the sandbox agent-server API ({agent_server_url}/api/... with X-Session-API-Key). It focuses on the newer resources/flows (app-conversations, start-tasks, sandboxes, reliable event counting via agent-server, trajectory zip fallback, etc.).

Will they step on each other’s toes?

In practice, they shouldn’t:

  • Different skill ids/dirs: skills/openhands_api/ vs skills/openhands_api_v1/ (no code/path collision).
  • Different trigger keywords (versioned and intentionally disjoint):
    • V0 skill triggers: openhands-api-v0, openhands-cloud-v0, oh-cloud-v0
    • V1 skill triggers: openhands-api-v1 (plus oh-cloud-api, oh-api, agent-server, agent-server-api)
    • So V0 won’t auto-trigger on V1 requests (and vice versa) unless someone explicitly uses the other version’s keywords.

Net: same general domain (programmatic OpenHands API usage), but different API generations. They can coexist; V1 should be the default recommendation going forward, while V0 stays available for legacy deployments/integrations until migration is complete.

@openhands-ai

This comment was marked as duplicate.

Copy link

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taste Rating: 🟡 Acceptable

This is pragmatic code solving a real problem. The design is straightforward, but there are some issues that need attention—particularly a path typo and missing tests for reference code.

@enyst
Copy link
Collaborator Author

enyst commented Feb 19, 2026

@OpenHands Use gh to read all comments in this PR, particularly inline review threads. They are AI reviews, so they could be hallucinated; so verify first if they are valid. Then fix them.

Note: we will not add tests with auth and stuff here, I don't think. But the rest of comments are worth checking!

You have the relevant key in your env, so you can test live what you modify. Do that and fix the client as appropriate.

IMPORTANT: we are nice. Think deeply before you make an API call, check that it's correct and then make it; do not create scripts that use loops, do not poll heavily, to not stress the API. Single requests, do the necessary, report at the end.

When you are done, push, and post a comment on top-level with all you did. Also, reply to the inline review threads with your resolution. Note that all will be rendered as markdown.

@openhands-ai
Copy link

openhands-ai bot commented Feb 19, 2026

I'm on it! enyst can track my progress at all-hands.dev

Copy link
Collaborator Author

enyst commented Feb 19, 2026

Addressed the inline review feedback + validated the reference client with minimal live calls.

Changes

  • Fixed broken CLI example path in skills/openhands_api_v1/SKILL.md (it pointed at skills/openhands_api/..., which does not exist in this repo).
  • Added skills/openhands_api_v1/references/example_prompt.md so the CLI example is copy/paste runnable.
  • TypeScript client refactor: introduced a shared baseRequest() helper and added parseAs: "json" | "blob" so appConversationDownloadZip() reuses the same error handling + auth logic.
  • Python client: removed the poll_start_task_until_ready() helper (avoids encouraging API polling/loops in the minimal reference code) and cleaned up the now-unused import.

Validation

  • Ran GET /api/v1/users/me and GET /api/v1/app-conversations/search?limit=1 using the Python client (single requests; no loops / polling).
  • python -m compileall on the Python script passes locally.

Replies are posted in each inline thread with the specific resolution.

@openhands-ai
Copy link

openhands-ai bot commented Feb 19, 2026

Since my last summary, there have been no additional code changes or commits (the PR branch remains at commit 28a6d5a), and no further modifications were needed.

Final checklist against your instructions

  • Read all PR comments, including inline review threads, using gh
  • Verified the inline feedback was real/valid (not hallucinated), then fixed the valid items
  • Made only minimal, relevant changes (docs/example path + small client refactor/cleanup)
  • Performed live validation with the available key using single requests only (no loops/polling)
  • Pushed the changes to openhands/openhands-api-v1-skill
  • Posted a top-level PR comment summarizing work
  • Replied in each inline review thread with the resolution
  • Confirmed CI is green

No extraneous changes were introduced after the last update, and the request has been fully addressed.

Copy link
Collaborator Author

@enyst enyst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

enyst and others added 2 commits February 20, 2026 00:06
- Fix broken prompt-file path in SKILL.md and add example prompt file
- Refactor TS client to reuse request logic and support blob downloads
- Remove polling helper from Python reference client to avoid API loops

Co-authored-by: openhands <openhands@all-hands.dev>
Includes exponential backoff, sleeps between requests, and optional max_polls cap.

Co-authored-by: openhands <openhands@all-hands.dev>
Removed deprecated openhands-api entry from default.json.
Copy link
Contributor

Here are a few suggestions from a skill I have been using for ~30 days:

  1. Event Structure Documentation — A table breaking down event kind values (ActionEvent, ObservationEvent, MessageEvent, ConversationStateUpdateEvent) with their sources and key fields, plus example JSON showing how tool_call_id and action_id link actions to observations.

  2. Debugging Scripts — Python one-liners for common debugging tasks:

    • Analyzing event timelines (print timestamp/source/kind)
    • Finding error events
    • Checking tool call matching (unmatched actions, duplicate observations)
  3. Common Issues / Troubleshooting — Guidance on diagnosing:

    • "Service Temporarily Unavailable" → check runtime_status
    • Tool Use / Tool Result mismatch errors
    • 404 on Events Endpoint (V1 path, access issues)
  4. Practical Tips — e.g., "Save responses locally for analysis", "Use jq for quick filtering", "Check runtime_status before querying events"

I leave it entirely to your judgement whether you think any of these would be helpful for your work.

Copy link
Contributor

jpshackelford commented Feb 26, 2026

Here are some other tips I've gathered from having OH work with the API:

Suggested addition to SKILL.md

Add this section after "Common agent server endpoints":


Troubleshooting / Common Issues

1. Direct ID lookup returns HTML instead of JSON

Symptom: Calling GET /api/v1/app-conversations/{id} returns HTML (the frontend app) instead of JSON.

Cause: This URL pattern is handled by the frontend router, not the API.

Solution: Use the batch endpoint with the ids query parameter:

# ❌ Wrong - returns HTML
curl "${BASE_URL}/api/v1/app-conversations/${CONVERSATION_ID}" -H "Authorization: Bearer ${API_KEY}"

# ✅ Correct - returns JSON
curl "${BASE_URL}/api/v1/app-conversations?ids=${CONVERSATION_ID}" -H "Authorization: Bearer ${API_KEY}"

The Python client already handles this correctly via app_conversations_get_batch().

2. Using search endpoints

For listing or finding conversations, always use the /search endpoints:

  • GET /api/v1/app-conversations/search?limit=... — list recent conversations
  • GET /api/v1/app-conversations/start-tasks/search — find start-task status (alternative to batch)

3. Timing expectations

Operation Typical Duration
Conversation creation (POST) < 1 second
Start-task reaches READY 5-15 seconds (varies by load)
Sandbox fully running Usually immediate once READY

Polling recommendations:

  • Poll every 3-5 seconds when waiting for start-task
  • Set a reasonable timeout (e.g., 2-3 minutes)
  • The client's poll_start_task_until_ready() uses exponential backoff which is polite to the API

4. Getting agent server connection info

After a conversation is created, you need conversation_url and session_api_key to access agent server endpoints. These are available from:

  • The completed start-task response (agent_server_url + derive session key)
  • The app conversation record via GET /api/v1/app-conversations?ids=...
# Get connection info from conversation record
conv = api.app_conversation_get(app_conversation_id)
agent_server_url = conv.get("conversation_url", "").rsplit("/api/conversations", 1)[0]
session_api_key = conv.get("session_api_key")

Attribution

These troubleshooting tips are adapted from the documentation in jpshackelford/openhands-sample-plugins.

@enyst
Copy link
Collaborator Author

enyst commented Feb 27, 2026

@OpenHands Read #48 (comment) and #48 (comment)

and let's learn from them to improve this Skill. Analyze and investigate the proposed updates/fixes and read the linked skill at https://github.com/jpshackelford/.openhands/blob/main/skills/openhands-cloud-api/SKILL.md

Fix the skill in this PR.

@openhands-ai
Copy link

openhands-ai bot commented Feb 27, 2026

@enyst your session has expired. Please login again at OpenHands Cloud and try again.

@enyst
Copy link
Collaborator Author

enyst commented Feb 27, 2026

@OpenHands Read #48 (comment) and #48 (comment)

and let's learn from them to improve this Skill. Analyze and investigate the proposed updates/fixes and read the linked skill at https://github.com/jpshackelford/.openhands/blob/main/skills/openhands-cloud-api/SKILL.md

Fix the skill in this PR.

@openhands-ai
Copy link

openhands-ai bot commented Feb 27, 2026

@enyst your session has expired. Please login again at OpenHands Cloud and try again.

@enyst
Copy link
Collaborator Author

enyst commented Feb 27, 2026

@OpenHands Read #48 (comment) and #48 (comment)

and let's learn from them to improve this Skill. Analyze and investigate the proposed updates/fixes and read the linked skill at https://github.com/jpshackelford/.openhands/blob/main/skills/openhands-cloud-api/SKILL.md

Fix the skill in this PR.

@openhands-ai
Copy link

openhands-ai bot commented Feb 27, 2026

I'm on it! enyst can track my progress at all-hands.dev

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
@openhands-ai
Copy link

openhands-ai bot commented Feb 27, 2026

Final summary (new since my last summary)

  • Added a short CI/validation gotcha note to extensions/AGENTS.md: every skills/* directory must be listed in marketplaces/default.json, otherwise CI fails with “Skills missing from marketplace…”.
  • Pushed that change to the existing PR branch and confirmed GitHub Actions is green.

Completion checklist

  • Read and incorporated learnings from the two referenced PR comments and the linked external skill.
  • Fixed the skill in this PR (docs improvements + troubleshooting + event/debugging guidance were already included in the earlier commit).
  • Ensured CI passes (tests succeeded on the PR after the final push).
  • Kept changes concise (no extra files committed; removed the untracked uv.lock that appeared during local test runs).
  • Pushed updates to the same remote branch backing PR Add OpenHands Cloud API V1 skill and minimal Python client #48 (no new PR created).

Do **not** rely on the last event `id` to infer the total number of events.
In the agent-server API, event IDs are UUIDs (not monotonically increasing integers).

## Troubleshooting / common issues
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@OpenHands

  1. Read this entire Skill md file, and split Troubleshooting / common issues into a troubleshooting md file in this skill. Fetch agentskills.io to see the Agentskill format, we want to respect the format!
  2. Read all comments in this issue, and note that this troubleshooting comes from recent comments by jpshackelford; look up in this repo's main branch recent commits his commit signature (email, etc) and add authorship here too
  3. Also fix attribution as per the comments to this PR. Find a good place, probably at the end

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm on it! enyst can track my progress at all-hands.dev

Copy link

@openhands-ai openhands-ai bot Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Final Summary

I have successfully addressed all three items from the PR comment:

  1. Split Troubleshooting into separate file: Created skills/openhands-api-v1/references/TROUBLESHOOTING.md with the troubleshooting content from the original SKILL.md. Updated SKILL.md to reference this file, following the AgentSkills progressive disclosure format.

  2. Added jpshackelford attribution: Found John-Mason P. Shackelford's commit signature from the main branch and added proper attribution in both README.md and SKILL.md.

  3. Fixed attribution: Added the attribution section at the end of both files.

All changes have been committed and pushed to the remote branch openhands/openhands-api-v1-skill, which updates PR #48.

@enyst enyst force-pushed the openhands/openhands-api-v1-skill branch from bb754bd to b3cc237 Compare February 28, 2026 00:39
…ution

- Created references/TROUBLESHOOTING.md with common issues from jpshackelford's feedback
- Updated SKILL.md to reference the new troubleshooting file (progressive disclosure)
- Added attribution for John-Mason P. Shackelford in README.md and SKILL.md
@enyst enyst force-pushed the openhands/openhands-api-v1-skill branch from b3cc237 to c70b8b8 Compare February 28, 2026 00:43
Updated trigger names for the OpenHands API skill.
@enyst enyst requested a review from jpshackelford February 28, 2026 00:50
@enyst
Copy link
Collaborator Author

enyst commented Feb 28, 2026

@jpshackelford I added your troubleshooting tips.

I also used this lately - or a variant of it. I'd suggest we take this in, and use it, so we can improve it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants