---
title: Public API
description: Use the SDKs to launch remote sandboxes for agents, execute tools, transfer files, and collect results. Start with a complete example in the SDK tutorial, or the package guide for…
---

Use the SDKs to launch remote sandboxes for agents, execute tools, transfer files,
and collect results. Start with a complete example in the
[SDK tutorial](https://laithalsaadoon.github.io/microvms-agentd/learn/tutorial/from-code/), or the package guide for
[Rust](https://github.com/laithalsaadoon/microvms-agentd/blob/741287b92afc4c7dae9e4091166582a759791f76/microvms-core/README.md), [Python](https://github.com/laithalsaadoon/microvms-agentd/blob/741287b92afc4c7dae9e4091166582a759791f76/microvms-py/README.md),
or [Node/TypeScript](https://github.com/laithalsaadoon/microvms-agentd/blob/741287b92afc4c7dae9e4091166582a759791f76/microvms-js/README.md).

| Task | API |
| --- | --- |
| Launch a VM from an existing image | `Sandbox` and `RunRequest` |
| Execute a command or transfer files | `Session` |
| Stream, wait for, or cancel a command | `ExecHandle` |
| Run Claude Code or Codex with Bedrock access | `AgentVm` |
| Terminate and check cleanup | `TeardownOpts` and `TeardownReport` |
| Build images or call the control plane directly | `ControlPlane` and `CreateImageRequest` |

`microvms-core` is the Rust client. The Python `microvms` package and Node
`microvms` package expose bindings to it. Core re-exports `protocol`, the shared
wire types. This page is a selected API reference; see
[Rust API docs](https://docs.rs/microvms-core) for the complete Rust surface and
[CLI reference](cli.md) for `microvm` commands.

## Launch networking

Rust `RunMicrovmRequest` and `RunRequest` accept `egress_network_connectors`.
Python `run` accepts `egress_network_connectors=[arn]`; Node `run` accepts
`egressNetworkConnectors: [arn]`. These attach existing VPC connector ARNs;
creation and VPC configuration use the separate AWS Lambda core API.

Custom connectors conflict with the managed `egress` option. Supplying an ARN
does not certify isolation: no internet egress requires a VPC without an IGW
or NAT gateway and no alternative internet route. See [Networking](../internals/networking.md).

## microvms-core

### Error

```rs
#[derive(Debug, thiserror::Error)]
#[error("{message}")]
pub struct Error {
```

A failure classified once at the point it is raised, deliberately a struct with a private body rather than an enum, because an enum over every raise site would make each new failure a breaking change for a binding that matched exhaustively.

`microvms-core/src/error.rs:41-43`

### Region

```rs
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum Region {
```

An AWS region, closed over the five that run MicroVMs plus a named escape hatch, so a typo'd region is a compile error rather than an `AccessDeniedException` carrying a null message.

`microvms-core/src/region.rs:44-45`

### ErrorKind

```rs
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum ErrorKind {
```

The coarse failure classes, one per non-zero row of the CLI's exit table, with the integer exit code left to the CLI because a library owning process exit codes would be a library with an opinion about being a process.

`microvms-core/src/error.rs:126-127`

### SizeClass

```rs
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum SizeClass {
    Mib512,
    Mib1024,
    Mib2048,
    Mib4096,
    Mib8192,
}
```

The five documented size classes, named for the baseline a caller writes into `minimumMemoryInMiB` and deliberately not for the peak, since naming both would suggest the two are picked independently.

`microvms-core/src/sizing.rs:118-125`

### Session

```rs
pub struct Session {
```

The control API of one running MicroVM.

`microvms-core/src/session/mod.rs:196`

### ControlPlane

```rs
pub struct ControlPlane {
```

The control-plane client, holding its transport and clock behind `Arc` so a caller keeping one across tasks does not need a second credential chain.

`microvms-core/src/control/mod.rs:171`

### Sandbox

```rs
pub struct Sandbox {
```

One MicroVM's whole life: the state machine, the suspended window, and explicit teardown.

`microvms-core/src/sandbox.rs:548`

### RunRequest

```rs
let mut request = RunRequest::new().with_image(&image_arn);
request.execution_role_arn = Some(execution_role_arn);
let session = sandbox.run(request).await?;
```

Use `microvms_core::sandbox::RunRequest` to launch an image containing `agentd`.
Pass the actual image ARN returned by a build; the SDK does not perform the CLI's
friendly image-name lookup. Defaults are a ten-minute idle window, a ten-minute
suspended window, a one-hour maximum lifetime, and no auto-resume. Network
connector omission does not establish internet isolation; see launch networking
above. Wait for `session.wait_until_ready` before executing commands.

### TeardownOpts and TeardownReport

```rs
let report = sandbox.terminate(TeardownOpts::default()).await;
```

These types live in `microvms_core::sandbox`. Termination returns a report
instead of an error: inspect `failures` and `undeleted`. Defaults request VM
termination and retain the image and logs. Add `.waiting_for_terminated()` to the
options when the caller must observe the final state. `Sandbox` does not clean
up on drop, so call `terminate` on both success and failure paths; the
[Rust quickstart](https://github.com/laithalsaadoon/microvms-agentd/blob/741287b92afc4c7dae9e4091166582a759791f76/microvms-core/README.md) shows that pattern.

### AgentVm

```rs
pub struct AgentVm {
```

One VM with coding agents in it: the `Sandbox` plus the `AgentSpec`s it was built for, with `image_request`, `build`, `launch_request`, `launch`, `install_access`, `prompt`, and `terminate` as the L3 steps over the lifecycle. The free functions beside it (`image_request_for`, `launch_request_for`, `install_access`, `prompt`, `installed_agents`, `spec_for`) are the same steps for a caller holding the sandbox and the specs separately, which is how the bindings drive it; `agents::bedrock::mint` is the in-process Bedrock bearer token.

`microvms-core/src/agents/mod.rs`

### WireKind

```rs
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum WireKind {
```

The daemon-side failure classes the conformance suite asserts on, several of which collapse onto one `ErrorKind` at the exit code rather than at the raise site.

`microvms-core/src/error.rs:218-219`

### RunHookTimeout

```rs
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct RunHookTimeout(u32);
```

A timeout for the `run`, `resume`, `suspend`, or `terminate` hook, accepting 1..=60 seconds and offering no conversion from `BuildHookTimeout`.

`microvms-core/src/hooks.rs:47-48`

### Transport

```rs
pub struct Transport {
```

A backend, the agent token, and the proxy auth every request needs, kept separate from `Session` because `ExecHandle` needs it and holding a whole session would make the two mutually recursive.

`microvms-core/src/session/mod.rs:75`

### BuildHookTimeout

```rs
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct BuildHookTimeout(u32);
```

A timeout for the `ready` or `validate` image-build hook, accepting 1..=3600 seconds, and a distinct type so a build-sized value cannot reach a field that caps at 60.

`microvms-core/src/hooks.rs:53-54`

### EstimatedUsd

```rs
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct EstimatedUsd(Decimal);
```

Dollars derived from published rates and not the bill, with no `From<EstimatedUsd> for f64`, no `Into`, no `Deref`, and no `as_f64`, so laundering an estimate into a float does not compile.

`microvms-core/src/cost.rs:513-514`

### ExecHandle

```rs
pub struct ExecHandle {
```

One exec addressed by its caller-minted id, which is also the idempotency key, so rebuilding a handle with the same id after a process restart still addresses the same server-side exec.

`microvms-core/src/session/exec.rs:214`

### RateTable

```rs
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RateTable {
```

The five us-east-1 rates, held privately so that pricing compute from the ARM rate is a property of the type rather than of a code path a caller can bypass.

`microvms-core/src/cost.rs:838-839`

### CostReport

```rs
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CostReport {
```

Per-phase cost attribution for one sandbox, measured or projected, holding the rate table it was computed against so it stays reproducible after `pinned_rates` is updated.

`microvms-core/src/cost.rs:1467-1468`

### ExecResult

```rs
#[derive(Debug)]
pub struct ExecResult {
    pub exec_id: String,
    pub phase: protocol::exec::Phase,
    /// `None` while running. Present once the child has exited.
    pub outcome: Option<protocol::exec::Outcome>,
}
```

An exec's phase and, once it has one, its outcome — a thin wrapper over the daemon's `PollResponse` rather than a re-modelling of it, so the two cannot disagree.

`microvms-core/src/session/exec.rs:66-72`

### Image

```rs
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Image {
```

A built image, and the log group the service created alongside it.

`microvms-core/src/control/image.rs:58-59`

## protocol

### protocol::exec::Phase

```rs
#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum Phase {
```

An exec's phase on the wire, with schemars reading the same `#[serde(...)]` attributes serde does so the published schema describes what the daemon actually emits.

`protocol/src/exec.rs:22-24`

### protocol::health::Health

```rs
#[derive(Debug, Deserialize, JsonSchema, Serialize)]
pub struct Health {
```

The `GET /v1/health` response: daemon version, bootstrap state, disk pressure, whether startup identity repair degraded, and the exec-activity pair `busy` / `execs`.

`protocol/src/health.rs:10-11`

### protocol::exec::StartRequest

```rs
#[derive(Debug, Deserialize, JsonSchema, Serialize)]
pub struct StartRequest {
```

The `POST /v1/exec/start` body, whose `command` field is either an argv array or, with `shell: true`, a single script string.

`protocol/src/exec.rs:103-104`

### protocol::exec::Outcome

```rs
#[derive(Clone, Debug, Default, Deserialize, JsonSchema, Serialize)]
pub struct Outcome {
```

Captured output and exit status of a finished exec.

`protocol/src/exec.rs:57-58`

### protocol::exec::PollResponse

```rs
#[derive(Debug, Deserialize, JsonSchema, Serialize)]
pub struct PollResponse {
    pub exec_id: String,
    pub phase: Phase,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(flatten)]
    pub result: Option<Outcome>,
}
```

The `GET /v1/exec/{id}` body, which flattens the outcome into the response and omits it entirely while the exec is still running.

`protocol/src/exec.rs:280-287`

## microvms-py

The Python module is declared rather than assembled: a `#[pymodule] mod microvms` lists its members in `#[pymodule_export]` use statements, 30 classes and 12 functions (the agent layer's `AgentSpec`, `AgentVm`, and `BearerToken`, with `mint_bedrock_token`, `installed_agents`, `install_agent_access`, `prompt_agent`, and `agent_constants`, arrived with `docs/AGENT-VMS.md`), so the macro can see the whole membership and `maturin generate-stubs` emits the real surface instead of a `__getattr__` escape hatch (`microvms-py/src/lib.rs:110-136`). The exception hierarchy stays imperative in `#[pymodule_init]`, because `create_exception!` builds its types at runtime and leaves no introspection record for `#[pymodule_export]` to carry (`microvms-py/src/lib.rs:105-144`). Every method is sync, blocking on one shared multi-thread tokio runtime with `py.detach` first (`microvms-py/src/lib.rs:42-46`). The generated stub and its PEP 561 marker are committed as `microvms-py/microvms.pyi` and `microvms-py/py.typed`, and `mise run stubs:check` fails when the committed stub no longer matches the pyo3 surface (`mise.toml:216-218`).

### microvms-py Region

```rs
#[pyclass(frozen, from_py_object, name = "Region", module = "microvms")]
#[derive(Clone)]
pub struct PyRegion {
```

An AWS region, closed over the five that run MicroVMs plus a named escape hatch, exported to Python as `Region`.

`microvms-py/src/region.rs:30-32`

### microvms-py Sandbox

```rs
#[pyclass(frozen, name = "Sandbox", module = "microvms")]
pub struct PySandbox {
```

One MicroVM's whole life, with `build_image`, `run`, `suspend`, `resume`, and `terminate` as the five transitions and every state guard left in the core.

`microvms-py/src/sandbox.rs:294-295`

### microvms-py Session

```rs
#[pyclass(frozen, name = "Session", module = "microvms")]
pub struct PySession {
```

One running MicroVM's control API, with the proxy auth handled for you.

`microvms-py/src/session.rs:258-259`

### microvms-py EstimatedUsd

```rs
#[pyclass(
    frozen,
    skip_from_py_object,
    name = "EstimatedUsd",
    module = "microvms"
)]
#[derive(Clone, Copy)]
pub struct PyEstimatedUsd {
```

A dollar figure with no `__float__`, `__int__`, `__index__`, or `__add__`, whose `amount` answers a string, so `float(usd)` raises `TypeError` — the Python equivalent of the core's missing impl.

`microvms-py/src/cost.rs:169-176`

## microvms-js

The Node surface has no barrel: every `#[napi]` item in the crate is exported, and `index.d.ts` plus the `index.js` loader and the compiled `.node` addon are generated by `napi build` and excluded from the repository as one platform's build output (`.gitignore:27-29`). Two shapes appear side by side and mean different things: `#[napi]` on a struct is a JS class with methods, while `#[napi(object)]` is a copied plain object with no methods, which is how the same wire results that pyo3 renders as frozen classes arrive in Node (`microvms-js/src/exec.rs:65-66`, `microvms-js/src/session.rs:48-49`). Construction diverges from Python for a reason that is structural rather than stylistic: `PySandbox` has a `#[new]` constructor that blocks on the shared runtime (`microvms-py/src/sandbox.rs:344-350`), and a `#[napi(constructor)]` cannot be async, so the Node class is built through a static factory instead (`microvms-js/src/sandbox.rs:411-415`).

### microvms-js Region

```rs
#[napi]
#[derive(Clone)]
pub struct Region {
```

An AWS region, closed over the five that run MicroVMs plus a named escape hatch, taken as an instance rather than a string everywhere on this surface.

`microvms-js/src/region.rs:33-35`

### microvms-js Session

```rs
#[napi]
pub struct Session {
```

One running MicroVM's control API, with the proxy auth handled for you.

`microvms-js/src/session.rs:269-270`

### microvms-js Sandbox

```rs
#[napi]
pub struct Sandbox {
```

One MicroVM's whole life, with `buildImage`, `run`, `suspend`, `resume`, and `terminate` as the five transitions and every state guard left in the core.

`microvms-js/src/sandbox.rs:394-395`

### microvms-js ExecProcess

```rs
#[napi]
pub struct ExecProcess {
```

A long-running exec in the AI SDK's `SandboxProcess` shape, built by `Session.spawn` and never by a constructor, and the one entry on this surface with no peer in `microvms-py`.

`microvms-js/src/process.rs:192-193`

## HTTP

The daemon serves 20 routes. All of them come from one list, `surface_docs`, which `app` walks to build the router and `GET /v1/schema` walks to publish the document (`agentd/src/routes.rs:419-728`). A route cannot be served unless it appears in that list, and a listed route with no handler panics at startup rather than serving an undocumented surface (`agentd/src/routes.rs:110-142`). Each row also declares its auth, which is what splits the router in two: `Auth::Bearer` rows go behind the token guard, `Auth::Open` and `Auth::PlatformHook` rows do not (`agentd/src/routes.rs:51-59`).

The six lifecycle hooks sit under a prefix fixed by the service, `/aws/lambda-microvms/runtime/v1` (`protocol/src/hook.rs:15`). They are unauthenticated because the platform has no token to present, and a consumer must never call them.

### POST /aws/lambda-microvms/runtime/v1/ready

The image-build readiness probe, answering 200 even before bootstrap, because the question it answers is whether the daemon started.

`agentd/src/routes.rs:449-457`

### POST /aws/lambda-microvms/runtime/v1/resume

Acknowledged; the token, filesystem, exec records, and even backgrounded processes survive a suspend/resume cycle, but the guest's view of time jumps, so any timeout or lease held by a running command expires at once.

`agentd/src/routes.rs:497-506`

### POST /aws/lambda-microvms/runtime/v1/run

The one-shot token bootstrap and the optional launch environment beside it, both one JSON parse deeper than the request body inside `runHookPayload`, sharing the platform's 4096-byte payload budget.

`agentd/src/routes.rs:467-489`

### POST /aws/lambda-microvms/runtime/v1/suspend

Acknowledged and logged.

`agentd/src/routes.rs:490-496`

### POST /aws/lambda-microvms/runtime/v1/terminate

Acknowledged; begins graceful shutdown with in-flight requests draining.

`agentd/src/routes.rs:507-513`

### POST /aws/lambda-microvms/runtime/v1/validate

The image-build validation probe, on the same reasoning as `ready`.

`agentd/src/routes.rs:458-466`

### POST /v1/exec/start

Starts a command under a caller-minted `exec_id`, idempotent on that id, so a retry returns success without spawning a second child.

`agentd/src/routes.rs:514-525`

### GET `/v1/exec/{id}`

Polls status and output, read-only, so polling never mutates the entry and output survives until an explicit ack.

`agentd/src/routes.rs:526-536`

### POST `/v1/exec/{id}/ack`

Releases output and enters TTL collection; only acked entries are ever collected, so output nobody read is never destroyed.

`agentd/src/routes.rs:570-580`

### POST `/v1/exec/{id}/kill`

Sends SIGTERM then SIGKILL to the whole process group rather than the direct child alone, because a shell that backgrounded a server leaves the interesting process outside the child pid.

`agentd/src/routes.rs:581-592`

### POST `/v1/exec/{id}/stdin`

Writes to a child's stdin or signals EOF, a separate request from the output stream so a dropped attach does not cost the ability to feed the process.

`agentd/src/routes.rs:555-569`

### GET /v1/procs

Process accounting: every registered exec with its process group's live pids, read from `/proc` inside the guest so the image needs no `ps`. `child_exited: true` beside a non-empty `pids` is a command that finished while something it backgrounded did not; `reap` echoes the start request's `reap_group_on_exit`.

`agentd/src/routes.rs`

### GET /v1/tcp

A WebSocket relayed to `127.0.0.1:<port>` in the guest, loopback-only, one connection per socket, with the outcome carried in close codes because the route leaves HTTP behind after its 101.

`agentd/src/routes.rs`

### GET `/v1/exec/{id}/stream`

Follows output as Server-Sent Events from a byte offset, resumable with `?offset=N`; a body that ends without an `exit` event means the connection failed, not the command.

`agentd/src/routes.rs:537-554`

### GET /v1/fs/file

Reads one file, or a 1-based inclusive line range of it, always streamed — an `end_line` past the last line reads through EOF without error, and omitting both bounds returns the whole file byte-identically.

`agentd/src/routes.rs:638-653`

### PUT /v1/fs/file

Writes one file, deliberately not confined to a root, because the same token authorizes exec and a root prefix would add no security while breaking harnesses that write to home directories and `/etc`.

`agentd/src/routes.rs:654-666`

### GET /v1/fs/tar

Downloads a tree as tar, packing symlinks as symlinks, which is the producing half of what extraction accepts.

`agentd/src/routes.rs:667-678`

### PUT /v1/fs/tar

Uploads and extracts a tar under `?path=`, the one confined write path because member paths come from the archive rather than the caller, mirroring the CPython tarfile `data` filter.

`agentd/src/routes.rs:679-694`

### GET /v1/health

Reports liveness, daemon version, bootstrap completion, and whether any exec is still running; `busy` exists so an orchestrator outside the VM can hold it alive, since the platform measures idleness by inbound traffic through a proxy that terminates outside the guest.

`agentd/src/routes.rs:695-716`

### GET /v1/schema

Returns this document: every route, shape, status code, and operative limit.

`agentd/src/routes.rs:717-726`

## See also

* [contract map](../internals/insights/contract-map.md) — 22 shared source citations
* [impact analysis](../internals/insights/impact-analysis.md) — 21 shared source citations
* [business logic](../internals/insights/business-logic.md) — 13 shared source citations
* [system overview](../internals/architecture/system-overview.md) — 8 shared source citations
* [debugging guide](../internals/insights/debugging-guide.md) — 8 shared source citations