codai docs
API referenceGateway

Sandboxes

Isolated gVisor workspaces where agents run Python, bash and LibreOffice and hand back files as scanned, signed artifacts.

A sandbox is an isolated container with a persistent /workspace directory. Your code or your agent runs commands in it (Python 3, bash, LibreOffice, Node 24, the usual data and document libraries, fonts with full Romanian coverage), writes results to /workspace/out and gets them back as artifacts: scanned, hashed and downloadable through a signed link. The workspace survives between execs until the sandbox is destroyed or expires.

Two backends share this API. v2 is the default since 18 October 2026: a long-lived pod with memory snapshots (pause/resume, fork, a persistent Python kernel, preview URLs), see Sandboxes v2. v1 (Cloud Run, the workspace restored per exec, plus POST /snapshot) stays available with backend: "v1". When you omit backend and v2 is not available for your key, the gateway falls back to v1; the backend field on every sandbox object says which one you got.

Every exec is metered; exec time is billed from the wallet afterwards (see Billing), so responses carry x-codai-cost-micro-usd: 0. Every route needs a codai_ key that reaches tool level l3_sandbox.

Security model

  • gVisor isolation. Every sandbox runs under gVisor with its own kernel boundary: v2 as a dedicated pod on GKE, v1 on Cloud Run (gen2). The image filesystem is read-only for your code and only /workspace is writable.
  • No network by default. With egress: { mode: 'none' } (the default) there is no route out at all: public IPs, private ranges and the metadata server are unreachable.
  • Egress through an allowlisting proxy. { mode: 'allowlist', domains: [...] } (at most 20 host names, no IP literals, no internal suffixes) or { mode: 'preset', preset: 'pypi' | 'npm' } lets HTTPS reach only those hosts, through the codai egress proxy, which resolves DNS itself and logs every connection. This works on both backends; the proxy opens plain tunnels and never injects credentials.
  • No secrets inside. No codai key, cloud credential or token is present in the sandbox; environment variables named CODAI_* or GOOGLE_* are rejected.
  • Audited. Every create, exec, file transfer, artifact, snapshot and destroy is written to an append-only audit log; each exec returns a reproducibility receipt.
  • Data stays in europe-west1 (x-codai-data-region).

Limits

LibreOffice converts every office format in the sandbox, for example soffice --headless --convert-to pdf --outdir /workspace/out report.docx (also .xlsx, .pptx, .odt, HTML and CSV). Every command runs under a small init that reaps background helper processes, and exit_code is the real exit status of your command.

LimitDefaultError
Running sandboxes per key3429 sandbox_concurrency_limit
Running v2 sandboxes per user5 (all keys together)429 sandbox_concurrency_limit
Paused v2 sandboxes per user10429 sandbox_paused_limit (on pause)
v2 platform capacityshared503 sandbox_capacity (retry later)
Concurrent execs per sandbox1409 sandbox_busy
Lifetime (ttl_seconds)1 h default, 24 h max400 invalid_ttl
Exec timeout (timeout_seconds)900 s400
Workspace size2 GBexec error event workspace_too_large
Exec seconds per user per day7 200429 sandbox_daily_limit
File upload100 MB413
Egress allowlist20 domains400
stdout / stderr per exec1 MB each, then a truncated event—

Artifacts

GET /v1/sandboxes/{id}/artifacts collects every file under /workspace/out. Each artifact carries:

  • mime, sniffed from the content (not from the file name);
  • sha256 of the bytes;
  • scan: the ClamAV verdict, clean, infected or error;
  • url: a signed download link valid for 1 h, present only when scan is clean (expires_at says when it stops working).

POST /v1/sandboxes/run returns the artifacts directly in its result.

Receipts

Every finished exec (the blocking result and the final exit stream event) includes a receipt: the image digest, the template, the SHA-256 of the inputs and of the command, every output file with its SHA-256 and size, and the workspace generation before and after. Store it next to the document you produced to prove what code made it, from which inputs.

Streaming

Send Accept: text/event-stream to POST /v1/sandboxes/{id}/exec to receive data: frames as the command runs: start, then any number of stdout / stderr chunks (and truncated when a stream hits 1 MB), then exactly one exit (or error). Without that header the call blocks and returns one JSON result.

Idempotency-Key on exec and run replays the stored response of an earlier request with the same key for 24 h instead of running the command again.

Quickstart: a PDF with Romanian diacritics

One shot with POST /v1/sandboxes/run: the sandbox is created, the script runs, /workspace/out is collected and the sandbox is destroyed.

