# CLI reference

> Source: https://cross-deck.com/docs/cli/ · Crossdeck developer docs (generated from the published page).

Reference
Current version: 1.4.0
·
~11 min read
·
Updated Aug 3, 2026

`@cross-deck/cli` is one binary — `crossdeck` — with **two families**. Sign in with your browser and you provision projects and apps, mint keys, and read revenue, analytics, and errors straight from your terminal, across **web, iOS, and Android**. Or drop the key-based source-map upload into CI, no login, no browser. Same binary, one keychain, honest command names. This page is the full reference.

## The launcher

Run `crossdeck` with no command on an interactive terminal and it opens a **session** — a working environment, like `claude`. Inside it you type bare commands with no `crossdeck` prefix (`whoami`, `init`, `revenue`, `use prolend`, `help`, `exit`), and your active project persists across the session and shows in the prompt. The session opens with the splash — the brand's first impression in your terminal, in **two states**: logged out shows exactly one path (sign in); logged in reveals the two hats — *stand up the engine*, then *read the facts*. The tagline is the honest moat, not a slogan: Crossdeck joins revenue, errors, and identity *by identity*.

Session, or one-shot — both work.

The session is for humans at a keyboard. Everything is *also* a one-shot command — `crossdeck revenue`, `crossdeck init …` — so scripts and CI compose exactly as you'd expect. When Crossdeck detects it isn't attached to an interactive terminal (a pipe, a CI runner), it never opens a session; it runs the command and exits.

~/prolend ❯ crossdeck
██╗ ██╗
╚██╗██╔╝
╚███╔╝
██╔██╗
██╔╝ ██╗
╚═╝ ╚═╝
crossdeck CLI · v1.4.0
The platform that joins revenue, errors, and identity — by identity — now in your terminal.
○ Not signed in.
Get started

crossdeck login Sign in — opens your browser
crossdeck login --key … Non-interactive (CI): use a project secret key

Docs cross-deck.com/docs · Dashboard app.cross-deck.com
~/prolend ❯ █

State 1 — logged out. One action.

~/prolend ❯ crossdeck
██╗ ██╗
╚██╗██╔╝
╚███╔╝
██╔██╗
██╔╝ ██╗
╚═╝ ╚═╝
crossdeck CLI · v1.4.0
The platform that joins revenue, errors, and identity — by identity — now in your terminal.
✓ Signed in as wes@pinet.co.za
● prolend · production
Stand up the engine

crossdeck init Create a project + app, mint keys
crossdeck apps create Add an app to a project
crossdeck use Switch the active project

Read the facts

crossdeck revenue Recognised cash for the project
crossdeck analytics Signup acquisition sources
crossdeck errors An issue, stitched to who it hit

Run crossdeck help for the full command tree.
~/prolend ❯ █

State 2 — logged in. The two hats.

The launcher never computes or advises.

It shows raw facts and the paths to them — never revenue-at-risk, churn queues, or "the cross" as a headline. Interpretation lives in Prism and the dashboard, never in the binary.

## Zero to a running account

The whole journey, word for word — from a machine with nothing installed to a live Crossdeck project with **identity wired, a payment rail connected, and the numbers flowing**. Follow it top to bottom; every command is exactly what you type and every result is what you'll see. You don't need to be an engineer — where a step happens in your app's code or in the dashboard instead of the terminal, it says so.

### Install & open the session Terminal

One package. Then type `crossdeck` to open the session — the working environment.

```
npm install -g @cross-deck/cli
crossdeck
```

You'll see the splash with `○ Not signed in` and one instruction: sign in.

### Sign in In the session

Type `login`. Your browser opens, you approve on the Crossdeck screen, and the terminal confirms — signed in as you. No password is ever typed into the terminal.

```
prolend ❯ login
```

### Stand up the project In the session

One command creates the project, creates the app, mints your **publishable keys**, and prints the snippet to paste. This is the moment you go from nothing to a real project.

```
prolend ❯ init --name "ProLend" --platform web --domain app.prolend.com
```

It prints your `proj_…` id, your `app_…` id, a **live** and a **test** key, and the exact code to paste next. Your new project is now the active one (it shows in the prompt).

### Turn on identity in your app Your app's code

Paste the snippet `init` gave you into your web app, and wire `identify()` to your own login. This is the moat: from here, every event, error, and dollar joins to the *real person*, not an anonymous blob.

