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

# CLI reference

> Full reference for the Keystroke CLI.

The `keystroke` CLI is the primary way to build, run, and operate Keystroke projects. You'll use it to scaffold a project, deploy it to the platform, and then invoke and inspect what's running — and optionally run it locally for offline iteration.

Most commands run against an **API target**, either your **local** Keystroke server or your **cloud** project on the platform. Understanding [how targets resolve](#targets-local-vs-cloud) is the key to using the CLI well.

**Command shape** — resource collections use plural names (matching the HTTP API): `keystroke <collection> <verb> [<slug>]`. Examples: `keystroke workflows run greeting`, `keystroke triggers disable inbound-email-poll`, `keystroke apps execute github …`.

<Tip>
  Run `keystroke --help`, or `keystroke <command> --help`, for the authoritative list of flags on any command.
</Tip>

## Install

Install the `keystroke` command globally. Requires [Node.js](https://nodejs.org) 20 or later.

<CodeGroup>
  ```bash npm theme={null}
  npm install -g @keystrokehq/cli
  ```

  ```bash pnpm theme={null}
  pnpm add -g @keystrokehq/cli
  ```

  ```bash yarn theme={null}
  yarn global add @keystrokehq/cli
  ```

  ```bash bun theme={null}
  bun add -g @keystrokehq/cli
  ```
</CodeGroup>

## Authentication

Authentication is **shared** across targets; log in once and the token is reused for every cloud command. Switching between local, cloud, or organizations does not require logging in again.

```bash theme={null}
keystroke auth login              # browser device flow; auto-picks your org
keystroke auth login --org <slug> # log in and select a specific organization
keystroke auth status             # show the current user and active organization
keystroke auth logout             # clear stored credentials
```

| Command       | Flags                                                     | Description                              |
| ------------- | --------------------------------------------------------- | ---------------------------------------- |
| `auth login`  | `--org <slug>`, `--web-url <url>`, `--platform-url <url>` | Authenticate via the browser device flow |
| `auth status` | `--web-url <url>`                                         | Print authentication status as JSON      |
| `auth logout` | `--web-url <url>`                                         | Remove the stored access token           |

Your token is stored in your operating system's secure credential store (Keychain, Credential Manager, or secret service).

## Targets: local vs cloud

Most runtime commands (`workflows`, `agents`, `triggers`, `credentials`, `connect`, `health`) resolve a target before they run:

| Target    | Points at                                                      | Use for                                                                       |
| --------- | -------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| **Cloud** | The platform control plane → your deployed project             | The default workflow: build, deploy, then run and inspect what's deployed     |
| **Local** | A local Keystroke server (`keystroke dev` / `keystroke start`) | Optional escape hatch: offline, no-auth iteration on `src/` without deploying |

**Rule of thumb:** Keystroke is deploy-first. Deploy your project (a full deploy, or `--filter` for one module), then use the CLI against the **cloud** target to run and inspect what's live. Reach for **local** only when you want to iterate offline without deploying.

### Resolution order

The CLI picks a target in this order:

1. `--local` flag → local
2. `keystroke dev` session is running → local
3. `--project <slug>` flag → cloud (that project only)
4. `apiTarget=local` in config → local
5. `apiTarget=platform` + a linked project in `keystroke.config.ts` → cloud
6. Default → local

While `keystroke dev` is running, other commands automatically target **local** unless you pass `--project`.

### Switching targets

Global CLI config lives in `~/.keystroke` (`apiTarget`: `local` or `platform`, plus active organization). **Which platform project you deploy to** is stored in the project's `keystroke.config.ts` as optional `project` and `organization` slugs — set by `keystroke projects link` or `keystroke init --project`.

```bash theme={null}
keystroke config show                 # print effective config + linked project slugs
keystroke config use local            # route to local server
keystroke config use cloud            # route to platform API
keystroke config use org              # interactively switch active organization
keystroke config use org <slug>       # switch active organization by slug
keystroke projects link --project <slug>  # link this directory to a platform project
```

For one-off overrides that don't change your config, use the [global flags](#global-options):

```bash theme={null}
keystroke --local workflows runs list greeting   # force local, just this once
keystroke --project other-app triggers list      # hit a specific cloud project once
```

## Scaffold a project

Create a project from the default template, then [deploy](#deploy) it.

```bash theme={null}
keystroke init my-app --yes        # scaffold a project from the default template
cd my-app
keystroke projects link --project <slug>  # link this directory to a platform project
keystroke deploy                    # lint + typecheck + build + ship dist/
```

Or seed the link at init time:

```bash theme={null}
keystroke init my-app --yes --project <slug> --organization <org-slug>
```

`keystroke init` creates `keystroke.config.ts`, a committed `tsconfig.json` (extends `@keystrokehq/cli/tsconfig.json`), a `src/` directory with an example agent, and an `AGENTS.md` guide so your coding agent knows how Keystroke works. Lint, typecheck, and test tooling (oxlint, TypeScript, vitest, `@types/node`) ship inside `@keystrokehq/cli` — projects do not declare them or ship `.oxlintrc.json` / `vitest.config.ts`. `AGENTS.md` is versioned to your CLI — commands run inside the project re-sync it automatically after a CLI update. For deeper context, use [`keystroke docs search`](/cli#docs) and [`keystroke docs query`](/cli#docs).

Optionally, run a local server for offline iteration before you deploy:

```bash theme={null}
keystroke dev                      # watch src/, rebuild, restart the API
```

| Command     | Key flags                                                                                                                          | Description                                                           |
| ----------- | ---------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| `init`      | `--name <name>`, `--template <name>`, `-y, --yes`, `--skip-install`, `--pm <manager>`, `--project <slug>`, `--organization <slug>` | Create a new project from a template (`--yes` for headless/agent use) |
| `dev`       | `--dir <path>`, `--port <number>`                                                                                                  | Watch `src/`, rebuild `dist/`, and restart the API on changes         |
| `start`     | `--dir <path>`, `--port <number>`                                                                                                  | Build once and run the API server                                     |
| `build`     | `--dir <path>`                                                                                                                     | Build the project for production                                      |
| `lint`      | `--dir <path>`                                                                                                                     | Lint `src/` with bundled oxlint (config lives in the CLI)             |
| `typecheck` | `--dir <path>`                                                                                                                     | Type-check with bundled TypeScript using the project `tsconfig.json`  |
| `test`      | `--dir <path>`, `[args...]`                                                                                                        | Run tests with bundled vitest (`--project unit` or `integration`)     |
| `health`    |                                                                                                                                    | Check connectivity to the resolved API target                         |

<Note>
  `package.json` scripts (`pnpm lint`, `pnpm typecheck`, `pnpm test`) call these commands. Config files stay in the CLI bundle — nothing is written into your project when you run them.
</Note>

<Note>
  `keystroke dev` is a watch-and-restart loop, not true hot reload. On each change it rebuilds `dist/` and reboots the API so newly added agents, workflows, and triggers are discovered.
</Note>

## Deploy

Deploy runs lint and typecheck, builds your project, uploads the `dist/` artifact, and activates it on the platform. Link the directory first (or pass `--project` for a one-off deploy). The first successful deploy switches your default target to cloud. A lockfile in your project is not part of what ships — deploy uploads only the built `dist/` tree.

You do not need to run `keystroke lint`, `keystroke typecheck`, or `keystroke build` first — deploy is the gate.

```bash theme={null}
keystroke projects list                     # list projects in your organization
keystroke projects create --name "My app"   # create a deploy target
keystroke projects link --project <slug>    # write project + org slugs to keystroke.config.ts
keystroke deploy                           # build, upload, and deploy
keystroke deploy --project other-app       # one-off deploy to a different project
```

| Command                     | Key flags                                              | Description                                                            |
| --------------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------- |
| `projects link`             | `--project <slug>`, `--dir <path>`                     | Link this directory to a platform project in `keystroke.config.ts`     |
| `deploy`                    | `--dir <path>`, `--filter <entry>`, `--project <slug>` | Lint, typecheck, build, upload, and deploy the project to the platform |
| `projects list`             | `--admin`                                              | List projects in the active organization                               |
| `projects create`           | `--name <name>`, `--description <desc>`                | Create a platform project                                              |
| `projects update`           | `--name <name>`, `--description <desc>`                | Update a project's name or description                                 |
| `projects delete`           | `-y, --yes`                                            | Delete a project                                                       |
| `projects metrics`          | `--project <slug>`, `--admin`                          | Show rollup metrics for projects                                       |
| `projects deployments list` | `--project <slug>`                                     | View deployment history for a project                                  |

Pass `--filter <entry>` to build and deploy only matching module entries on top of the active artifact, useful for fast, targeted redeploys after a full deploy.

## Pull

`keystroke pull` is the inverse of `deploy`: it downloads a project's **active deploy source** into a local directory and installs dependencies. Use it to start from a project that was created or deployed elsewhere — for example, onto a fresh machine.

```bash theme={null}
keystroke pull --project <slug>             # into the current directory
keystroke pull --project <slug> --dir ./my-app
keystroke pull --project <slug> --force     # overwrite an existing local tree
```

| Command | Key flags                 | Description                                                      |
| ------- | ------------------------- | ---------------------------------------------------------------- |
| `pull`  | `--dir <path>`, `--force` | Download a project's active deploy source into a local directory |

`pull` targets the linked project in `keystroke.config.ts` when `--project` is omitted. A directory that already contains an unrelated project is refused unless you pass `--force`; a directory from a prior pull of the same project refreshes in place, and a pull that's already on the active artifact exits without changes.

## Invoke and inspect

Run your workflows and agents, fire triggers, and audit run history. These commands follow the [resolved target](#targets-local-vs-cloud): your deployed cloud project by default, or a local server if you're running one.

### Workflows

```bash theme={null}
keystroke workflows list
keystroke workflows run greeting --input '{"name":"Ada"}'
keystroke workflows runs list greeting
keystroke workflows runs get greeting <run-id> --include steps,trace
keystroke workflows runs hooks greeting <run-id>
keystroke workflows runs cancel greeting <run-id>
```

| Command                 | Key flags                                                           | Description                         |
| ----------------------- | ------------------------------------------------------------------- | ----------------------------------- |
| `workflows run`         | `--input <json>`                                                    | Invoke a workflow by key            |
| `workflows runs list`   | `--limit <n>`, `--cursor <cursor>`, `--status <s>`, `--trigger <t>` | List runs for a workflow            |
| `workflows runs get`    | `--include <parts>` (`trigger,steps,trace`)                         | Full details for a single run       |
| `workflows runs hooks`  |                                                                     | Hook tokens + resume URLs for a run |
| `workflows runs cancel` | `<workflow> <run-id>`                                               | Cancel a running or queued run      |

### Agents

```bash theme={null}
keystroke agents list
keystroke agents prompt support --message "Hi"
keystroke agents prompt support --message "and then?" --session-id <session-id>
keystroke agents sessions get support <session-id> --include messages,trace
```

| Command                  | Key flags                                                          | Description                                |
| ------------------------ | ------------------------------------------------------------------ | ------------------------------------------ |
| `agents prompt`          | `--message <text>`, `--session-id <id>`                            | Send a message (or follow up in a session) |
| `agents sessions list`   | `--limit <n>`, `--cursor <cursor>`, `--status <s>`, `--source <s>` | List sessions for an agent                 |
| `agents sessions get`    | `--include <parts>` (`gateway,messages,events,trace`)              | Full details for a single session          |
| `agents sessions cancel` | `<agent> <session-id>`                                             | Cancel a running session                   |

### Actions

Discover and run catalog actions with a single `<app> <tool>` shape at every step — find → inspect → run. Credentials stay in the cloud; the CLI proxies execution through `/mcp/execute`, so there's no local server or project code required.

```bash theme={null}
keystroke apps actions list github --search user                       # find tool slugs for an app
keystroke apps actions get github github_get_the_authenticated_user    # inspect schema + how to run/import
keystroke apps execute github github_get_the_authenticated_user   # run it
keystroke apps execute acculynx acculynx_add_job_appointment --input '{"jobId":"123","startDate":"2026-01-01T09:00:00Z","endDate":"2026-01-01T10:00:00Z"}'
```

`apps actions list` and `apps actions get` print next steps (run ad-hoc, import into code, connect) after their output. Slugs are case-insensitive. If the app is not connected, `apps execute` exits with a hint to run `keystroke connect <app>`.

Credential resolution matches the runtime: it uses the **project default**, then the **org default**. Project credentials only resolve when you target a project with the global `--project <slug>` flag (or run inside a linked project); without it, only org credentials resolve. **User credentials are never a default** — reach one explicitly with `--credential <slug>`.

```bash theme={null}
keystroke apps execute github github_get_the_authenticated_user                   # org default
keystroke apps execute github github_get_the_authenticated_user --project my-app   # project default (falls back to org)
keystroke apps execute github github_get_the_authenticated_user --credential work-github  # pin a specific instance by slug
```

Pass `--credential <slug>` to pin a specific instance when a scope has more than one — the slug is the one you set with `keystroke credentials create <key> --slug <slug>` and can see with `keystroke credentials list`. It's also the only way to use a user-scoped credential.

| Command        | Key flags                                                                          | Description                                                              |
| -------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| `apps execute` | `--input <json>`, `--version <version>`, `--credential <slug>`, `--project <slug>` | Run a catalog action as `<app> <tool>` (version defaults to the catalog) |

### Triggers

```bash theme={null}
keystroke triggers list
keystroke triggers list --endpoint stripe   # webhooks on a shared endpoint
keystroke triggers url incoming-message     # print a webhook URL
keystroke triggers poll inbox-sync          # run a poll trigger on demand
keystroke triggers runs list inbound-email-poll
keystroke triggers runs list inbound-email-poll --workflow signup-pipeline
keystroke triggers disable signup                        # pause the trigger (all attachments)
keystroke triggers disable signup --workflow signup-pipeline   # pause just one attachment
keystroke triggers enable signup                         # resume
```

| Command              | Key flags                                                                            | Description                                          |
| -------------------- | ------------------------------------------------------------------------------------ | ---------------------------------------------------- |
| `triggers list`      | `--endpoint <endpoint>`                                                              | List discovered triggers                             |
| `triggers get`       |                                                                                      | Get a trigger by key, or all webhooks on an endpoint |
| `triggers url`       |                                                                                      | Print the webhook URL for a trigger                  |
| `triggers poll`      | `--group`, `--workflow <key>`, `--attachment <key>`                                  | Run a poll trigger on demand                         |
| `triggers disable`   | `--workflow <slug>`, `--agent <slug>`                                                | Pause a trigger — all attachments, or one target     |
| `triggers enable`    | `--workflow <slug>`, `--agent <slug>`                                                | Resume a trigger — all attachments, or one target    |
| `triggers runs list` | `--limit <n>`, `--cursor <cursor>`, `--trigger-type <type>`, `--workflow`, `--agent` | List runs for a trigger                              |
| `triggers runs get`  | `--include <parts>` (`workflows,trace`), `--workflow`, `--agent`                     | Full details for a single trigger run                |

### History (cloud)

```bash theme={null}
keystroke history list --kind workflow --status failed
keystroke history get <run-id>
```

| Command        | Key flags                                                    | Description                                   |
| -------------- | ------------------------------------------------------------ | --------------------------------------------- |
| `history list` | `--project <id>`, `--status <s>`, `--kind <workflow\|agent>` | List runs across projects in the organization |
| `history get`  |                                                              | Get details for a single run                  |

## Integrations and secrets

Connect third-party accounts and manage the credentials your agents and workflows use.

```bash theme={null}
keystroke apps list                                  # apps registered in your org
keystroke apps search github                         # search the live catalog
keystroke apps actions list github --search issue         # actions an app exposes
keystroke connect google                            # connect an app (web flow)
keystroke credentials create exa --set apiKey=@env:EXA_API_KEY
keystroke credentials list
```

| Command                         | Key flags                                                                                                                             | Description                                                    |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| `apps list`                     |                                                                                                                                       | List apps registered/connectable in your org                   |
| `apps search <query>`           | `--category <c>`, `--limit <n>`, `--cursor <c>`                                                                                       | Search the live catalog                                        |
| `apps get <slug>`               |                                                                                                                                       | Get details for one catalog app                                |
| `apps actions list [slug]`      | `--search <q>`                                                                                                                        | List an app's actions (or global search with no slug)          |
| `apps actions get <app> <tool>` |                                                                                                                                       | Full input/output schema for one action, plus run/import hints |
| `apps execute <app> <tool>`     | `--input <json>`, `--version <version>`, `--credential <slug>`, `--project <slug>`                                                    | Run a connected catalog action against the cloud               |
| `apps create`                   | `--name`, `--slug`, `--description`, `--field <f>`, `--mcp <url>`, `--openapi <url>`, `--graphql <url>`, `--auth <kind>`, `--preview` | Create a custom app (manual fields or auto-detect from URL)    |
| `apps sync <slug>`              | `--dir <path>`                                                                                                                        | Write `src/apps/<name>/app.ts` from the platform template      |
| `connect <slug>`                | `--print-url`                                                                                                                         | Open the web flow to connect an app (scope chosen there)       |
| `credentials list`              | `--key <key>`                                                                                                                         | List stored credential instances                               |
| `credentials get`               |                                                                                                                                       | Show a credential instance                                     |
| `credentials create`            | `--set <field>`, `--scope <scope>`, `--project-slug <slug>`, `--label <label>`, `--default`                                           | Create an `api_key` credential instance                        |
| `credentials update`            |                                                                                                                                       | Update a credential instance                                   |
| `credentials rotate`            |                                                                                                                                       | Rotate a credential's key                                      |
| `credentials delete`            |                                                                                                                                       | Delete a credential instance                                   |

`connect` requires an app slug (see `keystroke apps list` for connectable slugs) and chooses scope in the web flow — scope and project pinning are set on `credentials create`, not `connect`. For credential values, `--set key=value` takes a literal, and `--set key=@env:VAR` reads from your shell (falling back to the project `.env`). `apps create`'s `--field` syntax is `key`, `key:secret`, `key:optional`, or `key:secret:optional` (repeatable). For manual custom apps, at least one `--field` is required. With `--mcp`, `--openapi`, or `--graphql`, auth and credential fields are auto-detected from the URL; use `--preview` to print the assembled request without creating, and `--name`, `--slug`, `--description`, `--field`, or `--auth` (MCP only) to override detected values.

## Channels

Bind agents to external channels (like Slack) so people can talk to them where they already work.

```bash theme={null}
keystroke channels platforms          # list supported channel platforms
keystroke channels directory          # browse bindable channels
keystroke channels list               # list current bindings
keystroke channels bind ...           # bind an agent to a channel
```

## Organization and access

Manage members and platform API keys for your active organization. Most of these are org-admin operations.

```bash theme={null}
keystroke organization members list
keystroke organization members invite --email teammate@acme.com --role builder
keystroke api-key create --name "CI"
keystroke api-key list
```

| Command                       | Key flags                          | Description                           |
| ----------------------------- | ---------------------------------- | ------------------------------------- |
| `organization update`         | `--name <name>`                    | Rename the active organization        |
| `organization members list`   |                                    | List members                          |
| `organization members invite` | `--email <email>`, `--role <role>` | Invite members (`admin` or `builder`) |
| `organization members role`   | `--role <role>`                    | Change a member's role                |
| `organization members remove` |                                    | Remove a member                       |
| `api-key list`                |                                    | List org-scoped platform API keys     |
| `api-key create`              | `--name <name>`                    | Create an API key                     |
| `api-key revoke`              | `-y, --yes`                        | Revoke an API key                     |

## Docs

Read the Keystroke documentation from the CLI. These commands need **no authentication and no project** — they work the moment the CLI is installed, for you and your coding agent alike.

```bash theme={null}
keystroke docs search "webhook trigger"      # find pages by topic
keystroke docs query "cat /quickstart.mdx"   # read a page by path
```

| Command                | Description                                                                             |
| ---------------------- | --------------------------------------------------------------------------------------- |
| `docs search <query>`  | Search the docs for relevant pages and examples                                         |
| `docs query <command>` | Read the docs filesystem with a shell-style command (`cat`, `rg`, `ls`, `tree`, `head`) |

See [docs for agents](/build-with-ai/docs-for-agents) for how `query` works (stateless calls, output caps) and more examples.

## Global options

These flags work on every command:

| Flag               | Description                                                                     |
| ------------------ | ------------------------------------------------------------------------------- |
| `--project <slug>` | Target a specific cloud project for this command (does not change your default) |
| `--local`          | Force the local Keystroke server for this command                               |
| `--version`        | Print the CLI version and exit                                                  |
| `--help`           | Show help for the CLI or a specific command                                     |

<Card title="Using the CLI with AI agents" href="/build-with-ai/cli-for-agents">
  Keystroke is built for coding agents. Learn how agents use the CLI, the docs commands, and headless mode to build and operate your project.
</Card>