curl https://ai.codai.ro/v1/sandboxes/run \
  -H "Authorization: Bearer $CODAI_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: act-2026-10-16-001" \
  -d @- <<'JSON'
{
  "language": "python",
  "timeout_seconds": 120,
  "code": "import os\nfrom reportlab.lib.pagesizes import A4\nfrom reportlab.pdfbase import pdfmetrics\nfrom reportlab.pdfbase.ttfonts import TTFont\nfrom reportlab.pdfgen import canvas\n\npdfmetrics.registerFont(TTFont('DejaVu', '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf'))\nos.makedirs('/workspace/out', exist_ok=True)\nc = canvas.Canvas('/workspace/out/act.pdf', pagesize=A4)\nc.setFont('DejaVu', 14)\nc.drawString(72, 770, 'Act adițional nr. 1 la contractul de prestări servicii')\nc.drawString(72, 745, 'Părțile convin: ședința se mută în București, str. Ștefan cel Mare.')\nc.save()\nprint('ok')\n"
}
JSON

The answer is an exec result whose artifacts[0].url downloads act.pdf for one hour. The DejaVu Sans font embeds the full ă â î ș ț set, so the comma-below ș and ț render correctly.

SDKs

import { Codai } from 'codai-sdk';

const client = new Codai({ apiKey: process.env.CODAI_API_KEY! });
const sb = await client.sandboxes.create({ ttl_seconds: 1800 });
await client.sandboxes.files.put(sb.id, 'in/date.csv', 'client,suma\nSC Exemplu SRL,1200\n');
for await (const ev of client.sandboxes.execStream(sb.id, {
  shell: 'python3 -c "import pandas as pd; print(pd.read_csv(\'in/date.csv\'))"',
})) {
  if (ev.type === 'stdout') process.stdout.write(ev.data);
  if (ev.type === 'exit') console.log('exit', ev.exit_code, ev.receipt.command_sha256);
}
const { data } = await client.sandboxes.artifacts(sb.id);
await client.sandboxes.delete(sb.id);
from codai import Codai

client = Codai(api_key="...")
sb = client.sandboxes.create(ttl_seconds=1800)
client.sandboxes.files.put(sb["id"], "in/date.csv", "client,suma\nSC Exemplu SRL,1200\n")
for ev in client.sandboxes.exec_stream(sb["id"], language="python", code="print(open('in/date.csv').read())"):
    if ev["type"] == "stdout":
        print(ev["data"], end="")
result = client.sandboxes.run(shell="weasyprint in/page.html out/page.pdf",
                              inputs=[{"path": "page.html", "url": "https://example.com/page.html"}])
client.sandboxes.delete(sb["id"])

The codai_sandbox chat tool

Add { "type": "codai_sandbox" } to tools in POST /v1/chat/completions (optionally with egress and template) and the gateway does the rest server-side: it gives the model sandbox_bash, sandbox_python, sandbox_edit and sandbox_files, runs them in one sandbox per x-codai-session-id (lifetime 1 h, extended by every use) and, at the end, appends the artifacts to the final message as codai_sandbox.artifacts plus a markdown list with signed links. The key still needs tool level l3_sandbox.

curl https://ai.codai.ro/v1/chat/completions \
  -H "Authorization: Bearer $CODAI_API_KEY" \
  -H "Content-Type: application/json" \
  -H "x-codai-session-id: acte-firma-42" \
  -d '{"model":"codai","tools":[{"type":"codai_sandbox"}],
       "messages":[{"role":"user","content":"Fă-mi un PDF cu o hotărâre AGA pentru SC Exemplu SRL."}]}'

The built-in exec_python tool now runs on the same sandbox backend; it no longer executes code inside the gateway process.

Document skills (Claude Skills compatible)

Anthropic's pre-built Agent Skills (docx, xlsx, pdf, pptx) need a first-party container that is not available on the clouds behind codai, so codai provides them on its own sandbox, for every model. Two equivalent forms:

{ "tools": [{ "type": "codai_sandbox", "skills": ["docx", "pdf"] }] }
{ "container": { "skills": [{ "type": "anthropic", "skill_id": "xlsx", "version": "latest" }] } }

Both work on /v1/chat/completions and /v1/messages. They enable the codai_sandbox tool and add a fixed guidance block for the requested skills (python-docx, openpyxl, reportlab/pypdf, python-pptx, weasyprint), so the model produces real files in /workspace/out and you get them as signed links. Custom skills (type: "custom") and container reuse (container.id) answer 400; use x-codai-session-id to keep one sandbox per conversation.

MCP tools

