Skip to main content
A ready-to-run example is available here!
The OpenHands Cloud provides fully managed sandbox environments for agent execution. There are two ways to use it:
  1. OpenHandsCloudWorkspace - SDK integration for running agents with your own LLM configuration
  2. Cloud API Direct - Launch one-off tasks that use your cloud-configured LLM and credentials
This guide covers both approaches.

Key Concepts

OpenHandsCloudWorkspace

The OpenHandsCloudWorkspace connects to OpenHands Cloud to provision sandboxes:
with OpenHandsCloudWorkspace(
    cloud_api_url="https://app.all-hands.dev",
    cloud_api_key=cloud_api_key,
) as workspace:
This workspace type:
  • Connects to OpenHands Cloud API
  • Automatically provisions sandboxed environments
  • Manages sandbox lifecycle (create, poll status, delete)
  • Handles all infrastructure concerns

Getting Your API Key

To use OpenHands Cloud, you need an API key. See Obtaining an API Key for detailed instructions. Store this key securely and use it as the OPENHANDS_CLOUD_API_KEY environment variable.

Configuration Options

The OpenHandsCloudWorkspace supports several configuration options:
ParameterTypeDefaultDescription
cloud_api_urlstrRequiredOpenHands Cloud API URL
cloud_api_keystrRequiredAPI key for authentication
sandbox_spec_idstr | NoneNoneCustom sandbox specification ID
init_timeoutfloat300.0Timeout for sandbox initialization (seconds)
api_timeoutfloat60.0Timeout for API requests (seconds)
keep_aliveboolFalseKeep sandbox running after cleanup

Keep Alive Mode

By default, the sandbox is deleted when the workspace is closed. To keep it running:
workspace = OpenHandsCloudWorkspace(
    cloud_api_url="https://app.all-hands.dev",
    cloud_api_key=cloud_api_key,
    keep_alive=True,
)
This is useful for debugging or when you want to inspect the sandbox state after execution.

Workspace Testing

You can test the workspace before running the agent:
result = workspace.execute_command(
    "echo 'Hello from OpenHands Cloud sandbox!' && pwd"
)
logger.info(f"Command completed: {result.exit_code}, {result.stdout}")
This verifies connectivity to the cloud sandbox and ensures the environment is ready.

Comparison with Other Workspace Types

FeatureOpenHandsCloudWorkspaceAPIRemoteWorkspaceDockerWorkspace
InfrastructureOpenHands CloudRuntime APILocal Docker
AuthenticationAPI KeyAPI KeyNone
Setup RequiredNoneRuntime API accessDocker installed
Custom ImagesVia sandbox specsDirect image specificationDirect image specification
Best ForProduction useCustom runtime environmentsLocal development

Ready-to-run Example

This example shows how to connect to OpenHands Cloud for fully managed agent execution:
examples/02_remote_agent_server/07_convo_with_cloud_workspace.py
"""Example: OpenHandsCloudWorkspace for OpenHands Cloud API.

This example demonstrates using OpenHandsCloudWorkspace to provision a sandbox
via OpenHands Cloud (app.all-hands.dev) and run an agent conversation.

Usage:
  uv run examples/02_remote_agent_server/06_convo_with_cloud_workspace.py

Requirements:
  - LLM_API_KEY: API key for direct LLM provider access (e.g., Anthropic API key)
  - OPENHANDS_CLOUD_API_KEY: API key for OpenHands Cloud access

Note:
  The LLM configuration is sent to the cloud sandbox, so you need an API key
  that works directly with the LLM provider (not a local proxy). If using
  Anthropic, set LLM_API_KEY to your Anthropic API key.
"""

import os
import time

from pydantic import SecretStr

from openhands.sdk import (
    LLM,
    Conversation,
    RemoteConversation,
    get_logger,
)
from openhands.tools.preset.default import get_default_agent
from openhands.workspace import OpenHandsCloudWorkspace


logger = get_logger(__name__)


api_key = os.getenv("LLM_API_KEY")
assert api_key, "LLM_API_KEY required"

# Note: Don't use a local proxy URL here - the cloud sandbox needs direct access
# to the LLM provider. Use None for base_url to let LiteLLM use the default
# provider endpoint, or specify the provider's direct URL.
llm = LLM(
    usage_id="agent",
    model=os.getenv("LLM_MODEL", "anthropic/claude-sonnet-4-5-20250929"),
    base_url=os.getenv("LLM_BASE_URL") or None,
    api_key=SecretStr(api_key),
)

cloud_api_key = os.getenv("OPENHANDS_CLOUD_API_KEY")
if not cloud_api_key:
    logger.error("OPENHANDS_CLOUD_API_KEY required")
    exit(1)

cloud_api_url = os.getenv("OPENHANDS_CLOUD_API_URL", "https://app.all-hands.dev")
logger.info(f"Using OpenHands Cloud API: {cloud_api_url}")

