---
name: reindeer-external-cli
description: >
  Reindeer external CLI reference for customers managing workspaces, agents,
  connectors, artifacts, contexts, collections, templates, runs, members, and
  extensions (custom console display surfaces / dynamic UI).
  Use when: (1) a customer needs to set up or manage their workspace, (2) creating
  or configuring agents, connectors, and contexts, (3) managing collections and
  artifacts, (4) inspecting runs, steps, or cases, (5) managing secrets and
  connection profiles, (6) inviting or managing members, (7) building or editing
  extensions and the dynamic console UI, (8) finding the public API/OpenAPI schema.
  Triggers on: reindeer cli, workspace setup, agent create, connector trigger,
  collection manage, template list, member invite, extension create, dynamic ui,
  view_spec, openapi.
allowed-tools: Bash, Read, Write
---

# Reindeer CLI (External)

The external CLI binary is **`reindeer`**. It exposes the public Reindeer API for managing workspaces, agents, connectors, artifacts, contexts, collections, templates, runs, and more.

## Installation

**macOS / Linux:**

```bash
curl -fsSL https://storage.googleapis.com/reindeer-release-external/install.sh | bash
```

**Windows (PowerShell):**

```powershell
irm https://storage.googleapis.com/reindeer-release-external/install.ps1 | iex
```

Installs to `~/.reindeer/bin/` (macOS/Linux) or `~\.reindeer\bin\` (Windows). Verify with:

```bash
reindeer --version
```

If the shell doesn't pick it up, reload your shell (`source ~/.zshrc`, `source ~/.bashrc`, or restart the terminal on Windows).

## Login

```bash
reindeer login
```

Opens a browser for authentication. Config stored at `~/.reindeer/config.json`.

### Headless login (agent-friendly)

When running inside an agent or other non-interactive environment (no browser, no TTY), use:

```bash
reindeer login --headless --no-prompt
```

- `--headless` — does not try to open a browser. Instead prints the authentication URL to stdout so the agent can surface it to the user (or another system) to complete the OAuth flow out-of-band.
- `--no-prompt` — skips all interactive confirmations. The command will not block waiting for keyboard input, which is required for unattended/agentic flows.

Typical agentic flow:

1. The agent runs `reindeer login --headless --no-prompt`.
2. The CLI prints a one-time login URL.
3. The agent shows the URL to the user (e.g. in chat) and waits for them to complete auth in their own browser.
4. Once auth completes, the CLI writes the credentials to `~/.reindeer/config.json` and exits cleanly.

> Always pass both flags together for agent use. `--headless` alone may still prompt; `--no-prompt` alone may still try to launch a browser.

### Multi-tenant / multi-profile login

Use `--profile` and `--base-url` to manage credentials for different tenants side by side:

```bash
reindeer login --headless --no-prompt --profile demo --base-url https://api.demo.reindeerlabs.ai
```

Then pass `--profile demo --base-url https://api.demo.reindeerlabs.ai` on every subsequent command to target that tenant. Profiles are stored independently in `~/.reindeer/config.json`.

## Global Flags

All commands accept these flags:

| Flag | Description |
|---|---|
| `-w, --workspace <name>` | Workspace name or ID |
| `-o, --output <format>` | `json` or `table` (default: `table`) |
| `-b, --base-url <url>` | API base URL override |
| `-p, --profile <name>` | Config profile to use |
| `-v, --verbose` | Enable verbose output |

## API Reference

The CLI is a thin client over the public Reindeer v1 API — anything the CLI does is available directly over HTTP. The full schema is served per tenant:

- **OpenAPI spec** (for agents and code generation): `https://api.<tenant>.reindeerlabs.ai/api/v1/openapi.json`
- **Swagger UI** (browsable docs): `https://api.<tenant>.reindeerlabs.ai/api/v1/docs`

Replace `<tenant>` with your environment subdomain — the same value embedded in `--base-url`. For example, `--base-url https://api.demo.reindeerlabs.ai` → `https://api.demo.reindeerlabs.ai/api/v1/openapi.json`.

> Both the OpenAPI spec and the Swagger UI are public — no authentication is required to fetch them (only the API endpoints they describe require a token).

## Workspaces

```bash
reindeer workspaces list
reindeer workspaces create --name <url-safe-name> --display-name "<Display Name>"
```

## Templates

List available agent templates:

```bash
reindeer templates list -w <workspace>
reindeer templates get <template-name> -w <workspace>
```

> Templates define agent pipelines. Read the template description to understand what context items an agent needs.

## Connection Profiles

Connection profiles define how connectors authenticate with external systems (e.g. Microsoft 365, APIs).

```bash
reindeer connection-profiles list -w <workspace>
reindeer connection-profiles get <profile> -w <workspace>
reindeer connection-profiles test <profile> -w <workspace>
```