The codai MCP server (https://ai.codai.ro/mcp) exposes sandbox_create, sandbox_list, sandbox_exec, sandbox_files (list / read / write / artifacts) and sandbox_kill, plus the v2 tools sandbox_run_code, sandbox_pause, sandbox_resume, sandbox_fork and sandbox_preview (see Sandboxes v2). They call the same routes with your key, so the tool level, the limits and the metering are the same as on the REST API. sandbox_files reads return at most 256 KB inline; take bigger outputs through op: "artifacts" (signed URL + SHA-256).

Billing

Since 17 October 2026 (00:00 UTC) exec time is charged from the wallet at €0.10 per exec-hour, with the first 10 exec-hours per user per UTC month free; usage before that date is never billed. Idle running sandboxes cost nothing. Usage is settled in 10-minute buckets (wallet_ledger source sandbox_charge). If a bucket cannot be paid, new creates and execs answer 402 insufficient_balance (with unpaid_micro_eur) until a top-up settles it.

This section prices the v1 backend. Sandboxes v2 (the default) are billed differently, see v2 billing.

Sandboxes v2

Generally available to every key since 18 October 2026 and the default backend when backend is omitted. The REST surface above is shared: a sandbox picks its backend once, at create, and backend (v1 or v2) comes back on every sandbox object. If v2 is not available for your key and you did not ask for it explicitly, the create falls back to v1; with an explicit backend: "v2" it fails instead.

A v2 sandbox is a long-lived, isolated machine instead of a workspace restored per exec: processes keep running between calls, and the whole state (memory, running processes, /workspace) can be snapshotted, paused, resumed and forked. On v2:

  • Egress works as on v1: none (default), a pypi / npm preset or an allowlist of up to 20 domains, through the codai egress proxy (plain tunnels, no credential injection). Forks inherit the parent's egress.
  • Toolchain: Python 3, bash, the document libraries and Node 24 with npm in every template.
  • inputs on exec and GET /artifacts (ClamAV-scanned, SHA-256, signed URLs) behave exactly as on v1.
  • v1 only: POST /v1/sandboxes/{id}/snapshot answers 409 not_supported_on_v2; use pause/fork instead.

v2 limits

LimitValueError
Running sandboxes per key3429 sandbox_concurrency_limit
Running v2 sandboxes per user5, across all your keys429 sandbox_concurrency_limit
Paused v2 sandboxes per user10429 sandbox_paused_limit on pause
Fork children per call5400 invalid_count
Platform capacityshared pool503 sandbox_capacity, retry later

503 sandbox_capacity is transient: retry with backoff, or create with backend: "v1".

Templates

templateWhat is inside
codai-baseThe default image: Python 3, bash, Node 24 + npm and the document libraries.
brivio-actecodai-base plus LibreOffice, prepared for office documents and started warm.
desktopA Linux desktop reachable through noVNC on port 6080 (open it with a preview URL).

When a template has a warm snapshot, the create answer carries restored: true.

Persistent kernel: run_code

POST /v1/sandboxes/{id}/run_code with { "code": "...", "timeout_seconds": 60 } runs a Python cell in one long-lived kernel. Globals survive between calls, across pause/resume and into forks. The answer is { stdout, stderr, result, error, duration_ms, execution_count, kernel_restarted }: result is the repr of the last expression (at most 64 KB), error is { name, value, traceback } for an uncaught exception. A cell past its timeout restarts the kernel (kernel_restarted: true, state lost). POST /v1/sandboxes/{id}/kernel/reset restarts it on purpose; files are kept.

Pause and resume

POST /v1/sandboxes/{id}/pause snapshots the sandbox and stops it; its status becomes paused. You can keep at most 10 paused v2 sandboxes (429 sandbox_paused_limit); delete old ones to free a place. Resuming counts against the running caps again. POST /v1/sandboxes/{id}/resume brings it back and answers the sandbox plus restored and ready_ms. restored: true means memory and processes came back exactly as they were; restored: false means a cold start from the saved workspace (files intact, kernel state lost). codai measures this inside the sandbox; it is never assumed.

Fork

POST /v1/sandboxes/{id}/fork with { "count": 1..5, "ttl_seconds"?: n } snapshots a running sandbox (it keeps running) and starts count copies from that snapshot. Each copy is a full sandbox with its own id, workspace and kernel state, and is billed like any other. The answer is { object: "list", parent_id, data: [{ id, restored, ready_ms, error? }] }: per copy, restored: false means it started cold and error means it failed and is not running. A fork may not take a key above 6 live sandboxes, nor you above 5 running v2 sandboxes (429 sandbox_concurrency_limit).

Preview URLs

POST /v1/sandboxes/{id}/preview with { "port": 3000, "ttl_seconds": 900 } answers { port, url, expires_at }: a signed HTTPS URL on *.sbx.codai.ro that forwards to that port inside the sandbox (a dev server, or noVNC on 6080 in desktop). Ports 1024–65535 except 8080; validity 60 s to 1 h (default 15 min). The URL needs no key: anyone holding it reaches the port until it expires, so share it only with the people who should see the app.

const sb = await client.sandboxes.create({
  template: 'codai-base',
  egress: { mode: 'preset', preset: 'pypi' },
}); // v2 by default
await client.sandboxes.runCode(sb.id, {
  code: 'import pandas as pd\ndf = pd.DataFrame({"a": [1, 2]})',
});
const r = await client.sandboxes.runCode(sb.id, { code: 'df["a"].sum()' }); // r.result === '3'
const { data } = await client.sandboxes.fork(sb.id, { count: 3 });
await client.sandboxes.pause(sb.id);
const back = await client.sandboxes.resume(sb.id); // back.restored, back.ready_ms
sb = client.sandboxes.create(backend="v2", template="desktop")
p = client.sandboxes.preview(sb["id"], 6080, ttl_seconds=600)
print(p["url"], p["expires_at"])

v2 billing

  • Running: €0.10 per wall-clock hour while the sandbox exists (busy or idle), with 5 free hours per user per UTC month (separate from the v1 exec-hours above).
  • Paused: only the snapshot is stored, at €0.05 per GB-month, metered hourly.
  • Charges are settled from the wallet like v1 (402 insufficient_balance when a bucket cannot be paid).
POST
/v1/sandboxes

Authorization

bearerAuth
headerAuthorizationBearer <codai_…>

A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).