with OpenHandsCloudWorkspace(
    cloud_api_url=cloud_api_url,
    cloud_api_key=cloud_api_key,
) as workspace:
    agent = get_default_agent(llm=llm, cli_mode=True)
    received_events: list = []
    last_event_time = {"ts": time.time()}

    def event_callback(event) -> None:
        received_events.append(event)
        last_event_time["ts"] = time.time()

    result = workspace.execute_command(
        "echo 'Hello from OpenHands Cloud sandbox!' && pwd"
    )
    logger.info(f"Command completed: {result.exit_code}, {result.stdout}")

    conversation = Conversation(
        agent=agent, workspace=workspace, callbacks=[event_callback]
    )
    assert isinstance(conversation, RemoteConversation)

    try:
        conversation.send_message(
            "Read the current repo and write 3 facts about the project into FACTS.txt."
        )
        conversation.run()

        while time.time() - last_event_time["ts"] < 2.0:
            time.sleep(0.1)

        conversation.send_message("Great! Now delete that file.")
        conversation.run()
        cost = conversation.conversation_stats.get_combined_metrics().accumulated_cost
        print(f"EXAMPLE_COST: {cost}")
    finally:
        conversation.close()

    logger.info("✅ Conversation completed successfully.")
    logger.info(f"Total {len(received_events)} events received during conversation.")
Running the Example
export LLM_API_KEY="your-llm-api-key"
export OPENHANDS_CLOUD_API_KEY="your-cloud-api-key"
# Optional: specify a custom sandbox spec
# export OPENHANDS_SANDBOX_SPEC_ID="your-sandbox-spec-id"
cd agent-sdk
uv run python examples/02_remote_agent_server/07_convo_with_cloud_workspace.py

Launching One-Off Tasks via Cloud API

For scenarios where you want to launch a task and let it run asynchronously in the cloud (without waiting for completion), you can use the OpenHands Cloud API directly. This is particularly useful for:
  • CI/CD workflows - Trigger code reviews or automated tasks without blocking pipelines
  • Background processing - Launch long-running tasks and check results later
  • Minimal configuration - Only need an API key; LLM and GitHub credentials come from your cloud account

Key Benefits

AspectOpenHandsCloudWorkspaceCloud API Direct
LLM ConfigurationYou provide LLM_API_KEYUses your cloud-configured LLM
GitHub AccessYou provide GITHUB_TOKENUses your cloud-configured credentials
Execution ModelBlocking (waits for completion)Non-blocking (fire and forget)
Best ForInteractive scripts, testingCI/CD, automated workflows

How It Works

The Cloud API creates a conversation that:
  1. Uses your account’s configured LLM (no API key needed in the request)
  2. Has access to your GitHub credentials (if configured in your cloud account)
  3. Runs asynchronously - you get a conversation URL to track progress
  4. Can be viewed in the OpenHands Cloud UI

Example: PR Review Workflow

This example from the PR Review GitHub Action demonstrates launching a code review task via the Cloud API:
import json
import urllib.request

def start_cloud_conversation(
    cloud_api_url: str,
    cloud_api_key: str,
    initial_message: str,
) -> tuple[str, str]:
    """Start a conversation via OpenHands Cloud API.

    This creates a conversation directly through the Cloud API, which:
    - Uses the user's cloud-configured LLM (no LLM credentials needed)
    - Uses the user's cloud-configured GitHub credentials
    - Provisions a sandbox automatically
    - Returns a conversation URL for tracking in the UI

    Args:
        cloud_api_url: OpenHands Cloud API URL (e.g., https://app.all-hands.dev)
        cloud_api_key: API key for OpenHands Cloud
        initial_message: The initial prompt to send to the agent

    Returns:
        Tuple of (conversation_id, conversation_url)
    """
    url = f"{cloud_api_url}/api/conversations"

    payload = {"initial_user_msg": initial_message}

    data = json.dumps(payload).encode("utf-8")
    request = urllib.request.Request(url, data=data, method="POST")
    request.add_header("Authorization", f"Bearer {cloud_api_key}")
    request.add_header("Content-Type", "application/json")

    with urllib.request.urlopen(request, timeout=120) as response:
        result = json.loads(response.read().decode("utf-8"))

    conversation_id = result.get("conversation_id")
    conversation_url = f"{cloud_api_url}/conversations/{conversation_id}"
    return conversation_id, conversation_url


# Example usage for PR review
prompt = """
Review the PR and identify issues that need to be addressed.

## Pull Request Information
- **Repository**: OpenHands/software-agent-sdk
- **PR Number**: 1234
- **Title**: Add new feature

## Instructions
1. Fetch the PR diff using: gh pr diff 1234 --repo OpenHands/software-agent-sdk
2. Analyze the changes thoroughly
3. Post your review using the GitHub API (GITHUB_TOKEN is available)
"""

conversation_id, conversation_url = start_cloud_conversation(
    cloud_api_url="https://app.all-hands.dev",
    cloud_api_key="your-api-key",
    initial_message=prompt,
)

print(f"Review started: {conversation_url}")
# The workflow can exit immediately - review continues in the cloud

Using in GitHub Actions

The PR Review workflow demonstrates a complete implementation:
- name: Run PR Review (Cloud Mode)
  uses: OpenHands/software-agent-sdk/.github/actions/pr-review@main
  with:
      mode: cloud
      review-style: roasted
      # Only the cloud API key is needed - LLM and GitHub access
      # come from your OpenHands Cloud account configuration
      openhands-cloud-api-key: ${{ secrets.OPENHANDS_CLOUD_API_KEY }}
See the full PR Review example: examples/03_github_workflows/02_pr_review

When to Use Each Approach

Use CaseRecommended Approach
Interactive developmentOpenHandsCloudWorkspace
Unit tests with cloud sandboxOpenHandsCloudWorkspace
CI/CD code reviewsCloud API Direct
Background automationCloud API Direct
Need custom LLM configOpenHandsCloudWorkspace
Want simplest setupCloud API Direct

Next Steps