# API-based Sandbox

> Connect to hosted API-based agent server for fully managed infrastructure.

The API-sandboxed agent server demonstrates how to use `APIRemoteWorkspace` to connect to a [OpenHands runtime API service](https://runtime.all-hands.dev/). This eliminates the need to manage your own infrastructure, providing automatic scaling, monitoring, and secure sandboxed execution.

## Basic Example

<Note>
  This example is available on GitHub: [examples/02\_remote\_agent\_server/04\_convo\_with\_api\_sandboxed\_server.py](https://github.com/OpenHands/software-agent-sdk/blob/main/examples/02_remote_agent_server/04_convo_with_api_sandboxed_server.py)
</Note>

This example shows how to connect to a hosted runtime API for fully managed agent execution:

```python icon="python" expandable examples/02_remote_agent_server/04_convo_with_api_sandboxed_server.py theme={null}
"""Example: APIRemoteWorkspace with Dynamic Build.

This example demonstrates building an agent-server image on-the-fly from the SDK
codebase and launching it in a remote sandboxed environment via Runtime API.

Usage:
  uv run examples/24_remote_convo_with_api_sandboxed_server.py

Requirements:
  - LLM_API_KEY: API key for LLM access
  - RUNTIME_API_KEY: API key for runtime API access
"""

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 APIRemoteWorkspace


logger = get_logger(__name__)


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

llm = LLM(
    usage_id="agent",
    model=os.getenv("LLM_MODEL", "anthropic/claude-sonnet-4-5-20250929"),
    base_url=os.getenv("LLM_BASE_URL"),
    api_key=SecretStr(api_key),
)

runtime_api_key = os.getenv("RUNTIME_API_KEY")
if not runtime_api_key:
    logger.error("RUNTIME_API_KEY required")
    exit(1)


# If GITHUB_SHA is set (e.g. running in CI of a PR), use that to ensure consistency
# Otherwise, use the latest image from main
server_image_sha = os.getenv("GITHUB_SHA") or "main"
server_image = f"ghcr.io/openhands/agent-server:{server_image_sha[:7]}-python-amd64"
logger.info(f"Using server image: {server_image}")

with APIRemoteWorkspace(
    runtime_api_url=os.getenv("RUNTIME_API_URL", "https://runtime.eval.all-hands.dev"),
    runtime_api_key=runtime_api_key,
    server_image=server_image,
    image_pull_policy="Always",
) 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 sandboxed environment!' && 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()
```

```bash Running the Example theme={null}
export LLM_API_KEY="your-api-key"
# If using the OpenHands LLM proxy, set its base URL:
export LLM_BASE_URL="https://llm-proxy.eval.all-hands.dev"
export RUNTIME_API_KEY="your-runtime-api-key"
# Set the runtime API URL for the remote sandbox
export RUNTIME_API_URL="https://runtime.eval.all-hands.dev"
cd agent-sdk
uv run python examples/02_remote_agent_server/04_convo_with_api_sandboxed_server.py
```

## Key Concepts

### APIRemoteWorkspace

The `APIRemoteWorkspace` connects to a hosted runtime API service:

```python highlight={48-52} theme={null}
with APIRemoteWorkspace(
    runtime_api_url="https://runtime.eval.all-hands.dev",
    runtime_api_key=runtime_api_key,
    server_image="ghcr.io/openhands/agent-server:main-python",
) as workspace:
```

This workspace type:

* Connects to a remote runtime API service
* Automatically provisions sandboxed environments
* Manages container lifecycle through the API
* Handles all infrastructure concerns

### Runtime API Authentication

The example requires a runtime API key for authentication:

```python highlight={42-45} theme={null}
runtime_api_key = os.getenv("RUNTIME_API_KEY")
if not runtime_api_key:
    logger.error("RUNTIME_API_KEY required")
    exit(1)
```

This key authenticates your requests to the hosted runtime service.

### Pre-built Image Selection

You can specify which pre-built agent server image to use:

```python highlight={51} theme={null}
APIRemoteWorkspace(
    runtime_api_url="https://runtime.eval.all-hands.dev",
    runtime_api_key=runtime_api_key,
    server_image="ghcr.io/openhands/agent-server:main-python",
)
```

The runtime API will pull and run the specified image in a sandboxed environment.

### Workspace Testing

Just like with DockerWorkspace, you can test the workspace before running the agent:

```python highlight={61-64} theme={null}
result = workspace.execute_command(
    "echo 'Hello from sandboxed environment!' && pwd"
)
logger.info(f"Command completed: {result.exit_code}, {result.stdout}")
```

This verifies connectivity to the remote runtime and ensures the environment is ready.

### Automatic RemoteConversation

The conversation uses WebSocket communication with the remote server:

```python highlight={66-68} theme={null}
conversation = Conversation(
    agent=agent, workspace=workspace, callbacks=[event_callback], visualize=True
)
assert isinstance(conversation, RemoteConversation)
```

All agent execution happens on the remote runtime infrastructure.

## Next Steps

* **[Docker Sandboxed Server](/sdk/guides/agent-server/docker-sandbox)**
* **[Local Agent Server](/sdk/guides/agent-server/local-server)**
* **[Agent Server Overview](/sdk/guides/agent-server/overview)** - Architecture and implementation details
* **[Agent Server Package Architecture](/sdk/arch/agent-server)** - Remote execution architecture


---

> To find navigation and other pages in this documentation, fetch the llms.txt file at: https://docs.openhands.dev/llms.txt