Header Parameters

x-request-id?string

Client correlation id; echoed back as x-codai-trace-id and persisted on the usage row.

Lengthlength <= 128

Request Body

application/json
  1. body
backend?string

Execution backend: v2 (default: long-lived pod with memory snapshots, pause/resume, fork, persistent kernel, preview URLs, egress through the codai proxy, Node 24 + npm) or v1 (Cloud Run, workspace restored per exec, POST /snapshot). When omitted and v2 is not available for the key, the sandbox is created on v1; an explicit v2 fails instead.

Default"v2"
Value in"v1""v2"
template?string

Image template. v1: codai-base only. v2: codai-base, brivio-acte (office toolchain warmed up) or desktop (a Linux desktop over noVNC on port 6080).

Default"codai-base"
Value in"codai-base""brivio-acte""desktop"
ttl_seconds?integer

Lifetime; the sandbox expires afterwards (sliding for chat sessions). At most 86400 on v1 and 604800 on v2.

Range60 <= value <= 604800
Default3600
egress?||

Outbound network, on both backends. none (default) has no network at all; allowlist lets HTTPS reach at most 20 domains through the codai egress proxy (no IP literals, no internal hosts); preset opens a curated list (pypi, npm). The proxy opens plain tunnels and never injects credentials; v2 forks inherit the parent policy.

metadata?

Your own labels, returned as-is.

Response Body

The new sandbox.

application/json
  1. response
id*string
Formatuuid
object*"sandbox"
Value in"sandbox"
status*string
Value in"running""paused""stopped""expired""destroyed"
backend*string

Backend chosen at create time.

Value in"v1""v2"
restored?boolean

Only on create of a v2 sandbox: true when it started from a warm template snapshot.

template*string
egress*||

Outbound network, on both backends. none (default) has no network at all; allowlist lets HTTPS reach at most 20 domains through the codai egress proxy (no IP literals, no internal hosts); preset opens a curated list (pypi, npm). The proxy opens plain tunnels and never injects credentials; v2 forks inherit the parent policy.

created_at*string
Formatdate-time
expires_at*string
Formatdate-time
last_exec_at*|
Formatdate-time
metadata*
usage*
Example Request
curl -X POST 'https://ai.codai.ro/v1/sandboxes' \  -H 'Content-Type: application/json' \  -d '{}'
Example Response
{  "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",  "object": "sandbox",  "status": "running",  "backend": "v1",  "restored": true,  "template": "string",  "egress": {    "mode": "none"  },  "created_at": "2019-08-24T14:15:22Z",  "expires_at": "2019-08-24T14:15:22Z",  "last_exec_at": "2019-08-24T14:15:22Z",  "metadata": {    "property1": "string",    "property2": "string"  },  "usage": {    "exec_count": 0,    "exec_seconds": 0  }}
GET
/v1/sandboxes

Authorization

bearerAuth
headerAuthorizationBearer <codai_…>

A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).

Query Parameters

status?string

Only sandboxes in this status.

Value in"running""paused""stopped""expired""destroyed"

Header Parameters

x-request-id?string

Client correlation id; echoed back as x-codai-trace-id and persisted on the usage row.

Lengthlength <= 128

Response Body

Your sandboxes.

application/json
  1. response
object?"list"
Value in"list"
data*array<>
Example Request
curl -X GET 'https://ai.codai.ro/v1/sandboxes'
Example Response
{  "object": "list",  "data": [    {      "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",      "object": "sandbox",      "status": "running",      "backend": "v1",      "restored": true,      "template": "string",      "egress": {        "mode": "none"      },      "created_at": "2019-08-24T14:15:22Z",      "expires_at": "2019-08-24T14:15:22Z",      "last_exec_at": "2019-08-24T14:15:22Z",      "metadata": {        "property1": "string",        "property2": "string"      },      "usage": {        "exec_count": 0,        "exec_seconds": 0      }    }  ]}
GET
/v1/sandboxes/{id}

Authorization

