> ## Documentation Index
> Fetch the complete documentation index at: https://docs.beta.b3os.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Screenshot a page an agent built in a sandbox

The model writes an HTML page, a sandbox with no network serves it on a declared port, and render captures the sandbox's preview URL.

An agent that builds a page should look at it. Here the model writes one self-contained HTML file (an uptime page for
a fictional service), a sandbox serves it with `python3 -m http.server` on port 8080, and render captures it through
the sandbox's preview URL, in a browser frame on a background. The sandbox declares the port when it is created and
has `egress: deny`: serving a file needs no network. `POST /v1/sandboxes/{id}/ports/8080/url` mints a URL on
`usercontent.b3os.org` that works for 10 minutes, with no cookie or key; render loads it like any other page.

Verified 2026-11-01: this recipe's last run passed against `https://beta-api.b3os.org`, label `live-recorded` (recorded from production), from commit `771a9508`. The recipe harness runs it again, and a failed run takes this page out of the cookbook.

## Run it

**curl**

```bash
API=https://beta-api.b3os.org
AUTH="Authorization: Bearer $B3OS_API_KEY"

curl -s "$API/v1/infer/chat/completions" -H "$AUTH" -H "Content-Type: application/json" -d '{
  "model": "b3os/qwen3.5-9b", "temperature": 0, "max_tokens": 3500,
  "chat_template_kwargs": {"enable_thinking": false},
  "messages": [
    {"role": "system", "content": "You write one complete, self-contained HTML5 file: inline CSS, no JavaScript, no external URLs. Output only the HTML."},
    {"role": "user", "content": "An uptime page for a fictional service called Example Cloud with four components and their 90-day uptime."}]}' \
| jq -r '.choices[0].message.content' | sed '/^```/d' > index.html

SBX=$(curl -s "$API/v1/sandboxes" -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"template": "base", "timeout_s": 300, "ports": [{"port": 8080}], "egress": {"mode": "deny"}}' | jq -r .id)
curl -s -X PUT "$API/v1/sandboxes/$SBX/files?path=/workspace/site/index.html&mkdir=1" -H "$AUTH" \
  -H "Content-Type: application/octet-stream" --data-binary @index.html > /dev/null
curl -s "$API/v1/sandboxes/$SBX/exec" -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"cmd": "python3 -m http.server 8080 --bind 0.0.0.0 --directory /workspace/site", "background": true}' > /dev/null
sleep 1

PREVIEW=$(curl -s -X POST "$API/v1/sandboxes/$SBX/ports/8080/url" -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"ttl_s": 600}' | jq -r .url)
curl -s "$API/v1/render/screenshots" -H "$AUTH" -H "Content-Type: application/json" \
  -d "$(jq -n --arg url "$PREVIEW" '{url: $url, viewport: {width: 1280, height: 720},
        style: {background: "b3os-dark", frame: "browser-dark"}, format: "webp"}')" \
  | jq -r '.artifacts[0].url' | xargs curl -s -o preview.webp

curl -s -X DELETE "$API/v1/sandboxes/$SBX" -H "$AUTH" | jq .status
```
**TypeScript**

```ts
import { writeFileSync } from "node:fs";

const API = "https://beta-api.b3os.org";
const auth = { authorization: `Bearer ${process.env.B3OS_API_KEY}` };
const json = { ...auth, "content-type": "application/json" };
const post = async (path: string, body: unknown): Promise<any> =>
  (await fetch(`${API}${path}`, { method: "POST", headers: json, body: JSON.stringify(body) })).json();

const chat = await post("/v1/infer/chat/completions", {
  model: "b3os/qwen3.5-9b",
  temperature: 0,
  max_tokens: 3500,
  chat_template_kwargs: { enable_thinking: false },
  messages: [
    { role: "system", content: "You write one complete, self-contained HTML5 file: inline CSS, no JavaScript, no external URLs. Output only the HTML." },
    { role: "user", content: "An uptime page for a fictional service called Example Cloud with four components and their 90-day uptime." },
  ],
});
const html = chat.choices[0].message.content.replace(/^```\w*\n?|```$/gm, "");

const sbx = await post("/v1/sandboxes", { template: "base", timeout_s: 300, ports: [{ port: 8080 }], egress: { mode: "deny" } });
try {
  await fetch(`${API}/v1/sandboxes/${sbx.id}/files?path=/workspace/site/index.html&mkdir=1`, {
    method: "PUT",
    headers: { ...auth, "content-type": "application/octet-stream" },
    body: html,
  });
  await post(`/v1/sandboxes/${sbx.id}/exec`, {
    cmd: "python3 -m http.server 8080 --bind 0.0.0.0 --directory /workspace/site",
    background: true,
  });
  await new Promise((r) => setTimeout(r, 1000));
  const preview = await post(`/v1/sandboxes/${sbx.id}/ports/8080/url`, { ttl_s: 600 });
  const shot = await post("/v1/render/screenshots", {
    url: preview.url,
    viewport: { width: 1280, height: 720 },
    style: { background: "b3os-dark", frame: "browser-dark" },
    format: "webp",
  });
  writeFileSync("preview.webp", Buffer.from(await (await fetch(shot.artifacts[0].url)).arrayBuffer()));
} finally {
  await fetch(`${API}/v1/sandboxes/${sbx.id}`, { method: "DELETE", headers: auth });
}
```
**Python**

```python
import os
import re
import time
import requests

