# Agents and automation

**Spinbox is experimental: APIs change and data may be lost.** What is built and what proves it is on [Status](https://spin-stack.github.io/website/reference/status.md); what Spinbox does not do is on [Limitations](https://spin-stack.github.io/website/overview/limitations.md).

> Give an agent or a CI job its own API key, let it create workspaces it cannot escape, and use snapshots, diffs and clones to review and undo its work.

Spinbox is built for code you do not fully trust, and an agent is the most common author of such
code. This page shows how to give an agent a workspace, how to limit what it can reach, and how
to review and undo what it did.

## In four calls

```bash
spin workspace create --name task-1 --wait -o json
spin exec task-1 -- make test
spin snapshot diff task-1 <after-id> --from <before-id> -o json
spin workspace delete task-1
```

spin snapshot diff task-1 <after-id> --from <before-id> -o json:

```json
{
  "changes": [
    {
      "path": "/etc/greeting",
      "pathBytes": "",
      "kind": "FILE_CHANGE_KIND_ADDED",
      "to": {
        "type": "FILE_TYPE_REGULAR",
        "mode": 420,
        "uid": 0,
        "gid": 0,
        "size": "6",
        "symlinkTarget": "",
        "symlinkTargetBytes": "",
        "sha256": "5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03",
        "modifiedAt": "2026-10-21T18:04:00Z"
      }
    },
    {
      "path": "/etc/hosts",
      "pathBytes": "",
      "kind": "FILE_CHANGE_KIND_MODIFIED",
      "from": {
        "type": "FILE_TYPE_REGULAR",
        "mode": 420,
        "uid": 0,
        "gid": 0,
        "size": "20",
        "symlinkTarget": "",
        "symlinkTargetBytes": "",
        "sha256": "081ef9d5367595d16e30b4b4549d9f43537320508b4ce0788963e10e4f808857",
        "modifiedAt": "2026-10-21T17:58:00Z"
      },
      "to": {
        "type": "FILE_TYPE_REGULAR",
        "mode": 420,
        "uid": 0,
        "gid": 0,
        "size": "29",
        "symlinkTarget": "",
        "symlinkTargetBytes": "",
        "sha256": "2c155a6a94b4ccf8996a7c0a185525d3a290ce344d76083b1d768e6409582f6e",
        "modifiedAt": "2026-10-21T18:04:00Z"
      }
    },
    {
      "path": "/etc/issue",
      "pathBytes": "",
      "kind": "FILE_CHANGE_KIND_REMOVED",
      "from": {
        "type": "FILE_TYPE_REGULAR",
        "mode": 420,
        "uid": 0,
        "gid": 0,
        "size": "26",
        "symlinkTarget": "",
        "symlinkTargetBytes": "",
        "sha256": "",
        "modifiedAt": "2026-10-21T17:58:00Z"
      }
    }
  ],
  "truncated": false
}
```

Every command prints JSON with `-o json`: the whole answer, each file's type, mode, size and hash
on both sides. A model reading this site starts at
[`/llms.txt`](https://spin-stack.github.io/website/llms.txt.md), and every page has a Markdown twin. The control plane's API is in
[the API reference](https://spin-stack.github.io/website/reference/api.md).

## A key for the agent

Create an API key with only the scopes the agent needs:

```bash
spin api-key create "review-bot" --scope workspace:read --scope workspace:write --expires 30d
```

Every scope, and what it allows, is in [`spin api-key`](https://spin-stack.github.io/website/reference/cli/api-key.md).

A key can never create another key, approve a login, or administer the installation. It lasts
at most a year. The secret is shown once.

Give it to the agent through the environment:

```bash
export SPIN_ENDPOINT=https://app.<domain>
export SPIN_TOKEN=<api-key>
spin -o json workspace list
```

The CLI needs no config file with these set. `-o json` makes every answer one JSON document.

## What the agent can reach

An agent that creates its own workspace usually names no network policy, so the workspace runs
under the installation's `default_network_tags`, and under `offline` if there are none. This is
where an administrator decides what an agent may reach: for example a policy that allows your
Git host and your package registry, and nothing else. See [Network and
egress](https://spin-stack.github.io/website/concepts/network.md).

Afterwards, `spin workspace egress <workspace>` shows what it reached and what it was refused.

The key the agent talks to its provider with need not be on the disk at all: see [An agent
without keys](https://spin-stack.github.io/website/use/agent-keys.md).

## Review, then keep or undo

The pattern that makes an agent's work safe to accept:

```bash
spin workspace create --name task-142 --from-snapshot <base-snapshot> --wait
spin snapshot create task-142 --description before --wait

# ... the agent works in task-142, with spin exec or over SSH ...

spin snapshot create task-142 --description after --wait
spin snapshot diff task-142 <after> --from <before> --under /srv/app
```

- Like the diff: keep the workspace, or take what it produced.
- Do not like it: stop the workspace and `spin workspace restore task-142 <before>`.
- Want to try again from the same point: create another workspace `--from-snapshot <before>`.

## Many attempts at once

Every workspace created from one snapshot is a clone of the same layers. Starting ten attempts
from the same state costs the disk of one, plus what each attempt writes. Each is its own
machine, with its own kernel, so no attempt sees another.

## Running commands

```bash
spin exec task-142 -w /srv/app -- make test
echo $?
```

`spin exec` streams output, forwards standard input, and exits with the command's exit status,
so it composes with the agent's own tools. It needs no network path to the workspace.

## The API

Everything the CLI does is a ConnectRPC call to the control plane, over HTTPS with the same key
(`Authorization: Bearer <api-key>`). ConnectRPC speaks plain JSON over HTTP POST, so any language
can call it. The services are `spin.v1.WorkspaceService`, `ShareService`, `SSHKeyService`,
`TeamService`, `SettingsService` and `LogService`.