bearerAuth
headerAuthorizationBearer <codai_…>

A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).

Path Parameters

id*string

Sandbox id (uuid).

Formatuuid

Header Parameters

x-request-id?string

Client correlation id; echoed back as x-codai-trace-id and persisted on the usage row.

Lengthlength <= 128

Response Body

The sandbox.

application/json
  1. response
id*string
Formatuuid
object*"sandbox"
Value in"sandbox"
status*string
Value in"running""paused""stopped""expired""destroyed"
backend*string

Backend chosen at create time.

Value in"v1""v2"
restored?boolean

Only on create of a v2 sandbox: true when it started from a warm template snapshot.

template*string
egress*||

Outbound network, on both backends. none (default) has no network at all; allowlist lets HTTPS reach at most 20 domains through the codai egress proxy (no IP literals, no internal hosts); preset opens a curated list (pypi, npm). The proxy opens plain tunnels and never injects credentials; v2 forks inherit the parent policy.

created_at*string
Formatdate-time
expires_at*string
Formatdate-time
last_exec_at*|
Formatdate-time
metadata*
usage*
Example Request
curl -X GET 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08'
Example Response
{  "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",  "object": "sandbox",  "status": "running",  "backend": "v1",  "restored": true,  "template": "string",  "egress": {    "mode": "none"  },  "created_at": "2019-08-24T14:15:22Z",  "expires_at": "2019-08-24T14:15:22Z",  "last_exec_at": "2019-08-24T14:15:22Z",  "metadata": {    "property1": "string",    "property2": "string"  },  "usage": {    "exec_count": 0,    "exec_seconds": 0  }}
POST
/v1/sandboxes/{id}/exec

Authorization

bearerAuth
headerAuthorizationBearer <codai_…>

A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).

Path Parameters

id*string

Sandbox id (uuid).

Formatuuid

Header Parameters

x-request-id?string

Client correlation id; echoed back as x-codai-trace-id and persisted on the usage row.

Lengthlength <= 128
Idempotency-Key?string

Replays the stored response of an earlier request with the same key for 24 h instead of running it again.

Lengthlength <= 255

Request Body

application/json
  1. body

Exactly one of cmd, shell or language + code.

cmd?array<string>

Command as argv (no shell).

Itemsitems <= 256
shell?string

A shell line run with /bin/bash -lc.

Lengthlength <= 65536
language?string

Run code with this interpreter.

Value in"python""bash"
code?string

Source code for language.

Lengthlength <= 1048576
env?

Environment variables (at most 64; names starting with CODAI_ or GOOGLE_ are rejected).

cwd?string

Working directory inside /workspace.

Lengthlength <= 1024
timeout_seconds?integer

Wall-clock limit for this exec.

Range1 <= value <= 900
Default120
stdin?string

Standard input.

Lengthlength <= 1048576
inputs?array<>

Files written read-only under /workspace/in before the command runs.

Itemsitems <= 50

Response Body

JSON result, or an SSE stream when the request sent Accept: text/event-stream.

  1. response
exit_code*|

The command's own exit status; 128 + n when it was ended by signal n. null only when the exec timed out (timed_out: true) or the process could not be started.

stdout*string

Standard output (at most 1 MB).

stderr*string

Standard error (at most 1 MB).

timed_out*boolean
duration_ms*integer
artifacts?array<>

Present on /v1/sandboxes/run.

receipt*

What ran and what it produced: image digest, hashes of the inputs and the command, every output file with its SHA-256, and the workspace generations before and after.

Example Request
curl -X POST 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08/exec' \  -H 'Content-Type: application/json' \  -d '{}'
Example Response
{  "exit_code": 0,  "stdout": "string",  "stderr": "string",  "timed_out": true,  "duration_ms": 0,  "artifacts": [    {      "id": "string",      "path": "string",      "bytes": 0,      "mime": "string",      "sha256": "string",      "scan": "clean",      "url": "string",      "expires_at": "2019-08-24T14:15:22Z"    }  ],  "receipt": {    "image_digest": "string",    "template": "string",    "input_sha256": "string",    "command_sha256": "string",    "outputs": [      {        "path": "string",        "sha256": "string",        "bytes": 0      }    ],    "generation_before": 0,    "generation_after": 0  }}
PUT
/v1/sandboxes/{id}/files/{path}

Authorization

bearerAuth
headerAuthorizationBearer <codai_…>

A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).

Path Parameters

id*string

Sandbox id (uuid).

Formatuuid
path*string

File path relative to /workspace (slashes allowed, each segment URL-encoded).

Lengthlength <= 1024

Header Parameters

x-request-id?string

Client correlation id; echoed back as x-codai-trace-id and persisted on the usage row.

Lengthlength <= 128

Request Body

body*fileapplication/octet-stream
Formatbinary

Response Body

Written: size, SHA-256 and new generation.

application/json
  1. response