API = "https://beta-api.b3os.org"
s = requests.Session()
s.headers["Authorization"] = f"Bearer {os.environ['B3OS_API_KEY']}"

chat = s.post(f"{API}/v1/infer/chat/completions", json={
    "model": "b3os/qwen3.5-9b", "temperature": 0, "max_tokens": 3500,
    "chat_template_kwargs": {"enable_thinking": False},
    "messages": [
        {"role": "system", "content": "You write one complete, self-contained HTML5 file: inline CSS, no JavaScript, "
                                      "no external URLs. Output only the HTML."},
        {"role": "user", "content": "An uptime page for a fictional service called Example Cloud with four components "
                                    "and their 90-day uptime."},
    ],
}).json()
html = re.sub(r"^```\w*\n?|```$", "", chat["choices"][0]["message"]["content"], flags=re.M)

sbx = s.post(f"{API}/v1/sandboxes", json={
    "template": "base", "timeout_s": 300, "ports": [{"port": 8080}], "egress": {"mode": "deny"},
}).json()
try:
    s.put(f"{API}/v1/sandboxes/{sbx['id']}/files", params={"path": "/workspace/site/index.html", "mkdir": 1},
          data=html.encode(), headers={"Content-Type": "application/octet-stream"})
    s.post(f"{API}/v1/sandboxes/{sbx['id']}/exec", json={
        "cmd": "python3 -m http.server 8080 --bind 0.0.0.0 --directory /workspace/site", "background": True})
    time.sleep(1)
    preview = s.post(f"{API}/v1/sandboxes/{sbx['id']}/ports/8080/url", json={"ttl_s": 600}).json()
    shot = s.post(f"{API}/v1/render/screenshots", json={
        "url": preview["url"], "viewport": {"width": 1280, "height": 720},
        "style": {"background": "b3os-dark", "frame": "browser-dark"}, "format": "webp",
    }).json()
    with open("preview.webp", "wb") as f:
        f.write(requests.get(shot["artifacts"][0]["url"]).content)
finally:
    s.delete(f"{API}/v1/sandboxes/{sbx['id']}")
```
**MCP**

On the remote [B3OS MCP server](/sdks/mcp), ask your agent:

```text
Write a self-contained HTML uptime page for a fictional service called Example Cloud. Create a base sandbox with port
8080 declared and egress deny, write the page to /workspace/site/index.html, serve that folder with
`python3 -m http.server 8080` in the background, get a preview URL for port 8080, take a screenshot of it in a dark
browser frame, show it to me, then delete the sandbox.
```

The agent calls `sandbox_create`, `sandbox_write_file`, `sandbox_exec`, `sandbox_port_url`, `render_screenshot` and
`sandbox_delete`.

The page changes with the model's output, so the image below is the last green run's, drawn in production from a
page the model wrote in that run. A preview URL carries no credential: anyone who has it reaches the port until it
expires, so keep `ttl_s` short and the port private.

Owner: Docs. Products: Docs, Infer, Sandboxes, Render.

## Output of the last green run

**Command**

```bash
$ node run.mjs
# exit 0
```
**Outputs**

```json
{
  "model": "b3os/qwen3.5-9b",
  "html_bytes": 7133,
  "preview_host": "usercontent.b3os.org",
  "render_status": "succeeded",
  "image": "https://beta.b3os.org/gallery/recipes/sandbox-preview-screenshot/ec299f0f4cc288e0.webp",
  "image_bytes": 34686,
  "page_ms": 8824.7,
  "create_ms": 246.7,
  "serve_ms": 631.8,
  "preview_ms": 299.3,
  "render_ms": 924.4,
  "total_ms": 12886.6,
  "deleted": true
}
```

![Image 1 of 1 from the last green run of the recipe Screenshot a page an agent built in a sandbox](https://beta.b3os.org/gallery/recipes/sandbox-preview-screenshot/ec299f0f4cc288e0.webp)

The outputs this recipe publishes (its recipe.json publish.outputs), with workspace identifiers redacted. The run's stdout, stderr and other outputs are not published.

Captured 2026-11-01 from commit `771a9508`, label `live-recorded` (recorded from production). Case `recipes/sandbox-preview-screenshot`.