Create a connection profile:

```bash
reindeer connection-profiles create -w <workspace> --data '{
  "connection_profile_id": "<id>",
  "display_name": "<name>",
  "connection_type": "microsoft_365_email",
  "config": { ... }
}'
```

Update or delete:

```bash
reindeer connection-profiles update <profile> -w <workspace> --data '{ ... }'
reindeer connection-profiles delete <profile> -w <workspace>
```

## Connectors

```bash
reindeer connectors list -w <workspace>
reindeer connectors get <connector> -w <workspace>
```

Create a file upload (API) connector:

```bash
reindeer connectors create -w <workspace> --data '{
  "connector_id": "file-upload",
  "display_name": "File Upload",
  "connector_type": "api",
  "config": {
    "connector_type": "api",
    "trigger_mode": "per_file"
  }
}'
```

Trigger a connector with files:

```bash
reindeer connectors trigger -w <workspace> <connector-id> --file <path>
reindeer connectors trigger -w <workspace> <connector-id> --file file1.pdf --file file2.png
```

## Artifacts

```bash
reindeer artifacts list -w <workspace>
reindeer artifacts get <artifact> -w <workspace>
```

Create a document artifact from a file:

```bash
reindeer artifacts create -w <workspace> --file <path-to-file> --data '{
  "artifact_type": "document",
  "unique_key": "<dedup-key>",
  "data": "{\"type\": \"document\", \"filename\": \"<filename>\", \"content_type\": \"<mime-type>\", \"size_bytes\": 0}"
}'
```

> The `data` field is a JSON-encoded string (not a nested object). `size_bytes` can be `0` -- the server computes the actual size. The `unique_key` prevents duplicates within the workspace.

Download an artifact's content (returns a signed URL):

```bash
reindeer artifacts download <artifact-id> -w <workspace>
```

> Use `-o json` to extract the `download_url` programmatically, then `curl -sL <url>` to fetch the file content.

## Collections

Collections group artifacts together.

```bash
reindeer collections list -w <workspace>
reindeer collections get <collection> -w <workspace>
```

Create a collection:

```bash
reindeer collections create -w <workspace> --name <name> --description "<description>"
```

Manage artifacts in a collection:

```bash
reindeer collections list-artifacts <collection-id> -w <workspace>
reindeer collections add-artifacts <collection-id> -w <workspace> --data '{ ... }'
reindeer collections remove-artifacts <collection-id> -w <workspace> --data '{ ... }'
```

Update or delete:

```bash
reindeer collections update <collection> -w <workspace> --name <new-name>
reindeer collections delete <collection> -w <workspace>
```

## Contexts

```bash
reindeer contexts list -w <workspace>
reindeer contexts get <context> -w <workspace>
```

Create a context with artifact entries:

```bash
reindeer contexts create -w <workspace> \
  --name <url-safe-name> \
  --display-name "<Display Name>" \
  --description "<description>" \
  --entries '[{"artifact": "workspaces/<workspace>/artifacts/<artifact-id>", "display_name": "<entry name>", "description": "<entry description>"}]'
```

> `--entries` takes a JSON array. Each entry references an artifact by its full resource name.

Create a new context version (replaces entries, bumps version number):

```bash
reindeer contexts new-version -w <workspace> <context-name> \
  --entries '[{"artifact": "workspaces/<workspace>/artifacts/<new-artifact-id>", "display_name": "<entry name>", "description": "<entry description>"}]'
```

> The agent automatically picks up the latest context version. A new version triggers a new agent revision to be compiled (may take 30-60 seconds).

List context versions:

```bash
reindeer contexts versions -w <workspace> <context-id>
```

## Agents

```bash
reindeer agents list -w <workspace>
reindeer agents get <agent> -w <workspace>
```

Create an agent with a connector subscription and context:

```bash
reindeer agents create -w <workspace> \
  --name <url-safe-name> \
  --display-name "<Display Name>" \
  --description "<description>" \
  --subscriptions '{"connector_type":"api","connector":"workspaces/<workspace>/connectors/<connector>","events":[{"type":"api_trigger"}]}' \
  --contexts workspaces/<workspace>/contexts/<context-name>
```

> Each `--subscriptions` value is a single JSON object (not an array). Pass multiple `--subscriptions` flags for multiple connectors. `--contexts` takes a resource name string.

Update an agent (e.g. bind a context):

```bash
reindeer agents update -w <workspace> <agent> --contexts "workspaces/<workspace>/contexts/<context>"
```

## Agent Revisions

```bash
reindeer agent-revisions list -w <workspace> --agent <agent-name>
reindeer agent-revisions get -w <workspace> --agent <agent-name> <revision-name>
```

> The v1 API strips the `system_prompt` field from revisions. To see the compiled prompt, use the internal CLI.