path*string
bytes*integer
sha256*string
generation*integer
Example Request
curl -X PUT 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08/files/string' \  -H 'Content-Type: application/octet-stream' \  -d 'string'
Example Response
{  "path": "string",  "bytes": 0,  "sha256": "string",  "generation": 0}
GET
/v1/sandboxes/{id}/files/{path}

Authorization

bearerAuth
headerAuthorizationBearer <codai_…>

A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).

Path Parameters

id*string

Sandbox id (uuid).

Formatuuid
path*string

File path relative to /workspace (slashes allowed, each segment URL-encoded).

Lengthlength <= 1024

Header Parameters

x-request-id?string

Client correlation id; echoed back as x-codai-trace-id and persisted on the usage row.

Lengthlength <= 128

Response Body

File content.

response?fileapplication/octet-stream
Formatbinary
Example Request
curl -X GET 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08/files/string'
Example Response
"string"
GET
/v1/sandboxes/{id}/files

Authorization

bearerAuth
headerAuthorizationBearer <codai_…>

A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).

Path Parameters

id*string

Sandbox id (uuid).

Formatuuid

Query Parameters

path?string

Directory relative to /workspace.

Lengthlength <= 1024

Header Parameters

x-request-id?string

Client correlation id; echoed back as x-codai-trace-id and persisted on the usage row.

Lengthlength <= 128

Response Body

Directory entries.

application/json
  1. response
entries*array<>
Example Request
curl -X GET 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08/files'
Example Response
{  "entries": [    {      "path": "string",      "bytes": 0,      "type": "file",      "mtime": "2019-08-24T14:15:22Z"    }  ]}
GET
/v1/sandboxes/{id}/artifacts

Authorization

bearerAuth
headerAuthorizationBearer <codai_…>

A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).

Path Parameters

id*string

Sandbox id (uuid).

Formatuuid

Header Parameters

x-request-id?string

Client correlation id; echoed back as x-codai-trace-id and persisted on the usage row.

Lengthlength <= 128

Response Body

The artifacts.

application/json
  1. response
data*array<>
Example Request
curl -X GET 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08/artifacts'
Example Response
{  "data": [    {      "id": "string",      "path": "string",      "bytes": 0,      "mime": "string",      "sha256": "string",      "scan": "clean",      "url": "string",      "expires_at": "2019-08-24T14:15:22Z"    }  ]}
POST
/v1/sandboxes/{id}/snapshot

Authorization

bearerAuth
headerAuthorizationBearer <codai_…>

A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).

Path Parameters

id*string

Sandbox id (uuid).

Formatuuid

Header Parameters

x-request-id?string

Client correlation id; echoed back as x-codai-trace-id and persisted on the usage row.

Lengthlength <= 128

Request Body

application/json
  1. body
name?string

Optional snapshot name.

Lengthlength <= 128

Response Body

The snapshot.

application/json
  1. response
object*string

Storage object name.

bytes*integer
sha256*string
Example Request
curl -X POST 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08/snapshot' \  -H 'Content-Type: application/json' \  -d '{}'
Example Response
{  "object": "string",  "bytes": 0,  "sha256": "string"}
DELETE
/v1/sandboxes/{id}

Authorization

bearerAuth
headerAuthorizationBearer <codai_…>

A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).

Path Parameters

id*string

Sandbox id (uuid).

Formatuuid

Header Parameters

x-request-id?string

Client correlation id; echoed back as x-codai-trace-id and persisted on the usage row.

Lengthlength <= 128

Response Body

Destroyed.

Example Request
curl -X DELETE 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08'
Example Response
Empty
POST
/v1/sandboxes/run

Authorization

bearerAuth
headerAuthorizationBearer <codai_…>

A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).

Header Parameters

x-request-id?string

Client correlation id; echoed back as x-codai-trace-id and persisted on the usage row.

Lengthlength <= 128
Idempotency-Key?string

Replays the stored response of an earlier request with the same key for 24 h instead of running it again.

Lengthlength <= 255

Request Body

application/json
  1. body

The fields of SandboxCreate plus those of SandboxExecRequest.

template?"codai-base"

Image template.

Default"codai-base"
Value in"codai-base"
ttl_seconds?integer

Lifetime; the sandbox expires afterwards (sliding for chat sessions).

Range60 <= value <= 86400
Default3600
egress?||

Outbound network, on both backends. none (default) has no network at all; allowlist lets HTTPS reach at most 20 domains through the codai egress proxy (no IP literals, no internal hosts); preset opens a curated list (pypi, npm). The proxy opens plain tunnels and never injects credentials; v2 forks inherit the parent policy.

metadata?

Your own labels, returned as-is.

cmd?array<string>

Command as argv (no shell).

Itemsitems <= 256
shell?string

A shell line run with /bin/bash -lc.

Lengthlength <= 65536
language?string

Run code with this interpreter.