```
import { Crossdeck } from "@cross-deck/web";

Crossdeck.init({ publishableKey: "cd_pub_live_…" });

// after your user signs in, tell Crossdeck who they are:
await Crossdeck.identify(currentUser.id);
```

Deploy your app as normal. The **first page load self-registers your domain** — you don't hand-configure any allowlist. Watch it arrive live with `analytics` back in the session.

### Connect a payment rail Dashboard

Payment rails connect through a one-time secure handshake in the dashboard — not a terminal command, because it grants long-lived access that belongs behind an explicit click. Open [app.cross-deck.com](https://app.cross-deck.com), go to **Payment rails** (bottom bar) or **Settings → Connected accounts**, choose your rail (e.g. Stripe), and approve. Revenue now flows in — and because identity is already wired, it joins to the same people.

### See it all working In the session

Back in the terminal session, read the facts. These are the same REST APIs your own code can call — here they're one word each.

```
prolend ❯ revenue      # cash coming in, by person
prolend ❯ people       # who you're identifying + their journeys
prolend ❯ analytics    # traffic & where signups come from
```

### Make production errors readable CI / build

Optional but recommended. So a crash in production shows the original `file:line:function` instead of minified soup, upload your source maps from CI — the key-based family, no login needed. This is what the dashboard's Errors tab means by "install the CLI."

```
crossdeck sourcemaps upload --release v1.0.0 \
  --url-prefix https://app.prolend.com/static/js/ ./dist
```

### Lock it down Dashboard + session

Every machine you sign in appears in **Settings → Developer → Connected apps**, revocable in one click — the answer to a lost laptop. From the terminal, `logout` revokes this machine's session server-side.

That's the whole arc. You just touched several parts of Crossdeck at once — here's what each did:

The CLI Provisioned the project, app, and keys, and read the facts — steps 1-3, 6.
Identity (the SDK) identify() joins every event and dollar to the real person — step 4. The moat.
Payment rails Stripe (and more) connected once in the dashboard; revenue flows by identity — step 5.
Errors & source maps The key-based CI family makes production stack traces readable — step 7.

Prefer one-shot?

Every step above is also a plain command, no session needed — `crossdeck login`, `crossdeck init …`, `crossdeck revenue`. Use the session when you're working by hand; use one-shot commands in scripts and CI. Same engine, either way.

## Two families, one binary

There are two ways to authenticate, for two different callers. Keeping them distinct is deliberate — a human at a keyboard and a CI runner have opposite needs.

Family Who it's for Credential Commands

Account (OAuth)
You, at a terminal
Browser sign-in → a short-lived workspace token ( cd_wk_live_… ), stored in your keychain file. No secret ever pasted.
login , whoami , logout , init , projects , apps , use , revenue , analytics , errors

CI / source maps
Build pipelines (no browser)
A project secret key ( cd_sk_live_… ) in an env var. No login step.
sourcemaps upload (alias upload-sourcemaps ), doctor

"Install the errors CLI" points here.

The Errors tab in the dashboard tells you to install the CLI to resolve minified stack traces. That's this same binary — the **CI / source-maps family**. It's key-based and never signs in, so it's independent of your interactive account session. See [Source maps (CI)](#sourcemaps).

## Install

A normal npm package. Install it globally for interactive use; use `npx` in CI so the runner never depends on a pre-installed binary.

### Global install

```
npm install -g @cross-deck/cli
```

Provides a `crossdeck` binary on your `PATH`. Verify with `crossdeck --version` (prints `1.4.0`).

### One-shot via npx

```
npx @cross-deck/cli sourcemaps upload --release v1.2.3 --url-prefix https://app.example.com/static/js/ ./dist
```

### Requirements

- **Node.js ≥ 18.** Uses the native `fetch`; no `node-fetch` dependency.

- **One runtime dependency.** `commander` for argument parsing.

The npm scope is @cross-deck with a hyphen.

Not `@crossdeck`. If `npm install` errors with "package not found," the scope is the cause.

## Sign in

`crossdeck login` runs the standard OAuth flow: it opens your browser, you approve on the Crossdeck consent screen, and the CLI receives a short-lived **workspace token**. Under the hood it's RFC 8252 loopback + PKCE (S256) — the CLI binds a one-shot listener on `127.0.0.1`, never sees your password, and never receives a long-lived secret.

```
crossdeck login
```

Flag Description

--read-only Request read-only scope — no project / app / key creation. Good for a machine that should only ever read the facts.
--scope <scope> Override the exact space-separated OAuth scope to request (advanced).
--no-browser Print the sign-in URL instead of launching a browser — for headless / remote machines. Paste it into any browser and the loopback still completes.
--base-url <url> Crossdeck API base URL. Defaults to https://api.cross-deck.com .

The rotating refresh token is written to `~/.crossdeck/credentials.json` with `0600` permissions (owner read/write only). The 1-hour access token is **never** persisted — every command trades the refresh token for a fresh one and rotates it. See [Sessions & sign-out](#sessions).

CI never runs login .

A pipeline has no browser. For CI, use the key-based [source-maps family](#sourcemaps) with a `cd_sk_live_…` secret key in an env var. The account family is for humans.

## Zero to installed — crossdeck init

One command takes a signed-in account from nothing to a working install: it creates a project, creates an app, mints the app's publishable keys, seeds origins (or lets them self-learn), and prints the SDK snippet to paste.

```
crossdeck init --name "ProLend" --platform web --domain app.prolend.com
```

Flag Description

-n, --name <name> Project name.
-P, --platform <platform> App platform: web , ios , or android . Default web .
--domain <domain> Web app domain. Optional — web origins self-learn from the first heartbeat (see below).
--bundle-id <id> iOS bundle id (for --platform ios ).
--package-name <name> Android package name (for --platform android ).
--env-file [path] Write the publishable keys to an env file (default .env ).
--base-url <url> Crossdeck API base URL.

Origins self-seed — you don't hand-configure the allowlist.

A new web app starts with an empty origin allowlist. The **first heartbeat** the SDK sends learns the calling origin and registers the correct registrable-domain wildcard (`*.prolend.com`, not the exact tenant subdomain), then admits that first beat. Pass `--domain` / `--origin` only if you want it seeded up front.

## Projects & apps

The pieces `init` orchestrates are also available directly, for adding a second project or a second app to an existing one. All writes go through the audited provisioning gate — the workspace token's scope is the boundary, and every create is recorded.

### crossdeck projects

Command Description

projects create -n <name> Create a project. Options: --business-model <model> (e.g. subscription ), --activate to make it the active project, --json .
projects list List the projects you own. --json for the raw envelope.

### crossdeck apps

Command Description

apps create -P <platform> Create an app and mint its first publishable keys. Options: -p, --project <id> (defaults to active), --domain , --bundle-id , --package-name , --origin <origin...> to seed web origins now, --json .
apps list List apps — all, or one project's with -p, --project <id> . --json supported.

### crossdeck use

Set — or, with no argument, show — the active project. Every command that takes `-p, --project` falls back to this, so you set it once and stop repeating yourself.

```
crossdeck use proj_prolend      # make it active
crossdeck use                   # show the current one
```

## Monitor the REST APIs

The read hat. These call the same `/v1` REST APIs your own code would, and print the facts. Every number is **self-describing** — it carries what it measures and how much it covers — so a count of actors is never quietly reported as a count of customers. Pass `--json` to get the raw envelope for scripting; omit it for the rendered readout.

Command Reads Notes

crossdeck revenue
GET /v1/revenue
Recognised cash for the project. -p, --project , --json .

crossdeck analytics
GET /v1/acquisition
Signup acquisition sources. -p, --project , --json .

crossdeck errors -i <fingerprint>
GET /v1/errors
One error issue stitched to the moat — who it hit, how many of them pay. --issue is the fingerprint from the Errors tab. -p, --project , --json .

Read-only, and honest about coverage.

The account family reads your live workspace; it cannot change anything. Where a metric isn't instrumented yet, the CLI says so — it never prints `0` for a blind spot. Payment rails (Stripe and the rest) connect through a separate OAuth flow in the dashboard, not a CLI command.

## Source maps (CI)

The CI family — key-based, no login. Upload `.map` files from a production build so the dashboard resolves minified frames back to original `file:line:function`. This is what the Errors tab means by "install the CLI."

```
crossdeck sourcemaps upload --release v1.2.3 \
  --url-prefix https://app.example.com/static/js/ ./dist
```

Authenticate with a secret key in an env var — the same `CROSSDECK_SECRET_KEY` every Crossdeck server SDK reads (or pass `-t, --auth-token`). The environment (sandbox vs production) is inferred from the key prefix.

Command Description

sourcemaps upload <dist-dir> Walk a build dir, pair .js with .map , POST in batches of ≤100 to /v1/releases/sourcemaps . Requires -r, --release and -u, --url-prefix . Options: -e, --environment , -t, --auth-token , -p, --project , --base-url , -v, --verbose .
upload-sourcemaps <dist-dir> Permanent alias for sourcemaps upload — the name already shipped in the Errors tab instructions. Identical behaviour.
doctor Validate auth token, environment, and API reachability without uploading anything. CI-friendly exit codes.

The full source-map workflow — bundler config, URL-prefix schemes, running alongside Sentry — lives in the [Source maps](https://cross-deck.com/docs/source-maps/index.html) reference.

## Platforms & environments

Crossdeck is multi-platform. The CLI provisions and reads across every runtime the platform supports, and every app carries both a **live** and a **test** publishable key so you never mix production and sandbox traffic.

#### Web

Live

`crossdeck init -P web`. Origins self-learn from the first heartbeat; seed with `--domain` / `--origin`.

#### iOS

Live

`crossdeck init -P ios --bundle-id com.you.app`. Device UID is keychain-anchored by the SDK.

#### Android

Soon

`-P android --package-name com.you.app` provisions today; the Android SDK follows.

Environment Publishable key prefix Used for

production cd_pub_live_… Real traffic. Minted alongside every app.
sandbox cd_pub_test_… Test traffic. Minted alongside every app.

Payment rails are a connect flow, not a CLI command.

Crossdeck supports many payment rails (Stripe, and more). You connect a rail once, through its own OAuth handshake in the dashboard — the CLI reads the revenue that flows from it, but it does not connect rails. That boundary is deliberate: connecting a rail grants long-lived access, which belongs behind the dashboard's explicit consent, not a terminal flag.

## Sessions & sign-out

`crossdeck whoami` shows the active session — the API it targets, the scopes you were granted, and your portfolio size. `crossdeck logout` ends the session on this machine.

```
crossdeck whoami
crossdeck logout
```

Sign-out is a real revocation, not just a local wipe. `crossdeck logout` revokes this machine's refresh token **server-side** (RFC 7009) and then removes the local credential file — so a stolen `~/.crossdeck/credentials.json` is dead the moment you log out, not merely absent from your disk.

Revoke any machine from the dashboard.

Every CLI sign-in appears in **Settings → Developer → Connected apps** in the dashboard, with its device, granted scopes, and last-used time. Revoke one there and that machine's next command fails cleanly with "session revoked — run `crossdeck login`." This is the answer to a lost laptop.

## Help & the command tree

`crossdeck help` prints the full command tree, grouped by the two hats, in the same premium style as the launcher. `crossdeck help ` (or `crossdeck --help`) drills into one command's flags.

```
crossdeck help              # the whole tree
crossdeck help init         # one command's flags
crossdeck init --help       # the same, commander-native
```

Group Commands

Account login , whoami , logout
Stand up the engine init , projects create , projects list , apps create , apps list , use
Read the facts revenue , analytics , errors
Source maps (CI) sourcemaps upload , upload-sourcemaps , doctor

## Security

- **The key is the boundary.** A workspace token carries an explicit scope map. Reads are allowed by scope; every **write** (create project / app / key) is authorised and audited separately. The CLI never re-implements the gate or widens a token silently.

- **No secret ever transits your clipboard.** `login` is browser-based PKCE; the CLI receives a rotating refresh token, never your password and never a long-lived secret.

- **Least privilege on disk.** The credential file is `0600`; the 1-hour access token is never written to disk.

- **Revocable everywhere.** `logout` revokes server-side; the dashboard revokes any machine. A lost device is one click from dead.

- **CI keys stay scoped.** Source-map upload needs only a `cd_sk_*` secret key; publishable `cd_pub_*` keys are client-only and are rejected for upload at config-resolve time.

## Related

- [Web SDK reference](https://cross-deck.com/docs/web-sdk/index.html) — the SDK the CLI provisions keys for.

- [Source maps](https://cross-deck.com/docs/source-maps/index.html) — the full CI upload workflow and bundler config.

- [API keys & authentication](https://cross-deck.com/docs/api-keys/index.html) — publishable vs secret vs workspace tokens, scope, and rotation.

- [Create a project](https://cross-deck.com/docs/create-a-project/index.html) — the dashboard equivalent of `crossdeck init`.

- [Source on GitHub](https://github.com/Crossdeckhq/crossdeck-cli) — the CLI lives under `sdks/cli/`.

Last updated when `@cross-deck/cli@1.4.0` shipped (Aug 3, 2026). Two families — account (OAuth) and CI (source maps) — one binary.