### Comparing revisions via context diffs

A new agent revision is compiled each time a context version changes. To understand what changed between two revisions:

1. **List revisions** to identify the two you want to compare:
   ```bash
   reindeer agent-revisions list -w <workspace> --agent <agent-name>
   ```

2. **List context versions** to see what changed and when:
   ```bash
   reindeer contexts versions -w <workspace> <context-name>
   ```
   Match revision timestamps to context version timestamps — a revision created at time T uses the context version created just before T.

3. **Get the current context** to find artifact references in the entries:
   ```bash
   reindeer contexts get <context-name> -w <workspace> -o json
   ```
   Each entry contains an `artifact` resource name with the artifact ID.

4. **Download and diff the artifacts** for each version:
   ```bash
   url=$(reindeer artifacts download <artifact-id> -w <workspace> -o json | python3 -c "import json,sys; print(json.load(sys.stdin)['download_url'])")
   curl -sL "$url" > /tmp/version_a.md
   # Repeat for the other artifact ID
   diff /tmp/version_a.md /tmp/version_b.md
   ```

> Since `contexts get` only returns the latest version, find older artifact IDs by listing all artifacts and filtering by unique key pattern (e.g. `reindeer artifacts list -w <workspace> -o json | grep <context-name>`).

## Runs

```bash
reindeer runs list -w <workspace> --agent <agent-name>
reindeer runs get -w <workspace> --agent <agent-name> <run-id>
```

Run statuses: `running`, `completed`, `failed`.

The `result` field (available when `completed`) contains the structured output.

### Creating runs

Use `runs create` to start a new run via the CLI. The `--input` flag takes a JSON body matching the API Gateway's `CreateRunInput` shape.

```bash
reindeer runs create -w <workspace> --agent <agent-name> --input '<json>'
```

| Flag | Description |
|---|---|
| `--input <json>` | JSON body with `type` and type-specific fields (required) |
| `--wait` | Poll until the operation completes before returning |

The response is a pending Operation (`done: false`). When the Operation completes, its `result.run` holds the new run's resource name — the run itself keeps executing asynchronously; poll `runs get` until its `status` leaves `running`.

> **`type` is required** — one of `new_run`, `previous_run`, `previous_case`. There is no lenient default; the server rejects requests without it.
>
> **`session` is a nested object** for `previous_run` and `previous_case`: `{"session": "resume", ...}` continues the existing conversation, `{"session": "new", ...}` starts a fresh session. `user_input` goes **inside** the `session` object.
>
> **All IDs use AIP-122 resource names** (e.g. `workspaces/<ws>/agents/<agent>/runs/<run-id>`), not raw UUIDs.

#### NewRun

Start a brand-new run from caller-supplied inputs. Provide a non-empty `artifacts` or a non-empty `user_input` (400 if both are absent). Optional `agent_revision` pins a revision; omit to use the serving revision.

```bash
reindeer -w <workspace> runs create --agent <agent-name> \
  --input '{"type":"new_run","user_input":"...","artifacts":["workspaces/<ws>/artifacts/<id>"]}'
```

#### PreviousRun + Resume Session

Resume an existing run's session (continues the conversation). Optional `user_input` is delivered as an additional message; optional `artifacts` attaches new inputs on top of those inherited from the source run.

```bash
reindeer -w <workspace> runs create --agent <agent-name> \
  --input '{"type":"previous_run","previous_run":"workspaces/<ws>/agents/<agent>/runs/<run-id>","session":{"session":"resume","user_input":"..."}}'
```

#### PreviousRun + New Session

Start a fresh session from a previous run's inherited inputs (linked lineage). Optional `user_input` replaces the source run's instruction; optional `agent_revision` pins a revision.

```bash
reindeer -w <workspace> runs create --agent <agent-name> \
  --input '{"type":"previous_run","previous_run":"workspaces/<ws>/agents/<agent>/runs/<run-id>","session":{"session":"new","user_input":"..."}}'
```

#### PreviousCase + Resume Session

Resume the most recent session of a case:

```bash
reindeer -w <workspace> runs create --agent <agent-name> \
  --input '{"type":"previous_case","previous_case":"workspaces/<ws>/agents/<agent>/cases/<case-id>","session":{"session":"resume","user_input":"..."}}'
```

#### PreviousCase + New Session

Start a new session on an existing case:

```bash
reindeer -w <workspace> runs create --agent <agent-name> \
  --input '{"type":"previous_case","previous_case":"workspaces/<ws>/agents/<agent>/cases/<case-id>","session":{"session":"new","user_input":"..."}}'
```

## Steps

```bash
reindeer steps list -w <workspace> --agent <agent-name> --run-id <run-id>
```

## Cases

Cases group related runs.

