Native API
Call any provider endpoint the SDK doesn't wrap with box.native.request and box.native.fetch, use the official SDK on demand with box.native.sdk, and make account-level calls with provider.api.
box.native is the escape hatch for anything Sandbox SDK doesn’t wrap. What it holds depends on the provider:
- Providers that call their API directly need no package besides
@opencoredev/sandbox-sdk. Theirbox.nativeis aNativeApi: call any endpoint withrequestorfetch, or load the official SDK yourself withsdk. The factory also has anapiproperty for calls that aren’t about one sandbox. - Providers that wrap their SDK need that SDK installed, and their
box.nativeis the SDK’s own sandbox or client object.
Each provider page says which kind it is, lists its planes, and names its sandbox placeholder.
request(method, path, options?)
Calls an endpoint and resolves with its JSON body, or undefined when the body is empty, such as a 204. The call goes through the adapter’s base URL, credentials, retries, error mapping, and redaction, so you only write the path.
import type { NativeApi } from "@opencoredev/sandbox-sdk";
// Any provider's box.native or provider.api fits NativeApi.
export async function listWorkspace(native: NativeApi) {
return native.request<{ name: string; size: number }[]>("GET", "/sandboxes/{sandboxId}/files", {
query: { path: "/workspace" },
});
}
T is not checked at runtime. It describes the body the same way await response.json() would.
fetch(method, path, options?)
Calls an endpoint and resolves with the raw Response, for file bytes, logs, and other streams. A non-2xx response still rejects with SandboxError. You own the body: read it to the end, or cancel it with response.body?.cancel().
import type { NativeApi } from "@opencoredev/sandbox-sdk";
export async function printLogs(native: NativeApi, signal: AbortSignal) {
const response = await native.fetch("GET", "/sandboxes/{sandboxId}/logs", {
query: { follow: true },
signal,
});
const decoder = new TextDecoder();
for await (const chunk of response.body ?? [])
process.stdout.write(decoder.decode(chunk, { stream: true }));
}
Options
| Option | Type | Meaning |
|---|---|---|
plane |
provider-specific string | Which of the provider’s APIs to call, such as a control plane and a per-sandbox toolbox. Default: the control plane. |
params |
Record<string, string | number> |
Values for {name} placeholders in the path, URI-encoded. |
query |
Record<string, string | number | boolean | undefined> |
Query string values. undefined values are left out. |
body |
unknown |
Sent as is when it is a string, Uint8Array, ArrayBuffer, Blob, FormData, URLSearchParams, or ReadableStream. Anything else is sent as JSON. |
headers |
Record<string, string> |
Extra headers. |
signal |
AbortSignal |
Rejects with aborted. |
timeoutMs |
number |
request counts the whole call, body included. fetch counts until the response headers arrive. Default: the provider’s request timeout. |
Placeholders
On box.native, the provider’s sandbox placeholder, such as {sandboxId}, fills itself from the sandbox. Every other placeholder must be in params. A missing placeholder value, or a params entry with no placeholder, fails with invalid_argument before any request is sent. A value in params wins over the automatic one. On provider.api nothing is bound, so pass the sandbox ID in params too.
import type { NativeApi } from "@opencoredev/sandbox-sdk";
export async function previewUrl(native: NativeApi, port: number) {
return native.request<{ url: string }>("GET", "/sandboxes/{sandboxId}/ports/{port}/preview-url", {
params: { port },
});
}
Retries, errors, and credentials
- Retries. A
GET,HEAD,PUT, orDELETEis retried up to twice on 429, 502, 503, 504, and network errors, honouringRetry-Afterup to 20 seconds.POSTandPATCHare never retried, and neither is a body you pass as aReadableStream. - Errors. A failure rejects with
SandboxError, with the code mapped from the status: 400 and 422 areinvalid_argument, 401 and 403auth, 404not_found, 429quota, 5xxunavailable. APOSTthat may have reached the provider before it failed, such as a dropped connection or a timeout, isoutcome_unknown: check the provider’s state before you repeat it. Native errors keep the provider’s error code and message, with credentials and URLs removed. See Errors. - Credentials go only to the provider’s own hosts. A path that is a full URL on another host, or a redirect to one, is sent without them.
sdk(load)
Gives you the official SDK’s object for this sandbox, built from the module you import. The SDK is not a dependency of Sandbox SDK, so install it next to it first. Because you write the import(), your bundler sees it and the result is typed from the module you installed.
import { createSandbox } from "@opencoredev/sandbox-sdk";
import { daytona } from "@opencoredev/sandbox-sdk/daytona";
await using box = await createSandbox(daytona());
const sandbox = await box.native.sdk(() => import("@daytonaio/sdk"));
await sandbox.git.status("/workspace");
If the import fails because the package isn’t installed, sdk rejects with unavailable and the install command in hint. If the module doesn’t have the exports the provider needs, it rejects with invalid_argument.
provider.api
The provider factory has an api property: a NativeApi bound to no sandbox, for account-level calls such as listing snapshots or templates. provider.api.sdk(load) returns the SDK’s client instead of a sandbox.
import { daytona } from "@opencoredev/sandbox-sdk/daytona";
const provider = daytona();
const snapshots = await provider.api.request<{ items: { name: string }[] }>("GET", "/snapshots");