Value in"python""bash"
code?string

Source code for language.

Lengthlength <= 1048576
env?

Environment variables (at most 64; names starting with CODAI_ or GOOGLE_ are rejected).

cwd?string

Working directory inside /workspace.

Lengthlength <= 1024
timeout_seconds?integer

Wall-clock limit for this exec.

Range1 <= value <= 900
Default120
stdin?string

Standard input.

Lengthlength <= 1048576
inputs?array<>

Files written read-only under /workspace/in before the command runs.

Itemsitems <= 50

Response Body

Exit code, output, artifacts and receipt.

application/json
  1. response
exit_code*|

The command's own exit status; 128 + n when it was ended by signal n. null only when the exec timed out (timed_out: true) or the process could not be started.

stdout*string

Standard output (at most 1 MB).

stderr*string

Standard error (at most 1 MB).

timed_out*boolean
duration_ms*integer
artifacts?array<>

Present on /v1/sandboxes/run.

receipt*

What ran and what it produced: image digest, hashes of the inputs and the command, every output file with its SHA-256, and the workspace generations before and after.

Example Request
curl -X POST 'https://ai.codai.ro/v1/sandboxes/run' \  -H 'Content-Type: application/json' \  -d '{}'
Example Response
{  "exit_code": 0,  "stdout": "string",  "stderr": "string",  "timed_out": true,  "duration_ms": 0,  "artifacts": [    {      "id": "string",      "path": "string",      "bytes": 0,      "mime": "string",      "sha256": "string",      "scan": "clean",      "url": "string",      "expires_at": "2019-08-24T14:15:22Z"    }  ],  "receipt": {    "image_digest": "string",    "template": "string",    "input_sha256": "string",    "command_sha256": "string",    "outputs": [      {        "path": "string",        "sha256": "string",        "bytes": 0      }    ],    "generation_before": 0,    "generation_after": 0  }}
POST
/v1/sandboxes/{id}/run_code

Authorization

bearerAuth
headerAuthorizationBearer <codai_…>

A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).

Path Parameters

id*string

Sandbox id (uuid).

Formatuuid

Header Parameters

x-request-id?string

Client correlation id; echoed back as x-codai-trace-id and persisted on the usage row.

Lengthlength <= 128

Request Body

application/json
  1. body
code*string

Python source of the cell.

Length1 <= length <= 1000000
timeout_seconds?integer

Seconds before the kernel is restarted (default 60, max 900).

Range1 <= value <= 900
Default60

Response Body

The cell result.

application/json
  1. response
stdout*string

Text printed to stdout during the cell.

stderr*string

Text printed to stderr during the cell.

result*|

repr of the last expression, or null.

error*|

The uncaught exception, or null.

duration_ms*integer

Wall-clock milliseconds.

execution_count*integer

Kernel execution counter.

kernel_restarted*boolean

True when the kernel was restarted (timeout or crash) and its state is lost.

Example Request
curl -X POST 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08/run_code' \  -H 'Content-Type: application/json' \  -d '{  "code": "string"}'
Example Response
{  "stdout": "string",  "stderr": "string",  "result": "string",  "error": {    "name": "string",    "value": "string",    "traceback": "string"  },  "duration_ms": 0,  "execution_count": 0,  "kernel_restarted": true}
POST
/v1/sandboxes/{id}/kernel/reset

Authorization

bearerAuth
headerAuthorizationBearer <codai_…>

A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).

Path Parameters

id*string

Sandbox id (uuid).

Formatuuid

Header Parameters

x-request-id?string

Client correlation id; echoed back as x-codai-trace-id and persisted on the usage row.

Lengthlength <= 128

Response Body

The kernel was restarted.

application/json
  1. response
ok*true
Value intrue
Example Request
curl -X POST 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08/kernel/reset'
Example Response
{  "ok": true}
POST
/v1/sandboxes/{id}/pause

Authorization

bearerAuth
headerAuthorizationBearer <codai_…>

A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).

Path Parameters

id*string

Sandbox id (uuid).

Formatuuid

Header Parameters

x-request-id?string

Client correlation id; echoed back as x-codai-trace-id and persisted on the usage row.

Lengthlength <= 128

Response Body

The paused sandbox.

application/json
  1. response
id*string
Formatuuid
object*"sandbox"
Value in"sandbox"
status*string
Value in"running""paused""stopped""expired""destroyed"
backend*string

Backend chosen at create time.

Value in"v1""v2"
restored?boolean

Only on create of a v2 sandbox: true when it started from a warm template snapshot.

template*string
egress*||

Outbound network, on both backends. none (default) has no network at all; allowlist lets HTTPS reach at most 20 domains through the codai egress proxy (no IP literals, no internal hosts); preset opens a curated list (pypi, npm). The proxy opens plain tunnels and never injects credentials; v2 forks inherit the parent policy.

