# Cargo CLI

Command-line interface for the [Cargo API](https://docs.getcargo.ai/api-reference/introduction). Manage workspaces, storage, orchestration, connections, billing, and more from the terminal; for a complete feature overview, see the [Cargo CLI Overview](https://docs.getcargo.ai/cli/overview).

## Requirements

- Node.js 22.x
- A Cargo API token (create in your workspace under **Settings → API**)

## Installation

Install from npm (global):

```bash
npm install -g @cargo-ai/cli
```

Or run once with `npx` without installing:

```bash
npx @cargo-ai/cli orchestration workflow list
```

Using a coding agent? Skip the manual setup — see [Agent Skills](#agent-skills) below to have Claude Code, Codex, or Cursor drive this CLI for you.

## Authentication

A single `cargo-ai login` command supports three channels — pass exactly one of `--email`, `--oauth` or `--token`:

```bash
# 1. Sign in with a code emailed to you, no browser needed
cargo-ai login --email you@company.com

# 2. Sign in via your browser using the OAuth 2.0 Device Authorization Flow
cargo-ai login --oauth

# 3. Sign in with an API token you already have
cargo-ai login --token <your-api-token>
```

`--email` and `--oauth` both create the account if it does not exist yet, so there is no separate sign-up command.

`--email` and `--oauth` save the signed-in session — a short-lived access token plus the refresh token that renews it — to `~/.config/cargo-ai/credentials.json` (file mode `0600`). The CLI renews it in the background as it expires, so you stay signed in and no API token is created per machine. `--token` saves the API token you supplied instead. The same file backs `cargo-cdk`, so one login serves both.

`cargo-ai logout` revokes the session at the identity provider, so signing out is final. A token you supplied yourself with `--token` is only forgotten locally, since it may be in use elsewhere; pass `--keep-token` to force that behaviour for a session too. If a credentials file cannot be deleted — a read-only config directory, or one owned by another user — `logout` fails and names the file rather than reporting a sign-out the next command would contradict.

Treat the file as you would a password. A saved session acts as you across **every** workspace you belong to — unlike an API token, which is limited to one workspace and to the permissions it was created with — and it stays valid until you run `cargo-ai logout` or the identity provider expires it. On a shared or long-lived machine (CI, a build box), prefer an API token created under **Settings → API** and passed via `CARGO_API_TOKEN`, which you can scope down and revoke from the dashboard.

```bash
# Check current auth status
cargo-ai whoami

# Remove saved credentials
cargo-ai logout
```

### Book a demo

`cargo-ai book-demo` opens the same Cal.com form as the website's **Talk to sales** button. A person still has to pick a time — the command does not create a booking. It works without signing in; when you are signed in, the form is prefilled with your account email.

```bash
cargo-ai book-demo
cargo-ai book-demo --email you@company.com
cargo-ai book-demo --no-open    # print the URL only
```

This is not the guided product tour. That is `cargo-ai start --continue demo`.

### Sign-in without a browser (`--email`)

`cargo-ai login --email you@company.com` emails you a one-time code, then creates your account (on first use) and a workspace — no browser and no dashboard visit at any point. On a terminal the code is read back from a prompt. Where there is no terminal (sandboxes, agent shells) the first call sends the code and exits, so you can finish with:

```bash
cargo-ai login --email you@company.com --code 123456 --workspace-name "Acme GTM"
```

Re-running with an address that already has an account resolves to its existing workspace rather than creating one, and `--workspace-name` reuses a workspace of that name when one already exists, so this is safe to repeat.

To keep the code out of your shell history, pass it on stdin with `--code -`:

```bash
echo 123456 | cargo-ai login --email you@company.com --code -
```

### Browser sign-in (`--oauth`)

`cargo-ai login --oauth` runs the standard [OAuth 2.0 Device Authorization Flow](https://datatracker.ietf.org/doc/html/rfc8628) against the Cargo OAuth provider, prints a verification URL and user code, opens your default browser, and polls until you complete sign-in. On success the CLI writes the resulting session to `~/.config/cargo-ai/credentials.json`.

The CLI ships with a built-in OAuth client, so no setup is required.

### Workspace selection

`--email` and `--oauth` authenticate you as a user, so the CLI picks a workspace to save alongside the session:

- **No workspaces**: one is created (you are prompted for a name in a TTY, or pass `--workspace-name <name>`).
- **One workspace**: it is selected automatically.
- **Multiple workspaces**: you are prompted to pick one (in a TTY); in non-interactive shells the CLI prints the list and asks you to re-run with `--workspace-uuid <uuid>`.

Pass `--workspace-uuid <uuid>` or `--workspace-name <name>` to skip the prompt.

### Switching workspaces

A signed-in session is not tied to one workspace, so `CARGO_WORKSPACE_UUID` switches workspace for a single command or a whole shell:

```bash
CARGO_WORKSPACE_UUID=<other-workspace-uuid> cargo-ai storage model list
```

To change the default your session uses, sign in again for that workspace:

```bash
cargo-ai login --email you@company.com --workspace-uuid <other-workspace-uuid>
```

An API token is different: it is scoped to a single workspace and the server takes the workspace from the token, not from anything sent alongside it. So if you signed in with `--token` and `CARGO_WORKSPACE_UUID` names a different workspace, the CLI refuses the command rather than silently running against the token's workspace. The override still applies when you supply the token yourself via `CARGO_API_TOKEN`, since only you know which workspace that token is for.

### Environment variables (override)

Environment variables take precedence over saved credentials, useful for CI or temporary overrides.

| Variable               | Default                   | Description                                                                                 |
| ---------------------- | ------------------------- | ------------------------------------------------------------------------------------------- |
| `CARGO_API_TOKEN`      | —                         | Your Cargo API token (Bearer auth)                                                          |
| `CARGO_BASE_URL`       | `https://api.getcargo.io` | API base URL                                                                                |
| `CARGO_WORKSPACE_UUID` | —                         | Workspace UUID for workspace-scoped commands (some commands also accept `--workspace-uuid`) |

## Usage

Command structure: `cargo-ai <domain> <sub> <action>`.

After a global install, run:

```bash
cargo-ai <domain> <sub> <action>
```

Without installing, use `npx`:

```bash
npx @cargo-ai/cli <domain> <sub> <action>
```

All commands output JSON to stdout.

### Help

```bash
cargo-ai --help
cargo-ai orchestration --help
cargo-ai orchestration workflow --help
```

### Domains and example commands

| Domain                   | Description                                                               | Example commands                                                                                                                                                                                  |
| ------------------------ | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **orchestration**        | Workflows, plays, runs, batches, tools, templates                         | `cargo-ai orchestration workflow list`, `cargo-ai orchestration run list --workflow-uuid <uuid>`                                                                                                  |
| **workspaceManagement**  | Workspaces, users, tokens, roles, folders                                 | `cargo-ai workspaceManagement workspaces list`, `cargo-ai workspaceManagement token list`                                                                                                         |
| **storage**              | Datasets, models, relationships, runs, records                            | `cargo-ai storage dataset list`, `cargo-ai storage model list`                                                                                                                                    |
| **connection**           | Connectors and integrations                                               | `cargo-ai connection connector list`, `cargo-ai connection integration list`                                                                                                                      |
| **billing**              | Usage and subscription                                                    | `cargo-ai billing subscription get`, `cargo-ai billing usage get-metrics --payload '{"from":"2025-01-01","to":"2025-01-31"}'`                                                                     |
| **segmentation**         | Segments and changes                                                      | `cargo-ai segmentation segment list`, `cargo-ai segmentation change list --payload '{}'`                                                                                                          |
| **revenue-organization** | Allocations, capacities, members, territories                             | `cargo-ai revenue-organization member list`, `cargo-ai revenue-organization territory list`                                                                                                       |
| **expression**           | Recipes and expression evaluation                                         | `cargo-ai expression recipe list`, `cargo-ai expression eval evaluate --payload '{}'`                                                                                                             |
| **system-of-record**     | System of record, client, logs                                            | `cargo-ai system-of-record sor list`, `cargo-ai system-of-record log list --payload '{}'`                                                                                                         |
| **user-management**      | Current user (no workspace context)                                       | `cargo-ai user-management user get-current`                                                                                                                                                       |
| **ai**                   | AI templates, agents, releases, chats, MCP, files                         | `cargo-ai ai template list`, `cargo-ai ai agent list`, `cargo-ai ai file list`                                                                                                                    |
| **context**              | Context repository, runtime sandbox, and knowledge graph                  | `cargo-ai context repository get`, `cargo-ai context runtime browse --path <path>`, `cargo-ai context graph get`                                                                                  |
| **hosting**              | Cargo Hosting apps (Vite SPAs), workers (env vars, logs), and deployments | `cargo-ai hosting app list`, `cargo-ai hosting worker env list --worker-uuid <uuid>`, `cargo-ai hosting worker logs list --worker-uuid <uuid>`, `cargo-ai hosting deployment list --payload '{}'` |
| **mailboxManagement**    | Owned sending mailboxes, threads, events, suppressions, pricing           | `cargo-ai mailboxManagement mailbox list`, `cargo-ai mailboxManagement mailbox get-send-allowance <uuid>`                                                                                         |
| **project**              | Declarative IaC: define resources in code, deploy, generate types         | `cargo-ai project init <dir>`, `cargo-ai project types`, `cargo-ai project plan`, `cargo-ai project deploy` (`cdk` is an alias)                                                                   |

Commands that accept complex payloads use a `--payload <json>` option (e.g. `cargo-ai orchestration play create --payload '{"name":"My Play",...}'`). Use `--help` on any subcommand for options.

### `orchestration release deploy-draft`

Deploy a draft release of an **existing** workflow. Two input modes share the same command:

```bash
# Compile a Workflow SDK module and deploy it
cargo-ai orchestration release deploy-draft --file ./my-workflow.ts

# Preview nodes + form fields without deploying
cargo-ai orchestration release deploy-draft --file ./my-workflow.ts --dry-run

# Pin an explicit version + description
cargo-ai orchestration release deploy-draft --file ./my-workflow.ts --workflow-uuid <uuid> --version 2.0.0 --description "New enrichment path"

# Power-user path: deploy raw wire-format JSON
cargo-ai orchestration release deploy-draft --workflow-uuid <uuid> --nodes '[…]' --form-fields '[…]'
```

`--file <path>` loads a `@cargo-ai/workflow-sdk` module (its `export default defineWorkflow(...)`) and sources `nodes` / `formFields` from the compiled output. With `--file`:

- `--workflow-uuid` is optional — when omitted, the command best-effort matches the compiled `slug` against existing workflows' `template.slug` and errors if the match is missing or ambiguous.
- `--version` defaults to the next minor bump over the latest existing release (or `1.0.0` for the first).
- `--nodes` / `--form-fields` are rejected (they're sourced from the module).

Without `--file`, `--workflow-uuid`, `--nodes`, and `--form-fields` are all required (raw JSON path). Constraint either way: there is **no** workflow-create endpoint, so the target workflow must already exist.

Other release verbs (`list`, `get <uuid>`, `get-deployed`, `get-draft`, `update-draft`) live under the same group — see `cargo-ai orchestration release --help`.

## Agent Skills

[Cargo Skills](https://github.com/getcargohq/cargo-skills) teaches AI coding agents (Claude Code, Codex, Cursor, Windsurf, GitHub Copilot, Hermes Agent, etc.) how to use this CLI — seventeen skills covering lead sourcing, enrichment, email verification, lead scoring, CRM sync, buying-signal monitoring, orchestration, storage, and workspace-as-code.

**Let your agent install everything.** Paste this into Claude Code, Codex, or Cursor and it handles the CLI, sign-in, and skills for its own platform:

```
Install Cargo by following https://api.getcargo.io/INSTALL.md
```

**Or install manually.** As a plugin (Claude Code, Codex, Cursor — bundles the skills plus approval and session hooks):

```bash
/plugin marketplace add getcargohq/cargo-skills    # Claude Code
codex plugin marketplace add getcargohq/cargo-skills
```

As skills only (any [skills.sh](https://skills.sh)-compatible agent):

```bash
npx skills add getcargohq/cargo-skills
```

Pick one channel — installing both duplicates every skill.

On [Hermes Agent](https://github.com/NousResearch/hermes-agent), install from its own hub instead:

```bash
hermes skills install skills-sh/getcargohq/cargo-skills/cargo-gtm
```

## Development

```bash
# Build
npm run build

# Run with tsx (no build)
npm run dev
# then in another terminal: npx tsx src/index.ts orchestration workflow list

# Type-check and lint
npm run type:check
npm run lint:check
```

### Declaring an option's values

An option with a closed set of values lists them in its placeholder, never in
its description:

```ts
.option("--format <csv|json>", "Export format")
.option("--statuses <idle,pending,success>", "Filter by statuses")
.option("--is-finished [true|false]", "Only return finished runs")
```

A pipe means "one of these", a comma means "any of these, comma-separated", and
that is the whole grammar. Commander treats the placeholder as a display name,
so this changes nothing at parse time — but `--help` shows the set, and the
interactive palette turns it into a menu rather than an empty text field. Values
written into the description instead are invisible to both.

Use commander's own `.choices()` only when the CLI should _reject_ anything
else. It usually should not: the API owns these lists, and a value it starts
accepting would be refused here until someone noticed.

## License

See the project for license information.
