> ## 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.

# Product steps

Every product operation a routine step can call, with its with, outputs, effect class, kind and meter. Generated from the API spec.

A routine step calls a product operation with `uses: <product>/<step>@<major>`. Its `with` is the operation's request body plus one field for each path or query parameter, and its outputs are the response body, or, for a job, the finished job record. This page is generated from the API spec, the same source the node builds its table from. Steps run as the key that published the routine version, so that key needs each operation's scope.

**Kind.** A `sync` step makes one request. A `job` step creates a job, waits for it without holding a step slot, and outputs the finished job; canceling the run cancels the job. **Effect.** `read` steps retry under `defaults.retry`; `write` steps retry only when the step sets its own `retry` ([Steps](/routines/definitions#steps)). **Idempotency-Key.** Every product step sends `<run id>:<step id>`; the operations marked yes return the stored response to a repeat instead of running again ([FAQ](/routines/limits#faq)).

| Step | Kind | Effect | Operation | Meter | Dedupes on Idempotency-Key |
|---|---|---|---|---|---|
| `vision/scrape@1` | sync | read | [`POST /v1/vision/scrape`](/api/post-v1-vision-scrape) | `vision.page` | no |
| `vision/map@1` | sync | read | [`POST /v1/vision/map`](/api/post-v1-vision-map) | `vision.page` | no |
| `vision/crawl@1` | job | read | [`POST /v1/vision/crawls`](/api/post-v1-vision-crawls) | `vision.page` | yes |
| `vision/batch@1` | job | read | [`POST /v1/vision/batches`](/api/post-v1-vision-batches) | `vision.page` | yes |
| `vision/act@1` | sync | write | [`POST /v1/vision/sessions/{session_id}/act`](/api/post-v1-vision-sessions-session-id-act) | none | no |
| `vision/extract@1` | job | read | `POST /v1/vision/extracts` (not in the API reference yet) | `vision.page`, `infer.input_tokens`, `infer.output_tokens` | yes |
| `render/screenshot@1` | sync | read | [`POST /v1/render/screenshots`](/api/post-v1-render-screenshots) | `render.screenshot` | yes |
| `render/video@1` | job | read | [`POST /v1/render/videos`](/api/post-v1-render-videos) | `render.video_s` | yes |
| `render/restyle@1` | job | read | [`POST /v1/render/videos/{video_id}/restyle`](/api/post-v1-render-videos-video-id-restyle) | `render.video_s` | yes |
| `sandboxes/create@1` | sync | write | [`POST /v1/sandboxes`](/api/post-v1-sandboxes) | `sandboxes.vcpu_ms`, `sandboxes.gib_ms` | yes |
| `sandboxes/exec@1` | sync | write | [`POST /v1/sandboxes/{sandbox_id}/exec`](/api/post-v1-sandboxes-sandbox-id-exec) | none | yes |
| `sandboxes/read_file@1` | sync | read | [`GET /v1/sandboxes/{sandbox_id}/files`](/api/get-v1-sandboxes-sandbox-id-files) | none | no |
| `sandboxes/write_file@1` | sync | write | [`PUT /v1/sandboxes/{sandbox_id}/files`](/api/put-v1-sandboxes-sandbox-id-files) | none | no |
| `sandboxes/run_code@1` | sync | write | [`POST /v1/sandboxes/{sandbox_id}/run_code`](/api/post-v1-sandboxes-sandbox-id-run-code) | none | no |
| `sandboxes/snapshot@1` | sync | write | [`POST /v1/sandboxes/{sandbox_id}/snapshot`](/api/post-v1-sandboxes-sandbox-id-snapshot) | none | yes |
| `sandboxes/fork@1` | sync | write | [`POST /v1/sandboxes/{sandbox_id}/fork`](/api/post-v1-sandboxes-sandbox-id-fork) | `sandboxes.vcpu_ms`, `sandboxes.gib_ms` | yes |
| `sandboxes/delete@1` | sync | write | [`DELETE /v1/sandboxes/{sandbox_id}`](/api/delete-v1-sandboxes-sandbox-id-) | none | no |
| `infer/chat@1` | sync | read | [`POST /v1/infer/chat/completions`](/api/post-v1-infer-chat-completions) | `infer.input_tokens`, `infer.output_tokens` | no |
| `infer/speech@1` | sync | read | [`POST /v1/infer/audio/speech`](/api/post-v1-infer-audio-speech) | `infer.audio_chars` | no |

## Vision

What each operation does is on the [Vision](/vision) pages and in its API reference.

**`vision/scrape@1`** calls [`POST /v1/vision/scrape`](/api/post-v1-vision-scrape) (scope `vision:run`).

- `with`: the [request body](/api/post-v1-vision-scrape) (required: `url`).
- Outputs: the response body (`ScrapeResult`).

**`vision/map@1`** calls [`POST /v1/vision/map`](/api/post-v1-vision-map) (scope `vision:run`).

- `with`: the [request body](/api/post-v1-vision-map) (required: `url`).
- Outputs: the response body (`MapResult`).

**`vision/crawl@1`** calls [`POST /v1/vision/crawls`](/api/post-v1-vision-crawls) (scope `vision:run`).

- `with`: the [request body](/api/post-v1-vision-crawls) (required: `url`).
- Outputs: the finished job record, as `GET /v1/vision/crawls/{crawl_id}` returns it.

**`vision/batch@1`** calls [`POST /v1/vision/batches`](/api/post-v1-vision-batches) (scope `vision:run`).

- `with`: the [request body](/api/post-v1-vision-batches) (required: `urls`).
- Outputs: the finished job record, as `GET /v1/vision/batches/{batch_id}` returns it.

**`vision/act@1`** calls [`POST /v1/vision/sessions/{session_id}/act`](/api/post-v1-vision-sessions-session-id-act) (scope `vision:run`).

- `with`: the [request body](/api/post-v1-vision-sessions-session-id-act) (required: `actions`) and `session` for the path parameter `session_id` (optional).
- Outputs: the response body (`ActResult`).
- `session` is optional: without it the step acts in a browser session made for it and closed after it, also when it fails or times out, and `profile` loads a saved profile into that session.

**`vision/extract@1`** calls `POST /v1/vision/extracts` (not in the API reference yet) (scope `vision:run`).

- `with`: the request body (required: `urls`).
- Outputs: the finished job record, as `GET /v1/vision/extracts/{job_id}` returns it.

## Render

What each operation does is on the [Render](/render) pages and in its API reference.

**`render/screenshot@1`** calls [`POST /v1/render/screenshots`](/api/post-v1-render-screenshots) (scope `render:run`).

- `with`: the [request body](/api/post-v1-render-screenshots) (no field is required).
- Outputs: the response body (`Screenshot`).

**`render/video@1`** calls [`POST /v1/render/videos`](/api/post-v1-render-videos) (scope `render:run`).

- `with`: the [request body](/api/post-v1-render-videos) (required: `source` and `beats`).
- Outputs: the finished job record, as `GET /v1/render/videos/{video_id}` returns it.

**`render/restyle@1`** calls [`POST /v1/render/videos/{video_id}/restyle`](/api/post-v1-render-videos-video-id-restyle) (scope `render:run`).

- `with`: the [request body](/api/post-v1-render-videos-video-id-restyle) (no field is required) and `video` for the path parameter `video_id`.
- Outputs: the finished job record, as `GET /v1/render/videos/{video_id}` returns it.

## Sandboxes

What each operation does is on the [Sandboxes](/sandboxes) pages and in its API reference.

**`sandboxes/create@1`** calls [`POST /v1/sandboxes`](/api/post-v1-sandboxes) (scope `sandboxes:write`).

- `with`: the [request body](/api/post-v1-sandboxes) (no field is required).
- Outputs: the response body (`Sandbox`).

**`sandboxes/exec@1`** calls [`POST /v1/sandboxes/{sandbox_id}/exec`](/api/post-v1-sandboxes-sandbox-id-exec) (scope `sandboxes:run`).

- `with`: the [request body](/api/post-v1-sandboxes-sandbox-id-exec) (required: `cmd`) and `sandbox` for the path parameter `sandbox_id`.
- Outputs: the response body (`ExecResult`).
- Give `sandbox` to run in an existing sandbox, or `template` instead to run in a sandbox made for the step and deleted after it; `create` then takes the rest of that sandbox's create request. See [Steps](/routines/definitions#steps).

**`sandboxes/read_file@1`** calls [`GET /v1/sandboxes/{sandbox_id}/files`](/api/get-v1-sandboxes-sandbox-id-files) (scope `sandboxes:run`).

- `with`: `sandbox` for the path parameter `sandbox_id`, `path` for the query parameter `path` and `list` for the query parameter `list` (optional).
- Outputs: the response body (`FileEntryList`).
- Not runnable in a routine yet: the step fails with `501 not_implemented`, because steps call products in-process and that path carries JSON only on this build.

**`sandboxes/write_file@1`** calls [`PUT /v1/sandboxes/{sandbox_id}/files`](/api/put-v1-sandboxes-sandbox-id-files) (scope `sandboxes:run`).

- `with`: the bytes to send as the request body, `sandbox` for the path parameter `sandbox_id`, `path` for the query parameter `path`, `mode` for the query parameter `mode` (optional) and `mkdir` for the query parameter `mkdir` (optional).
- Outputs: the response body (`FileEntry`).
- Not runnable in a routine yet: the step fails with `501 not_implemented`, because steps call products in-process and that path carries JSON only on this build.

**`sandboxes/run_code@1`** calls [`POST /v1/sandboxes/{sandbox_id}/run_code`](/api/post-v1-sandboxes-sandbox-id-run-code) (scope `sandboxes:run`).

- `with`: the [request body](/api/post-v1-sandboxes-sandbox-id-run-code) (required: `code`) and `sandbox` for the path parameter `sandbox_id`.
- Outputs: the response body (`RunCodeResult`).

**`sandboxes/snapshot@1`** calls [`POST /v1/sandboxes/{sandbox_id}/snapshot`](/api/post-v1-sandboxes-sandbox-id-snapshot) (scope `sandboxes:write`).

- `with`: the [request body](/api/post-v1-sandboxes-sandbox-id-snapshot) (no field is required) and `sandbox` for the path parameter `sandbox_id`.
- Outputs: the response body (`Snapshot`).

**`sandboxes/fork@1`** calls [`POST /v1/sandboxes/{sandbox_id}/fork`](/api/post-v1-sandboxes-sandbox-id-fork) (scope `sandboxes:write`).

- `with`: the [request body](/api/post-v1-sandboxes-sandbox-id-fork) (no field is required) and `sandbox` for the path parameter `sandbox_id`.
- Outputs: the response body (`SandboxList`).

**`sandboxes/delete@1`** calls [`DELETE /v1/sandboxes/{sandbox_id}`](/api/delete-v1-sandboxes-sandbox-id-) (scope `sandboxes:write`).

- `with`: `sandbox` for the path parameter `sandbox_id`.
- Outputs: the response body (`Sandbox`).

## Infer

What each operation does is on the [Infer](/infer) pages and in its API reference.

**`infer/chat@1`** calls [`POST /v1/infer/chat/completions`](/api/post-v1-infer-chat-completions) (scope `infer:run`).

- `with`: the [request body](/api/post-v1-infer-chat-completions) (required: `model` and `messages`).
- Outputs: the response body (`ChatCompletion`).

**`infer/speech@1`** calls [`POST /v1/infer/audio/speech`](/api/post-v1-infer-audio-speech) (scope `infer:run`).

- `with`: the [request body](/api/post-v1-infer-audio-speech) (required: `model`, `input` and `voice`).
- Outputs: the response body (`SpeechWithTimestamps`).
- Not runnable in a routine yet: the step fails with `501 not_implemented`, because steps call products in-process and that path carries JSON only on this build.