created_at*string
Formatdate-time
expires_at*string
Formatdate-time
last_exec_at*|
Formatdate-time
metadata*
usage*
Example Request
curl -X POST 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08/pause'
Example Response
{  "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",  "object": "sandbox",  "status": "running",  "backend": "v1",  "restored": true,  "template": "string",  "egress": {    "mode": "none"  },  "created_at": "2019-08-24T14:15:22Z",  "expires_at": "2019-08-24T14:15:22Z",  "last_exec_at": "2019-08-24T14:15:22Z",  "metadata": {    "property1": "string",    "property2": "string"  },  "usage": {    "exec_count": 0,    "exec_seconds": 0  }}
POST
/v1/sandboxes/{id}/resume

Authorization

bearerAuth
headerAuthorizationBearer <codai_…>

A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).

Path Parameters

id*string

Sandbox id (uuid).

Formatuuid

Header Parameters

x-request-id?string

Client correlation id; echoed back as x-codai-trace-id and persisted on the usage row.

Lengthlength <= 128

Response Body

The resumed sandbox.

application/json
  1. response
id*string
Formatuuid
object*"sandbox"
Value in"sandbox"
status*string
Value in"running""paused""stopped""expired""destroyed"
backend*string

Backend chosen at create time.

Value in"v1""v2"
restored*boolean

True when the memory state was restored, false on a cold start.

template*string
egress*||

Outbound network, on both backends. none (default) has no network at all; allowlist lets HTTPS reach at most 20 domains through the codai egress proxy (no IP literals, no internal hosts); preset opens a curated list (pypi, npm). The proxy opens plain tunnels and never injects credentials; v2 forks inherit the parent policy.

created_at*string
Formatdate-time
expires_at*string
Formatdate-time
last_exec_at*|
Formatdate-time
metadata*
usage*
ready_ms*integer

Milliseconds until the sandbox answered.

Example Request
curl -X POST 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08/resume'
Example Response
{  "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",  "object": "sandbox",  "status": "running",  "backend": "v1",  "template": "string",  "egress": {    "mode": "none"  },  "created_at": "2019-08-24T14:15:22Z",  "expires_at": "2019-08-24T14:15:22Z",  "last_exec_at": "2019-08-24T14:15:22Z",  "metadata": {    "property1": "string",    "property2": "string"  },  "usage": {    "exec_count": 0,    "exec_seconds": 0  },  "restored": true,  "ready_ms": 0}
POST
/v1/sandboxes/{id}/fork

Authorization

bearerAuth
headerAuthorizationBearer <codai_…>

A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).

Path Parameters

id*string

Sandbox id (uuid).

Formatuuid

Header Parameters

x-request-id?string

Client correlation id; echoed back as x-codai-trace-id and persisted on the usage row.

Lengthlength <= 128

Request Body

application/json
  1. body
count*integer

How many children to start (1–5).

Range1 <= value <= 5
ttl_seconds?integer

Lifetime of each child in seconds (default: what is left of the parent).

Range60 <= value <= 604800

Response Body

The children.

application/json
  1. response
object*"list"
Value in"list"
parent_id*string
Formatuuid
data*array<>
Example Request
curl -X POST 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08/fork' \  -H 'Content-Type: application/json' \  -d '{  "count": 1}'
Example Response
{  "object": "list",  "parent_id": "1c6ca187-e61f-4301-8dcb-0e9749e89eef",  "data": [    {      "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",      "restored": true,      "ready_ms": 0,      "error": {        "code": "string",        "message": "string"      }    }  ]}
POST
/v1/sandboxes/{id}/preview

Authorization

bearerAuth
headerAuthorizationBearer <codai_…>

A codai API key (codai_ prefix). Ephemeral tokens from POST /v1/tokens are accepted only by the media surfaces matching their scope (/v1/realtime, /v1/audio/*, /v1/embeddings).

Path Parameters

id*string

Sandbox id (uuid).

Formatuuid

Header Parameters

x-request-id?string

Client correlation id; echoed back as x-codai-trace-id and persisted on the usage row.

Lengthlength <= 128

Request Body

application/json
  1. body
port*integer

Port inside the sandbox (1024–65535, not 8080).

Range1024 <= value <= 65535
ttl_seconds?integer

Validity in seconds (60–3600, default 900).

Range60 <= value <= 3600
Default900

Response Body

The signed preview URL.

application/json
  1. response
port*integer
url*string

Signed HTTPS URL; anyone holding it reaches the port until it expires.

Formaturi
expires_at*string
Formatdate-time
Example Request
curl -X POST 'https://ai.codai.ro/v1/sandboxes/497f6eca-6276-4993-bfeb-53cbbbba6f08/preview' \  -H 'Content-Type: application/json' \  -d '{  "port": 1024}'
Example Response
{  "port": 0,  "url": "http://example.com",  "expires_at": "2019-08-24T14:15:22Z"}

On this page