```bash
reindeer cases list -w <workspace> --agent <agent-name>
reindeer cases get <case> -w <workspace> --agent <agent-name>
```

## Extensions

Extensions add custom display surfaces to the Reindeer console without the platform shipping a dedicated entity for each one. Each extension is a workspace-scoped, versioned, typed resource whose `payload` tells the console **what** to render and **where**.

```bash
reindeer extensions list -w <workspace>
reindeer extensions list -w <workspace> --type <type>
reindeer extensions list -w <workspace> --parent workspaces/<workspace>/agents/<agent>
reindeer extensions get <extension> -w <workspace>
```

Each extension has a `type` that targets a specific console surface, and a `payload` whose shape depends on that type. **The available extension types and their payload schemas are evolving — treat the [API Reference](#api-reference) OpenAPI spec (the `ExtensionType` and `ExtensionPayload` schemas) as the source of truth, not this doc.** To see which types exist in an environment, list extensions or read the spec.

A `type` is fixed at creation and never changes. Scope is set via `--parent`, which is either a workspace (`workspaces/<workspace>`) or an agent (`workspaces/<workspace>/agents/<agent>`); the server rejects a parent that isn't valid for the chosen type.

### Payloads

`--payload` is a JSON object whose `type` field must match the extension's `type`; its remaining fields depend on the type (see the OpenAPI spec for each schema). Most display extensions carry a `view_spec` — a declarative UI block (see [Dynamic UI](#dynamic-ui-view_spec) below) — which can be set to `null` to clear the configured view.

### Create

```bash
reindeer extensions create -w <workspace> \
  --name <url-safe-name> \
  --type <type> \
  --parent workspaces/<workspace>/agents/<agent> \
  --display-name "<Display Name>" \
  --payload '{"type": "<type>", ...}'
```

> `--name` must be URL-safe (`[a-z0-9-]`, max 63 chars) and unique within the workspace. Add `--wait` to block until the operation completes.

### Update vs. new version

`update` changes only mutable metadata (the display name) and does **not** bump the version:

```bash
reindeer extensions update <extension> -w <workspace> --display-name "<New Name>"
```

To change what gets rendered, publish a new version. Each new version replaces the served payload and increments the version number; the latest version is served automatically:

```bash
reindeer extensions new-version <extension-id> -w <workspace> \
  --payload '{"type": "<type>", ...}'
```

Inspect history:

```bash
reindeer extensions versions <extension-id> -w <workspace>
reindeer extensions version <extension-id> <version-number> -w <workspace>
```

Delete:

```bash
reindeer extensions delete <extension> -w <workspace>
```

### Dynamic UI (`view_spec`)

The `view_spec` carried by the display extensions is a declarative JSON tree the console renders at runtime — no per-customer frontend code ships. Each node names a component and supplies its `props`; container nodes nest `children`:

```json
{
  "type": "Card",
  "props": { "title": "Invoice" },
  "children": [
    { "type": "Text", "props": { "value": "..." } }
  ]
}
```

The console maps each `type` to a built-in component from a fixed catalog. Available components include:

- **Layout** — Stack, Grid, Card, Tabs, Accordion, Separator
- **Content** — Heading, Text, Markdown, Badge, StatusBadge, Alert, Icon
- **Data** — Table, DataField, DocumentPreview, FormattedNumber, FormattedDate, KpiCard
- **Charts** — BarChart, LineChart, PieChart
- **Input / actions** — Button, Input, Select
- **Containers** — DetailModal

Props can be bound to the live record (the case or run the surface is rendering) instead of hard-coded, so one spec renders different content per case. Because the spec is plain JSON validated against the catalog, it can be generated by an agent and refined in the console, then saved as a new extension version. Component types or props that aren't in the catalog are rejected.

## Secrets

Manage workspace secrets (values are write-only):

```bash
reindeer secrets list -w <workspace>
reindeer secrets get <secret> -w <workspace>
reindeer secrets create -w <workspace> --name <name> --description "<description>" --value "<secret-value>"
reindeer secrets update <secret> -w <workspace> --value "<new-value>"
reindeer secrets delete <secret> -w <workspace>
```

> Secret values are write-only and never returned by `get` or `list`.

## Members

### Organization Members

```bash
reindeer members list
reindeer members invite --email <email> --org-role <role>
reindeer members update-role <member-id> --org-role <role>
reindeer members remove <member-id>
```

Roles: `none`, `organization_viewer`, `organization_editor`, `organization_admin`.

### Workspace Members

```bash
reindeer workspace-members list -w <workspace>
reindeer workspace-members update <member-id> -w <workspace> --role <role>
```

## Configuration

```bash
reindeer config get <key>
reindeer config set <key> <value>
```

Common keys: `base-url`, `profile`.

## Upgrade

```bash
reindeer upgrade
```
