> For the complete documentation index, see [llms.txt](https://docs.orchard.airoverflow.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.orchard.airoverflow.com/reference/api.md).

# Python API (generated)

## Table of Contents

* [orchard\_sdk](#orchard_sdk)
* [orchard\_sdk.selector](#orchard_sdk.selector)
  * [resolve\_all](#orchard_sdk.selector.resolve_all)
  * [resolve](#orchard_sdk.selector.resolve)
* [orchard\_sdk.\_ws](#orchard_sdk._ws)
  * [ws\_url](#orchard_sdk._ws.ws_url)
  * [raw\_ws\_url](#orchard_sdk._ws.raw_ws_url)
  * [connect](#orchard_sdk._ws.connect)
  * [channel](#orchard_sdk._ws.channel)
* [orchard\_sdk.\_version](#orchard_sdk._version)
* [orchard\_sdk.\_http](#orchard_sdk._http)
  * [HttpClient](#orchard_sdk._http.HttpClient)
    * [get\_content](#orchard_sdk._http.HttpClient.get_content)
    * [post\_files](#orchard_sdk._http.HttpClient.post_files)
    * [download](#orchard_sdk._http.HttpClient.download)
* [orchard\_sdk.aio.channels](#orchard_sdk.aio.channels)
  * [AsyncChannels](#orchard_sdk.aio.channels.AsyncChannels)
    * [logs](#orchard_sdk.aio.channels.AsyncChannels.logs)
    * [shell](#orchard_sdk.aio.channels.AsyncChannels.shell)
    * [screen](#orchard_sdk.aio.channels.AsyncChannels.screen)
    * [adb](#orchard_sdk.aio.channels.AsyncChannels.adb)
    * [vnc](#orchard_sdk.aio.channels.AsyncChannels.vnc)
    * [mitm\_flows](#orchard_sdk.aio.channels.AsyncChannels.mitm_flows)
* [orchard\_sdk.aio](#orchard_sdk.aio)
* [orchard\_sdk.errors](#orchard_sdk.errors)
  * [OrchardError](#orchard_sdk.errors.OrchardError)
  * [ApiError](#orchard_sdk.errors.ApiError)
    * [user\_message](#orchard_sdk.errors.ApiError.user_message)
  * [SelectorError](#orchard_sdk.errors.SelectorError)
  * [ChannelClosedError](#orchard_sdk.errors.ChannelClosedError)
* [orchard\_sdk.models](#orchard_sdk.models)
  * [Instance](#orchard_sdk.models.Instance)
  * [ApiKey](#orchard_sdk.models.ApiKey)
* [orchard\_sdk.\_channels](#orchard_sdk._channels)
  * [SyncChannel](#orchard_sdk._channels.SyncChannel)
    * [send](#orchard_sdk._channels.SyncChannel.send)
    * [close](#orchard_sdk._channels.SyncChannel.close)
  * [Tunnel](#orchard_sdk._channels.Tunnel)
    * [run](#orchard_sdk._channels.Tunnel.run)
* [orchard\_sdk.config](#orchard_sdk.config)
  * [Config](#orchard_sdk.config.Config)
    * [api\_base](#orchard_sdk.config.Config.api_base)
    * [ws\_base](#orchard_sdk.config.Config.ws_base)
  * [from\_env](#orchard_sdk.config.from_env)
* [orchard\_sdk.client](#orchard_sdk.client)
  * [Orchard](#orchard_sdk.client.Orchard)
    * [from\_env](#orchard_sdk.client.Orchard.from_env)
    * [me](#orchard_sdk.client.Orchard.me)
    * [sandbox](#orchard_sdk.client.Orchard.sandbox)
    * [close](#orchard_sdk.client.Orchard.close)
* [orchard\_sdk.resources.remote](#orchard_sdk.resources.remote)
  * [Remote](#orchard_sdk.resources.remote.Remote)
    * [credentials](#orchard_sdk.resources.remote.Remote.credentials)
    * [add\_credential](#orchard_sdk.resources.remote.Remote.add_credential)
    * [delete\_credential](#orchard_sdk.resources.remote.Remote.delete_credential)
    * [catalog](#orchard_sdk.resources.remote.Remote.catalog)
    * [create](#orchard_sdk.resources.remote.Remote.create)
    * [get](#orchard_sdk.resources.remote.Remote.get)
    * [delete](#orchard_sdk.resources.remote.Remote.delete)
    * [start](#orchard_sdk.resources.remote.Remote.start)
    * [stop](#orchard_sdk.resources.remote.Remote.stop)
    * [upload](#orchard_sdk.resources.remote.Remote.upload)
    * [shell](#orchard_sdk.resources.remote.Remote.shell)
* [orchard\_sdk.resources.misc](#orchard_sdk.resources.misc)
  * [Snapshots](#orchard_sdk.resources.misc.Snapshots)
    * [list](#orchard_sdk.resources.misc.Snapshots.list)
  * [Templates](#orchard_sdk.resources.misc.Templates)
    * [list](#orchard_sdk.resources.misc.Templates.list)
    * [delete](#orchard_sdk.resources.misc.Templates.delete)
    * [launch](#orchard_sdk.resources.misc.Templates.launch)
  * [Catalog](#orchard_sdk.resources.misc.Catalog)
    * [android](#orchard_sdk.resources.misc.Catalog.android)
    * [presets](#orchard_sdk.resources.misc.Catalog.presets)
    * [sim\_device\_types](#orchard_sdk.resources.misc.Catalog.sim_device_types)
    * [providers](#orchard_sdk.resources.misc.Catalog.providers)
  * [Jobs](#orchard_sdk.resources.misc.Jobs)
    * [list](#orchard_sdk.resources.misc.Jobs.list)
    * [cancel](#orchard_sdk.resources.misc.Jobs.cancel)
    * [logs](#orchard_sdk.resources.misc.Jobs.logs)
  * [Nodes](#orchard_sdk.resources.misc.Nodes)
    * [list](#orchard_sdk.resources.misc.Nodes.list)
    * [create](#orchard_sdk.resources.misc.Nodes.create)
    * [test](#orchard_sdk.resources.misc.Nodes.test)
    * [refresh](#orchard_sdk.resources.misc.Nodes.refresh)
    * [delete](#orchard_sdk.resources.misc.Nodes.delete)
* [orchard\_sdk.resources](#orchard_sdk.resources)
* [orchard\_sdk.resources.keys](#orchard_sdk.resources.keys)
  * [Keys](#orchard_sdk.resources.keys.Keys)
    * [list](#orchard_sdk.resources.keys.Keys.list)
    * [create](#orchard_sdk.resources.keys.Keys.create)
    * [delete](#orchard_sdk.resources.keys.Keys.delete)
* [orchard\_sdk.resources.team](#orchard_sdk.resources.team)
  * [\_Members](#orchard_sdk.resources.team._Members)
    * [list](#orchard_sdk.resources.team._Members.list)
    * [edit](#orchard_sdk.resources.team._Members.edit)
    * [remove](#orchard_sdk.resources.team._Members.remove)
  * [\_Invites](#orchard_sdk.resources.team._Invites)
    * [list](#orchard_sdk.resources.team._Invites.list)
    * [create](#orchard_sdk.resources.team._Invites.create)
    * [delete](#orchard_sdk.resources.team._Invites.delete)
  * [Team](#orchard_sdk.resources.team.Team)
    * [get](#orchard_sdk.resources.team.Team.get)
    * [edit](#orchard_sdk.resources.team.Team.edit)
    * [invite\_info](#orchard_sdk.resources.team.Team.invite_info)
    * [accept\_invite](#orchard_sdk.resources.team.Team.accept_invite)
  * [\_Workspace](#orchard_sdk.resources.team._Workspace)
    * [edit](#orchard_sdk.resources.team._Workspace.edit)
    * [delete](#orchard_sdk.resources.team._Workspace.delete)
  * [Workspaces](#orchard_sdk.resources.team.Workspaces)
    * [list](#orchard_sdk.resources.team.Workspaces.list)
    * [create](#orchard_sdk.resources.team.Workspaces.create)
    * [get](#orchard_sdk.resources.team.Workspaces.get)
* [orchard\_sdk.resources.sandboxes](#orchard_sdk.resources.sandboxes)
  * [Sandboxes](#orchard_sdk.resources.sandboxes.Sandboxes)
    * [list](#orchard_sdk.resources.sandboxes.Sandboxes.list)
    * [create](#orchard_sdk.resources.sandboxes.Sandboxes.create)
    * [create\_sim](#orchard_sdk.resources.sandboxes.Sandboxes.create_sim)
    * [resolve](#orchard_sdk.resources.sandboxes.Sandboxes.resolve)
    * [resolve\_all](#orchard_sdk.resources.sandboxes.Sandboxes.resolve_all)
    * [get](#orchard_sdk.resources.sandboxes.Sandboxes.get)
  * [Sandbox](#orchard_sdk.resources.sandboxes.Sandbox)
    * [get](#orchard_sdk.resources.sandboxes.Sandbox.get)
    * [start](#orchard_sdk.resources.sandboxes.Sandbox.start)
    * [stop](#orchard_sdk.resources.sandboxes.Sandbox.stop)
    * [delete](#orchard_sdk.resources.sandboxes.Sandbox.delete)
    * [edit](#orchard_sdk.resources.sandboxes.Sandbox.edit)
    * [tag](#orchard_sdk.resources.sandboxes.Sandbox.tag)
    * [control](#orchard_sdk.resources.sandboxes.Sandbox.control)
    * [tap](#orchard_sdk.resources.sandboxes.Sandbox.tap)
    * [screenshot](#orchard_sdk.resources.sandboxes.Sandbox.screenshot)
    * [describe\_ui](#orchard_sdk.resources.sandboxes.Sandbox.describe_ui)
    * [metrics](#orchard_sdk.resources.sandboxes.Sandbox.metrics)
    * [storage](#orchard_sdk.resources.sandboxes.Sandbox.storage)
    * [logs](#orchard_sdk.resources.sandboxes.Sandbox.logs)
    * [shell](#orchard_sdk.resources.sandboxes.Sandbox.shell)
    * [screen](#orchard_sdk.resources.sandboxes.Sandbox.screen)
    * [adb](#orchard_sdk.resources.sandboxes.Sandbox.adb)
    * [vnc](#orchard_sdk.resources.sandboxes.Sandbox.vnc)
    * [console](#orchard_sdk.resources.sandboxes.Sandbox.console)
    * [mitm\_flows](#orchard_sdk.resources.sandboxes.Sandbox.mitm_flows)
    * [mitm\_ca](#orchard_sdk.resources.sandboxes.Sandbox.mitm_ca)
    * [tunnel](#orchard_sdk.resources.sandboxes.Sandbox.tunnel)
  * [Files](#orchard_sdk.resources.sandboxes.Files)
    * [list](#orchard_sdk.resources.sandboxes.Files.list)
    * [download](#orchard_sdk.resources.sandboxes.Files.download)
    * [upload](#orchard_sdk.resources.sandboxes.Files.upload)
  * [Apps](#orchard_sdk.resources.sandboxes.Apps)
    * [list](#orchard_sdk.resources.sandboxes.Apps.list)
    * [install](#orchard_sdk.resources.sandboxes.Apps.install)
    * [uninstall](#orchard_sdk.resources.sandboxes.Apps.uninstall)
    * [download](#orchard_sdk.resources.sandboxes.Apps.download)
    * [launch](#orchard_sdk.resources.sandboxes.Apps.launch)
  * [SandboxSnapshots](#orchard_sdk.resources.sandboxes.SandboxSnapshots)
    * [list](#orchard_sdk.resources.sandboxes.SandboxSnapshots.list)
    * [create](#orchard_sdk.resources.sandboxes.SandboxSnapshots.create)
    * [restore](#orchard_sdk.resources.sandboxes.SandboxSnapshots.restore)
    * [delete](#orchard_sdk.resources.sandboxes.SandboxSnapshots.delete)
    * [save\_as\_template](#orchard_sdk.resources.sandboxes.SandboxSnapshots.save_as_template)

## orchard\_sdk

Orchard SDK, a Python client for the Orchard mobile-sandbox control plane.

Quickstart::

```
from orchard_sdk import Orchard

orchard = Orchard.from_env()            # ORCHARD_API_URL + ORCHARD_API_KEY
for s in orchard.sandboxes.list()["instances"]:
    print(s["name"], s["state"])

sb = orchard.sandbox("my-pixel")        # by uuid / uuid-prefix / name / tag
sb.tag("payments", "`22c55e`")
with sb.logs() as stream:
    for line in stream:
        print(line)
```

See <https://docs.orchard.airoverflow.com> for guides and the full API reference.

## orchard\_sdk.selector

Resolve a human-friendly sandbox selector to a concrete instance.

Precedence (first non-empty tier wins): exact uuid, uuid prefix, exact name/display\_name, then tag. Only node sandboxes (those with a `uuid`) are candidates; remote cloud rows are addressed by `record_id` elsewhere.

**resolve\_all**

```python
def resolve_all(instances: list[Any], selector: str) -> list[dict[str, Any]]
```

Every sandbox the selector matches, by the first precedence tier that hits.

Tag selectors legitimately match many; single-target callers use :func:`resolve`, which rejects more than one.

**resolve**

```python
def resolve(instances: list[Any], selector: str) -> dict[str, Any]
```

Exactly one sandbox, or :class:`SelectorError` (zero or ambiguous match).

## orchard\_sdk.\_ws

Async WebSocket core: URL assembly + a decoded frame iterator.

A *channel* is one of Orchard's per-sandbox WebSocket streams (logs, shell, screen, adb, vnc, mitm, …). Frames arrive as text or binary depending on the channel; :func:`channel` decodes each frame with the caller-supplied `decode`. Used directly by :mod:`orchard_sdk.aio`; :mod:`orchard_sdk._channels` wraps it for synchronous callers.

**ws\_url**

```python
def ws_url(cfg: Config, uuid: str, suffix: str, **extra: Any) -> str
```

Build `wss?://host/ws/sandboxes/<uuid>/<suffix>?token=…` with auth + extras.

`extra` values that are `None` are omitted (e.g. `sid=None`).

**raw\_ws\_url**

```python
def raw_ws_url(cfg: Config, path: str, **extra: Any) -> str
```

Build a WS URL for a non-sandbox path (e.g. `ws/jobs/…`, `ws/remote/…`).

**connect**

```python
def connect(url: str)
```

Open a client connection with the raw-transport settings binary channels need.

**channel**

```python
async def channel(
        url: str,
        decode: Callable[[str | bytes],
                         Any] = decode_text) -> AsyncIterator[Any]
```

Async-iterate decoded frames from `url` until the socket closes cleanly.

Raises :class:`ChannelClosedError` if the server closes with an error code (4401/4403/4404/4502).

## orchard\_sdk.\_version

## orchard\_sdk.\_http

The sync REST transport: one httpx client that knows Orchard's base URL, auth header, tenancy headers, and how a server error becomes an :class:`ApiError`.

Internal, consumers use :class:`orchard_sdk.Orchard` and its resource namespaces, not this class directly.

### HttpClient Objects

```python
class HttpClient()
```

Thin synchronous wrapper over `httpx.Client` scoped to one Orchard org/workspace.

**get\_content**

```python
def get_content(path: str, **kw: Any) -> bytes
```

A GET whose body is raw bytes (screenshots, mitm CA).

**post\_files**

```python
def post_files(path: str,
               files: list[tuple[str, Path]],
               data: dict[str, Any] | None = None) -> Any
```

multipart POST (APK/IPA install, file upload). `files` is `[(field, Path)]`.

**download**

```python
def download(absolute_url: str, dest: Path) -> int
```

Stream a signed-grant URL (full URL incl. `?token=`) to a file.

The grant is itself the credential, so this request carries **no** Authorization header. Returns the number of bytes written.

## orchard\_sdk.aio.channels

Async WebSocket channels for asyncio callers.

The synchronous :class:`orchard_sdk.resources.sandboxes.Sandbox` channels wrap these behind a thread. If you are already in an event loop, use these directly::

```
import asyncio
from orchard_sdk import from_env
from orchard_sdk.aio import AsyncChannels

async def main():
    cfg = from_env()
    async for line in AsyncChannels(cfg, "<uuid>").logs():
        print(line)

asyncio.run(main())
```

### AsyncChannels Objects

```python
class AsyncChannels()
```

Async-iterable live channels for one sandbox uuid.

**logs**

```python
def logs() -> AsyncIterator[str]
```

Async-iterate log lines.

**shell**

```python
def shell() -> AsyncIterator[str]
```

Async-iterate shell output (read-only here; use the raw socket to send).

**screen**

```python
def screen(source: str = "scrcpy") -> AsyncIterator[bytes]
```

Async-iterate raw screen frames.

**adb**

```python
def adb() -> AsyncIterator[bytes]
```

Async-iterate raw adb transport bytes.

**vnc**

```python
def vnc() -> AsyncIterator[bytes]
```

Async-iterate raw RFB bytes (iOS VM).

**mitm\_flows**

```python
def mitm_flows() -> AsyncIterator[Any]
```

Async-iterate captured flow summaries (decoded JSON).

## orchard\_sdk.aio

Asyncio-native WebSocket channels. REST stays synchronous on :class:`orchard_sdk.Orchard`; this exposes the async channel iterators.

## orchard\_sdk.errors

Exceptions raised by the Orchard SDK.

### OrchardError Objects

```python
class OrchardError(Exception)
```

Base class for every error the SDK raises.

### ApiError Objects

```python
class ApiError(OrchardError)
```

A non-2xx REST response.

**Attributes**:

* `status` - The HTTP status code.
* `detail` - The server's `detail` message (or a best-effort body excerpt).

**user\_message**

```python
@property
def user_message() -> str
```

A human-friendly, actionable message for the terminal/logs.

### SelectorError Objects

```python
class SelectorError(OrchardError)
```

A sandbox selector matched zero or more than one sandbox.

### ChannelClosedError Objects

```python
class ChannelClosedError(OrchardError)
```

A WebSocket channel closed with an error code.

**Attributes**:

* `code` - The WebSocket close code (4401 unauth, 4403 forbidden, 4404 not found, 4502 upstream agent unreachable).
* `reason` - The server's close reason, if any.

## orchard\_sdk.models

Typed shapes for Orchard's wire JSON.

These mirror the TypeScript types in the web console (`frontend/lib/api.ts`); the backend is the single source of truth, so they are intentionally permissive `TypedDict`\s (no runtime validation), a response is returned as-is and merely annotated for editor/mypy help. Fields that may be absent use `NotRequired`.

### Instance Objects

```python
class Instance(TypedDict)
```

A sandbox as returned by `GET /api/instances` and `/api/sandboxes/<uuid>`.

### ApiKey Objects

```python
class ApiKey(TypedDict)
```

A row from `GET /api/tokens`. The raw secret is only present in the `POST` create response (shown once).

## orchard\_sdk.\_channels

Synchronous wrappers over the async WebSocket core.

:class:`SyncChannel` lets ordinary (non-asyncio) code consume a live channel as a plain iterator::

```
with sandbox.logs() as stream:
    for line in stream:
        print(line)
```

It runs a private asyncio loop on a daemon thread, pushes decoded frames onto a queue, and shuts the loop down cleanly when the `with` block exits, including on `break` or Ctrl-C. :class:`Tunnel` drives the reverse-tunnel control channel.

### SyncChannel Objects

```python
class SyncChannel()
```

A live WebSocket channel exposed as a context-managed, synchronous iterator.

**Arguments**:

* `url` - The fully-built channel URL (see :func:`orchard_sdk._ws.ws_url`).
* `decode` - Per-frame decoder (text/json/bytes). Defaults to text.

  The channel is lazy: the socket opens on `__enter__` and closes on `__exit__`. Iterating yields decoded frames until the stream ends or the block is left. `send()` pushes data upstream (e.g. shell keystrokes).

**send**

```python
def send(data: str | bytes) -> None
```

Send a frame upstream (e.g. a shell keystroke or a resize JSON).

**close**

```python
def close() -> None
```

Stop the stream and join the worker thread.

### Tunnel Objects

```python
class Tunnel()
```

The reverse-tunnel control channel: forwards guest proxy connections into a local proxy you run (Burp/mitmproxy). Context-managed; blocks in :meth:`run`.

**run**

```python
def run(on_event: Callable[[str, str], None] | None = None) -> int
```

Hold the tunnel open until the server closes it. `on_event(level, text)` receives `ready`/`open`/`warn`/`error`/`close` notifications.

## orchard\_sdk.config

Connection configuration, resolved from explicit arguments or the environment.

Primary environment variables:

* `ORCHARD_API_URL`, the control-plane URL, e.g. `https://orchard.example.com` (`ORCHARD_URL` is accepted as a legacy alias).
* `ORCHARD_API_KEY`, an Orchard API key (`orc_…`) or a JWT access token.
* `ORCHARD_ORG` / `ORCHARD_WS`, optional org / workspace slug overrides.

### Config Objects

```python
@dataclass
class Config()
```

Resolved connection settings shared by the REST and WebSocket transports.

**Arguments**:

* `url` - Control-plane base URL (scheme + host), e.g. `https://orchard.example.com`.
* `token` - API key (`orc_…`) or JWT access token sent as `Authorization: Bearer`.
* `org` - Optional organization slug (`X-Orchard-Org`). Ignored for API keys, which are bound to the org they were minted in.
* `ws` - Optional workspace slug (`X-Orchard-Workspace`).
* `timeout` - Default REST request timeout in seconds.

**api\_base**

```python
@property
def api_base() -> str
```

REST base URL, e.g. `https://host/api`.

**ws\_base**

```python
@property
def ws_base() -> str
```

WebSocket origin, e.g. `wss://host` (any path/query on `url` is dropped).

**from\_env**

```python
def from_env(url: str | None = None,
             token: str | None = None,
             org: str | None = None,
             ws: str | None = None,
             timeout: float = 60.0) -> Config
```

Build a :class:`Config`, letting explicit arguments win over environment variables. Raises :class:`ValueError` if neither a URL nor a token can be found.

> > > cfg = from\_env() # reads ORCHARD\_API\_URL + ORCHARD\_API\_KEY

## orchard\_sdk.client

The Orchard client facade, the one object users construct.

### Orchard Objects

```python
class Orchard()
```

A client for an Orchard control plane.

Construct it with a URL + API key, or :meth:`from_env` to read `ORCHARD_API_URL` / `ORCHARD_API_KEY` (+ optional `ORCHARD_ORG` / `ORCHARD_WS`).

> > > from orchard\_sdk import Orchard orchard = Orchard("<https://orchard.example.com>", "orc\_…") for s in orchard.sandboxes.list()\["instances"]: ... print(s\["name"], s\["state"]) orchard.sandbox("my-pixel").tag("payments", "`22c55e`")

The client holds an HTTP connection pool; use it as a context manager or call :meth:`close` when done.

**from\_env**

```python
@classmethod
def from_env(cls, *, timeout: float = 60.0) -> "Orchard"
```

Build a client from the `ORCHARD_*` environment variables.

**me**

```python
def me() -> dict[str, Any]
```

The authenticated identity: orgs, active org/workspace, features.

**sandbox**

```python
def sandbox(selector: str) -> Sandbox
```

Resolve a selector (uuid / uuid-prefix / name / tag) to a :class:`Sandbox` handle.

Raises :class:`orchard_sdk.SelectorError` on zero or ambiguous matches.

**close**

```python
def close() -> None
```

Close the underlying HTTP connection pool.

## orchard\_sdk.resources.remote

Remote (cloud) sandboxes and provider credentials, `orchard.remote`.

Remote sandboxes run on cloud providers (e2b/daytona/modal) rather than nodes, and are addressed by their numeric `record_id` (not a uuid).

### Remote Objects

```python
class Remote()
```

The `orchard.remote` namespace.

**credentials**

```python
def credentials() -> List[dict[str, Any]]
```

List stored provider credentials.

**add\_credential**

```python
def add_credential(provider: str,
                   key: str,
                   label: str | None = None,
                   config: dict[str, Any] | None = None) -> dict[str, Any]
```

Store (and verify) a provider API key.

**delete\_credential**

```python
def delete_credential(pk: int) -> None
```

Delete a stored credential.

**catalog**

```python
def catalog() -> dict[str, Any]
```

Which cloud providers are available/configured.

**create**

```python
def create(provider: str, name: str, **spec: Any) -> dict[str, Any]
```

Create a remote sandbox (`image`/`cpu`/`memory_mb`/`region`/`ttl_minutes`…).

**get**

```python
def get(pk: int) -> dict[str, Any]
```

Fetch a remote sandbox by record id.

**delete**

```python
def delete(pk: int) -> None
```

Terminate a remote sandbox.

**start**

```python
def start(pk: int) -> dict[str, Any]
```

Start a remote sandbox.

**stop**

```python
def stop(pk: int) -> dict[str, Any]
```

Stop a remote sandbox.

**upload**

```python
def upload(pk: int,
           local: str | Path,
           path: str | None = None) -> dict[str, Any]
```

Upload a file into a remote sandbox.

**shell**

```python
def shell(pk: int) -> SyncChannel
```

An interactive shell to a remote sandbox (`with remote.shell(pk) as s: ...`).

## orchard\_sdk.resources.misc

Smaller resource namespaces: snapshots, templates, catalog, jobs, nodes.

### Snapshots Objects

```python
class Snapshots()
```

`orchard.snapshots`, every point-in-time snapshot in the org/workspace.

**list**

```python
def list() -> List[SnapshotRow]
```

List all point snapshots visible to the caller.

### Templates Objects

```python
class Templates()
```

`orchard.templates`, reusable sandbox templates.

**list**

```python
def list() -> List[SnapshotRow]
```

List available templates.

**delete**

```python
def delete(pk: int) -> None
```

Delete a template by id.

**launch**

```python
def launch(pk: int, name: str, **spec: Any) -> dict[str, Any]
```

Launch a new sandbox from a template.

### Catalog Objects

```python
class Catalog()
```

`orchard.catalog`, what can be launched (images, presets, device types).

**android**

```python
def android() -> dict[str, Any]
```

Android system images and device profiles.

**presets**

```python
def presets() -> dict[str, Any]
```

iOS patch presets.

**sim\_device\_types**

```python
def sim_device_types() -> dict[str, Any]
```

iOS simulator device types and runtimes.

**providers**

```python
def providers() -> List[dict[str, Any]]
```

Per-node provider availability (platform admin only).

### Jobs Objects

```python
class Jobs()
```

`orchard.jobs`, background jobs (image downloads, vm creates).

**list**

```python
def list() -> List[Job]
```

List jobs visible to the caller.

**cancel**

```python
def cancel(node: str, job_id: str) -> dict[str, Any]
```

Cancel a running job.

**logs**

```python
def logs(node: str, kind: str, name: str) -> SyncChannel
```

Stream a job's log lines. `with jobs.logs(...) as s: for line in s: ...`

### Nodes Objects

```python
class Nodes()
```

`orchard.nodes`, infrastructure nodes (mostly platform-admin).

**list**

```python
def list() -> List[dict[str, Any]]
```

List placeable nodes.

**create**

```python
def create(**cfg: Any) -> dict[str, Any]
```

Enroll a node (`name`, `agent_url`, `agent_token`).

**test**

```python
def test(**cfg: Any) -> dict[str, Any]
```

Probe an agent URL+token before enrolling.

**refresh**

```python
def refresh(pk: int) -> dict[str, Any]
```

Re-poll one node.

**delete**

```python
def delete(pk: int) -> None
```

Remove a node.

## orchard\_sdk.resources

Resource namespaces for the Orchard client.

## orchard\_sdk.resources.keys

API keys, including fine-grained scoped keys.

### Keys Objects

```python
class Keys()
```

The `orchard.keys` namespace, manage API keys (`/api/tokens`).

**list**

```python
def list() -> List[ApiKey]
```

List the caller's API keys (metadata only; secrets are never returned).

**create**

```python
def create(name: str,
           *,
           sandbox_uuids: List[str] | None = None,
           tag: str | None = None,
           allowed_features: List[str] | None = None,
           expires_in_days: int | None = None) -> ApiKey
```

Mint an API key; the raw secret is in the response `token` field (shown once).

Scope it by passing any of `sandbox_uuids` (specific sandboxes), `tag` (every sandbox carrying that tag, now and future), and `allowed_features` (a capability subset). Omit all three for a full org-wide key.

> > > k = orchard.keys.create("ci", sandbox\_uuids=\[sb.uuid], ... allowed\_features=\["logs", "files"], expires\_in\_days=30) print(k\["token"]) # store it now

**delete**

```python
def delete(key_id: int) -> None
```

Revoke a key by id.

## orchard\_sdk.resources.team

Organization, members and invites (`orchard.team`), and workspaces (`orchard.workspaces`).

### \_Members Objects

```python
class _Members()
```

**list**

```python
def list() -> List[dict[str, Any]]
```

List members.

**edit**

```python
def edit(pk: int, **fields: Any) -> dict[str, Any]
```

Change a member's `role`/`denied_features`/limits.

**remove**

```python
def remove(pk: int) -> None
```

Remove a member.

### \_Invites Objects

```python
class _Invites()
```

**list**

```python
def list() -> List[dict[str, Any]]
```

List pending invites.

**create**

```python
def create(role: str = "member") -> dict[str, Any]
```

Create an invite; the response `token` is the one-time join credential.

**delete**

```python
def delete(pk: int) -> None
```

Revoke an invite.

### Team Objects

```python
class Team()
```

`orchard.team`, the active organization, its members and invites.

**get**

```python
def get() -> dict[str, Any]
```

The org, its usage, features and member count.

**edit**

```python
def edit(**fields: Any) -> dict[str, Any]
```

Change org fields (e.g. `name`), org admin only.

**invite\_info**

```python
def invite_info(token: str) -> dict[str, Any]
```

Describe an invite token (unauthenticated-friendly).

**accept\_invite**

```python
def accept_invite(token: str, **account: Any) -> dict[str, Any]
```

Accept an invite (optionally creating an account with `username`/`password`/`email`).

### \_Workspace Objects

```python
class _Workspace()
```

**edit**

```python
def edit(**fields: Any) -> dict[str, Any]
```

Change workspace fields (e.g. `name`).

**delete**

```python
def delete() -> None
```

Delete the workspace (must be empty; cannot delete the default).

### Workspaces Objects

```python
class Workspaces()
```

`orchard.workspaces`, teams within the org.

**list**

```python
def list() -> List[dict[str, Any]]
```

List the caller's workspaces (via `/api/me`).

**create**

```python
def create(name: str, slug: str | None = None) -> dict[str, Any]
```

Create a workspace (org admin).

**get**

```python
def get(slug: str) -> "_Workspace"
```

A handle to one workspace by slug.

## orchard\_sdk.resources.sandboxes

Sandboxes: listing/creating them, and the :class:`Sandbox` handle that drives one sandbox (lifecycle, editing, files, apps, snapshots, and live channels).

### Sandboxes Objects

```python
class Sandboxes()
```

The `orchard.sandboxes` namespace: fleet-wide listing and creation.

**list**

```python
def list() -> InstanceList
```

List every sandbox visible to this key/org.

> > > for s in orchard.sandboxes.list()\["instances"]: ... print(s\["name"], s\["state"])

**create**

```python
def create(**spec: Any) -> dict[str, Any]
```

Create a sandbox. `spec` includes `platform` ("ios"/"android"), `name`, `cpu`, `memory_mb`, and platform fields (`image`/`device` for Android, `preset`/`variant` for iOS).

**create\_sim**

```python
def create_sim(**spec: Any) -> dict[str, Any]
```

Create an iOS simulator sandbox (`name`, `device_type`, `runtime`).

**resolve**

```python
def resolve(selector: str) -> Instance
```

Resolve a selector (uuid / uuid-prefix / name / tag) to one instance dict.

**resolve\_all**

```python
def resolve_all(selector: str) -> List[Instance]
```

Every instance a selector matches (tags match many).

**get**

```python
def get(uuid: str) -> "Sandbox"
```

A :class:`Sandbox` handle for a known uuid (no lookup).

### Sandbox Objects

```python
class Sandbox()
```

A handle to one sandbox, addressed by uuid. Returned by `orchard.sandbox(selector)` and `orchard.sandboxes.get(uuid)`.

**get**

```python
def get() -> Instance
```

Fetch the full current instance dict.

**start**

```python
def start() -> dict[str, Any]
```

Start (boot) the sandbox.

**stop**

```python
def stop() -> dict[str, Any]
```

Stop the sandbox.

**delete**

```python
def delete() -> dict[str, Any]
```

Terminate the sandbox (deletes its disk on the node).

**edit**

```python
def edit(*,
         notes: str | None = None,
         tag: str | None = None,
         tag_color: str | None = None,
         owner: str | None = None,
         cpu: int | None = None,
         memory_mb: int | None = None) -> Instance
```

Change metadata/size. `cpu`/`memory_mb` require the sandbox stopped; `owner` requires org-admin.

**tag**

```python
def tag(tag: str, color: str | None = None) -> Instance
```

Set the sandbox's tag (and optional `rrggbb` chip color).

**control**

```python
def control(action: str, **kw: Any) -> dict[str, Any]
```

Send a unified input action (`tap`/`swipe`/`key`/`text`/`launch`/`tap_text`).

**tap**

```python
def tap(x: int, y: int) -> dict[str, Any]
```

Tap at device coordinates.

**screenshot**

```python
def screenshot() -> bytes
```

A PNG/JPEG screenshot (raw bytes).

**describe\_ui**

```python
def describe_ui() -> dict[str, Any]
```

The accessibility/UI tree.

**metrics**

```python
def metrics() -> dict[str, Any]
```

Live CPU/memory/battery/uptime metrics.

**storage**

```python
def storage() -> dict[str, Any]
```

Disk-usage breakdown (apps/media/system/free).

**logs**

```python
def logs(*, follow: bool = True) -> SyncChannel
```

Stream logs. `with sb.logs() as s: for line in s: ...`

**shell**

```python
def shell() -> SyncChannel
```

An interactive shell stream (send keystrokes with `.send()`).

**screen**

```python
def screen(*, source: str = "scrcpy") -> SyncChannel
```

Raw screen frames (binary H.264/PNG). `source` is `host` or `scrcpy`.

**adb**

```python
def adb() -> SyncChannel
```

Raw adb transport bytes (bridge to a local port to `adb connect`).

**vnc**

```python
def vnc() -> SyncChannel
```

Raw RFB (VNC) bytes, iOS VM only.

**console**

```python
def console(*, process: str | None = None) -> SyncChannel
```

An lldb debugger console stream.

**mitm\_flows**

```python
def mitm_flows() -> SyncChannel
```

Captured HTTP(S) flow summaries (JSON frames).

**mitm\_ca**

```python
def mitm_ca() -> bytes
```

The mitmproxy CA certificate (PEM bytes).

**tunnel**

```python
def tunnel(to_host: str, to_port: int) -> Tunnel
```

A reverse tunnel forwarding guest proxy traffic into `to_host:to_port`.

### Files Objects

```python
class Files()
```

`sandbox.files`, list, download and upload files on the device.

**list**

```python
def list(path: str = "") -> dict[str, Any]
```

List a directory on the device (default: the platform's home).

**download**

```python
def download(path: str, dest: str | Path) -> int
```

Download a device file to `dest` via a short-lived signed grant. Returns bytes written.

**upload**

```python
def upload(local: str | Path, path: str | None = None) -> dict[str, Any]
```

Upload a local file into device directory `path`.

### Apps Objects

```python
class Apps()
```

`sandbox.apps`, list, install, uninstall and pull apps.

**list**

```python
def list() -> dict[str, Any]
```

List installed apps/packages.

**install**

```python
def install(*apks: str | Path) -> dict[str, Any]
```

Install an `.apk`/`.ipa`, pass several parts for a split-APK set.

**uninstall**

```python
def uninstall(package: str) -> dict[str, Any]
```

Uninstall a package by id.

**download**

```python
def download(package: str, dest: str | Path) -> int
```

Pull an installed app's APK(s) to `dest` via a signed grant.

**launch**

```python
def launch(package: str) -> dict[str, Any]
```

Launch an installed app by package id.

### SandboxSnapshots Objects

```python
class SandboxSnapshots()
```

`sandbox.snapshots`, point-in-time snapshots of this sandbox.

**list**

```python
def list() -> List[SnapshotRow]
```

List this sandbox's snapshots.

**create**

```python
def create(name: str) -> dict[str, Any]
```

Take a snapshot.

**restore**

```python
def restore(name: str) -> dict[str, Any]
```

Restore the sandbox to a snapshot.

**delete**

```python
def delete(name: str) -> dict[str, Any]
```

Delete a snapshot.

**save\_as\_template**

```python
def save_as_template(name: str,
                     *,
                     visibility: str = "private",
                     description: str = "",
                     snapshot: str | None = None) -> SnapshotRow
```

Save the sandbox (or one of its snapshots) as a reusable template.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.orchard.airoverflow.com/reference/api.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
