---
url: https://saas.dev/docs/cli
md: https://saas.dev/docs/cli.md
---

# CLI

`npx leaseclock show leases` lists leases, and `npx leaseclock do Lease.renew 4b --endsOn 2027-11-30` renews one. Your team and your customers' scripts use the same commands, with the same permissions as the app.

## In short

[A terminal running npx leaseclock show and do](https://leaseclock.com/docs/cli)

- The CLI ships in the `leaseclock` package on npm, under your product's name. `npx leaseclock` runs it with nothing to install.
- `show` reads records. The words after it are the API path, so `show leases 4b` returns what `GET leaseclock.com/api/leases/4b` returns.
- `do` runs a Verb. Every Verb in your definition is a command, and every Noun also has `create` and `update`.
- Each `do` previews the change and asks before it runs, and `undo` reverses it.
- A token carries its owner's role and workspace, so the CLI can do exactly what that person can do in the app.

## Leaseclock in a terminal

Priya Shah, the leasing coordinator at Northgate Rentals, runs [Leaseclock](https://leaseclock.com) from her terminal. She checks which leases are active and renews Dana Whitfield's lease on Unit 4B for another year.

```bash title="Terminal"
$ npx leaseclock show leases --status active
UNIT  RENTER                        ENDS ON
4B    dana.whitfield@example.com    2026-11-30
2A    marcus.bell@example.com       2026-12-15
7C    lena.okafor@example.com       2027-01-31
3D    tomas.rivera@example.com      2027-03-31
1F    owen.price@example.com        2027-06-30

$ npx leaseclock do Lease.renew 4b --endsOn 2027-11-30
Preview  Lease 4B  endsOn 2026-11-30 → 2027-11-30
Renew it? y
✓ Renewed lease 4B. It now ends 2027-11-30.
  Undo: npx leaseclock undo Lease.renew 4b
```

The leases come back sorted by end date, because `endsOn` is indexed in `Lease.mdx`. The same renewal is a button in the app, a `POST` in the [API](https://saas.dev/docs/api.md) and a `do` call in [MCP](https://saas.dev/docs/mcp.md), and it lands in the lease's history the same way from each.

## What each definition line makes

Each command, flag and help line comes from a line of Leaseclock's definition.

| In `Lease.mdx` | In the CLI |
| --- | --- |
| `name: Lease` | `show leases`, `show leases 4b` and `do Lease.…` |
| `endsOn: { type: date, required: true, indexed: true }` | Lists sorted by end date, and the `--endsOn.before` and `--endsOn.after` filters |
| `status: { type: choice, options: [{ key: active }, { key: ended }] }` | `--status active`, with any other value rejected before the request is sent |
| `input: { endsOn: { says: the new end date, type: date, required: true } }` | `do Lease.renew <lease> --endsOn <date>`, which stops when `--endsOn` is missing |
| `from: [active]` | `renew` runs only on an active lease |
| `does: Move the end date forward` | The first line of `help Lease.renew`, and `says` gives the flag its description |

```bash title="Terminal"
$ npx leaseclock help Lease.renew
Lease.renew: Move the end date forward

  npx leaseclock do Lease.renew <lease> --endsOn <date>

  --endsOn <date>   the new end date (required)

Runs on: active
```

## Sign in

```bash title="Terminal"
npx leaseclock login
```

`login` opens Leaseclock's sign-in in the browser and stores the token on the machine. Every later command uses it.

Scripts and CI read the token from `LEASECLOCK_TOKEN` instead. Create one in the Leaseclock app under **Settings → API keys**. It carries the role of the person who created it, and it can be revoked there at any time.

## Read records

`show` takes a Noun's plural name, and optionally one record's id.

```bash title="Terminal"
$ npx leaseclock show leases 4b
Lease 4B
  unit     4B · Alder Court
  renter   dana.whitfield@example.com
  endsOn   2027-11-30
  status   active
  Verbs    renew, end
```

Any field filters by its value: `--status active`, `--unit 4b`. An indexed field also takes `.before` and `.after`:

```bash title="Terminal"
npx leaseclock show leases --status active --endsOn.before 2027-01-01
```

A list returns up to 200 records. Pass `--limit` for fewer, or `--all` to page through every match.

## Run a Verb

`do` takes `Noun.verb`, the record's id and the Verb's input as flags.

```bash title="Terminal"
npx leaseclock do Lease.renew 4b --endsOn 2027-11-30
npx leaseclock do Lease.end 7c
npx leaseclock do Unit.create --label 6E --building "Birch House"
npx leaseclock do Lease.update 2a --renter m.bell@example.com
```

Before anything changes, `do` prints the preview and asks to confirm. `--preview` prints it and stops. When a Verb cannot run, the CLI says why, links the fix and exits non-zero:

```bash title="Terminal"
$ npx leaseclock do Lease.renew 5a --endsOn 2027-09-30
✗ Lease 5A is ended, and renew runs only on an active lease.
  See https://leaseclock.com/docs/lease#renew
```

## Undo and history

Every `do` is recorded as a version of the record. `undo` reverses the last run of a Verb, and `history` lists every change with who made it and from where.

```bash title="Terminal"
$ npx leaseclock history leases 4b
2026-10-25 14:02  renew   endsOn 2026-11-30 → 2027-11-30   priya@northgaterentals.com  CLI
2025-11-12 09:40  create                                      priya@northgaterentals.com  App

$ npx leaseclock undo Lease.renew 4b
Preview  Lease 4B  endsOn 2027-11-30 → 2026-11-30
Undo the renewal? y
✓ Lease 4B ends 2026-11-30 again.
```

## Use it in scripts

`--json` prints each record on its own line, as the same JSON the API returns. `--yes` confirms without a prompt. Without a terminal to confirm in, `do` prints the preview and stops, so a script changes nothing unless it passes `--yes`.

```bash title="renewals-due.sh"
# Who to call this week: every active lease ending before the new year.
npx leaseclock show leases --status active --endsOn.before 2027-01-01 --json \
  | jq -r '.renter'
```

```text title="Output"
dana.whitfield@example.com
marcus.bell@example.com
```

In CI, pass the token as a secret:

```yaml title=".github/workflows/renewals.yml"
- run: npx leaseclock show leases --status active --endsOn.before 2027-01-01 --json
  env:
    LEASECLOCK_TOKEN: ${{ secrets.LEASECLOCK_TOKEN }}
```

## Who can run what

The CLI checks the same roles as the app, on every command.

- **Your customers** see only their own workspace. A token from Northgate Rentals lists Northgate's leases and nobody else's.
- **A viewer** can `show` and `history`. A member, admin or owner can also `do` and `undo`.
- **Your team** signs in as Leaseclock's own admins and picks a customer with `--workspace`, as in `npx leaseclock show leases --workspace northgate-rentals`.
- **Offer limits** hold in the CLI too. On Leaseclock Pro, `do Unit.create` stops at 200 Units with a link to upgrade.

## Commands

| Command | What it does |
| --- | --- |
| `login` | Signs in through the browser and stores the token |
| `show <nouns> [id]` | Lists records, or shows one |
| `do <Noun.verb> [id] [--input value]` | Runs a Verb, after a preview |
| `undo <Noun.verb> <id>` | Reverses the last run of that Verb on that record |
| `history <nouns> <id>` | Lists every change to a record |
| `help [Noun.verb]` | Lists every command, or explains one Verb |

| Flag | What it does |
| --- | --- |
| `--json` | Prints records as the API's JSON, one per line |
| `--yes` | Confirms without a prompt |
| `--preview` | Prints the preview and runs nothing |
| `--workspace <slug>` | Picks a customer's workspace, for your team |
| `--limit <n>, --all` | Sets how many records a list returns |

A Test key reaches the Test copy, with its own records and Stripe's test mode, and a Live key reaches Live, so the same command works in both.

## Questions

### Does anyone need to install it?

No. `npx leaseclock` downloads and runs it. Anyone who uses it daily can run `npm i -g leaseclock` and type `leaseclock` instead. The package also holds the [SDK](https://saas.dev/docs/sdk.md).

### How is it different from `npx saas.dev`?

[`npx saas.dev`](https://saas.dev/docs/saas-dev-cli.md) is for you: it creates your product and pushes its definition. `npx leaseclock` is your product's own CLI, for your team and your customers to work with records. Nothing in it names SaaS.Dev.

### What happens when I add a Verb?

Push the file and the Verb is a command. Add `pause` to Lease, and `npx leaseclock do Lease.pause 4b` runs on the next call, with its own help line, preview and undo.

### Should my customers' agents use the CLI or MCP?

MCP. An agent connected to `leaseclock.com/mcp` gets `search`, `fetch` and `do` as tools, with no shell to run. The CLI is for people at a terminal and for scripts. See [MCP](https://saas.dev/docs/mcp.md).
