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

# CLI

> Provision and inspect sandboxes from a shell, a CI job, or a coding agent

`twinbay` wraps the same API the console uses. Source and per-command reference live in [panoratech/twinbay-cli](https://github.com/panoratech/twinbay-cli).

<Note>
  The CLI is pre-production. Pin a release in CI rather than tracking `latest`.
</Note>

## Install

<CodeGroup>
  ```bash macOS and Linux theme={null}
  curl -fsSL https://raw.githubusercontent.com/panoratech/twinbay-cli/main/scripts/install.sh | bash
  ```

  ```powershell Windows theme={null}
  iwr -useb https://raw.githubusercontent.com/panoratech/twinbay-cli/main/scripts/install.ps1 | iex
  ```

  ```bash Go theme={null}
  go install github.com/panoratech/twinbay-cli/cmd/twinbay@latest
  ```
</CodeGroup>

Binaries for each platform are on the [releases page](https://github.com/panoratech/twinbay-cli/releases). Check the install with `twinbay version`.

## Authenticate

The CLI sends an organization API key. Mint one in the console under **API keys**, or with `twinbay api-keys create --name ci`.

```bash theme={null}
twinbay auth login     # prompts, then stores the key in your OS keychain
twinbay whoami         # shows each setting and where it came from
twinbay auth logout    # clears stored credentials
```

Values resolve in this order:

1. Flags: `twinbay --organization-api-key <key> <command>`
2. Environment: `CLI_TWINBAY_ORGANIZATION_API_KEY`
3. The OS keychain, written by `twinbay auth login` or `twinbay configure`
4. `~/.config/twinbay/config.yaml`

macOS Keychain, GNOME Keyring or KWallet, and Windows Credential Locker hold the secret. Without a keychain the CLI falls back to the config file. In CI, set the environment variable from your secret store. `twinbay configure` sets credentials and global preferences together, and `twinbay whoami` masks credential values while naming their source as `[flag]`, `[env]`, `[keyring]`, `[config]` or `[unset]`.

## A run, end to end

```bash theme={null}
SANDBOX=$(twinbay sandboxes create \
  --name checkout-tests \
  --twins '[{"twin":"resend"}]' \
  --prompt "one segment with a delivered and a hard-bounce recipient" \
  --jq '.id' | tr -d '"')

TWIN=$(twinbay sandboxes get --sandbox-id "$SANDBOX" --jq '.twins[0].id' | tr -d '"')

# Poll until the twin serves
until [ "$(twinbay sandboxes get --sandbox-id "$SANDBOX" --jq '.twins[0].state')" = '"ready"' ]; do
  sleep 2
done

BASE_URL=$(twinbay sandboxes get --sandbox-id "$SANDBOX" --jq '.twins[0].url' | tr -d '"')
API_KEY=$(twinbay sandboxes twins credential \
  --sandbox-id "$SANDBOX" --sandbox-twin-id "$TWIN" --jq '.api_key' | tr -d '"')

# Your suite reads the base URL and key it should point the provider SDK at
TWIN_BASE_URL=$BASE_URL TWIN_API_KEY=$API_KEY pytest

twinbay sandboxes logs list --sandbox-id "$SANDBOX" --output-format table
```

## Commands

| Group                     | Commands                                                             |
| ------------------------- | -------------------------------------------------------------------- |
| `twins`                   | `list`, `get`                                                        |
| `sandboxes`               | `list`, `create`, `get`                                              |
| `sandboxes twins`         | `start`, `stop`, `credential`, `advance`                             |
| `sandboxes twins records` | `list`, `update`                                                     |
| `sandboxes templates`     | `list`, `delete`                                                     |
| `sandboxes logs`          | `list`, `get`                                                        |
| `api-keys`                | `create`, `list`, `revoke`                                           |
| `organizations`           | `list`, `create`, `ensure-default`, `read-current`, `rename-current` |
| `users`                   | `read-me`                                                            |
| Session                   | `auth login`, `auth logout`, `auth whoami`, `configure`, `whoami`    |
| Local                     | `explore`, `version`                                                 |

`twinbay explore` opens an interactive browser over every command, its description and its flags, and runs the one you pick. Run `twinbay <group> <command> --help` for flags.

Path parameters are flags: `--sandbox-id`, `--sandbox-twin-id`, `--resource`, `--external-id`. Body fields are flags too, so `sandboxes create` takes `--name`, `--prompt`, `--twins`, `--template` and `--save-as-template`, and `sandboxes twins advance` takes `--at`.

## Request bodies

Commands that send a body accept three inputs. Individual flags win over `--body`, which wins over stdin.

```bash theme={null}
twinbay sandboxes create --body '{"name":"nightly","twins":[{"twin":"resend"}]}'
twinbay sandboxes create < sandbox.json
twinbay sandboxes create --body "$(cat sandbox.json)" --name override
```

`--twins` takes the JSON array the API takes, so a curated scenario rides along with the slug:

```bash theme={null}
twinbay sandboxes create --name nightly \
  --twins '[{"twin":"resend","scenario_template":"hard-bounce"},{"twin":"linear"}]'
```

Record bodies go in `--body-param`, keeping `--body` free for the whole request:

```bash theme={null}
twinbay sandboxes twins records update \
  --sandbox-id "$SANDBOX" --sandbox-twin-id "$TWIN" \
  --resource emails --external-id 4ef9a417-02e9-4d39-ad75-9611e0fcc33c \
  --body-param '{"last_event":"bounced"}'
```

## Output

`--output-format` (`-o`) picks the rendering: `pretty` (default), `json`, `yaml`, `table`, and `toon` for [Token-Oriented Object Notation](https://github.com/toon-format/spec), which costs 30 to 60 percent fewer tokens than JSON when a response goes into a prompt.

```bash theme={null}
twinbay sandboxes list --output-format table
twinbay sandboxes logs list --sandbox-id "$SANDBOX" --output-format toon
twinbay sandboxes get --sandbox-id "$SANDBOX" --jq '.twins[] | {twin_slug, state, url}'
```

`--jq` (`-q`) applies a [jq](https://jqlang.org) expression and always prints JSON, overriding `--output-format`. Under `--all`, output streams as it arrives: NDJSON for `json`, `---`-separated documents for `yaml`.

Colors follow `--color auto|always|never`, plus `NO_COLOR` and `FORCE_COLOR`.

## Inside a coding agent

`--agent-mode` returns structured errors and defaults the output to TOON. The CLI turns it on by itself when it detects an agent environment such as `CLAUDE_CODE` or `CURSOR_AGENT`, so an agent gets compact output without being told; pass `--agent-mode=false` to opt out. Add `--no-interactive` to shut off prompting, TUI forms and the explorer auto-launch in any non-tty context.

For an agent that speaks MCP, the [MCP server](/mcp-server) is the shorter path: the same operations arrive as tools, and the user signs in rather than passing a key.

## Scripting

`--dry-run` prints the request the CLI would send to stderr and makes no network call. Exit code `0` means success and `1` means failure, with data on stdout and errors on stderr.

```bash theme={null}
twinbay sandboxes create --name nightly --twins '[{"twin":"resend"}]' --dry-run
```

Also on every command: `--debug` (`-d`) for request and response diagnostics on stderr, `--include-headers` to keep response headers in the output, `--header` (`-H`) to add a request header, `--timeout 30s`, `--server-url` to point at another deployment, and `--usage` to print the command schema as KDL.

## Shell completion

```bash theme={null}
source <(twinbay completion bash)   # or zsh, fish, powershell
```
