# Linkdash CLI

Install `linkdash-cli` from npm, then run `linkdash`. Successful commands print JSON on stdout. Help and errors are plain text on stderr (exit code 1). There is no `--json` flag.

```bash
npm i -g linkdash-cli
# or: pnpm add -g linkdash-cli / bun add -g linkdash-cli / npx linkdash-cli@latest
```

HTTP equivalent: [api.md](https://linkdash.dev/api.md). MCP: [mcp.md](https://linkdash.dev/mcp.md). Agent skill: [SKILL.md](https://linkdash.dev/skills/linkdash/SKILL.md). Pricing: [pricing.md](https://linkdash.dev/pricing.md).

## Auth

```bash
linkdash auth create
linkdash auth login
linkdash auth whoami
linkdash auth logout
```

No account yet? `auth create` opens https://linkdash.dev/sign-up, then returns to https://linkdash.dev/device with the CLI code so you can approve. Already have an account? `auth login` opens /device. Headless: set `LINKDASH_TOKEN` to a session token from `auth create` or `auth login`. Do not invent tokens.

Local / non-prod:

```bash
export LINKDASH_SITE_URL=https://linkdash.dev
export LINKDASH_CONVEX_URL=https://<deployment>.convex.cloud
export LINKDASH_CONVEX_SITE_URL=https://<deployment>.convex.site
```

Session file: `~/.config/linkdash/config.json` (mode 0600).

The CLI cannot change email, password, or billing. Send the user to https://linkdash.dev/dashboard/billing or https://linkdash.dev/dashboard/settings. API keys (HTTP only): https://linkdash.dev/dashboard/keys.

## Commands

```
linkdash <command> [options]
```

| Command | What it does |
| --- | --- |
| `auth create` | Browser sign-up, then device approval |
| `auth login` | Browser device login |
| `auth logout` | Forget the stored session |
| `auth whoami` | Signed-in account JSON |
| `links list` | List links |
| `links get <id>` | One link |
| `links create --url <url>` | Short link from a URL |
| `links create --file <path>` | Upload a file and get a link |
| `links create --text <text>` | Store a snippet |
| `links update <id>` | Change destination, file, password, expiry, domain, or status |
| `links delete <id>` | Delete a link |
| `links stats <id>` | Visit, download, and play totals |
| `links export` | Print all links as CSV |
| `links import <file>` | Import URL rows from CSV |
| `webhooks list` | List webhook endpoints (secret masked) |
| `webhooks get <id>` | Show one webhook URL (secret masked) |
| `webhooks create --url <https>` | Add an HTTPS endpoint (secret shown once) |
| `webhooks rotate <id>` | Issue a new signing secret |
| `webhooks delete <id>` | Remove a webhook |
| `domains list` | List custom hostnames |
| `domains add <host>` | Add a hostname (1 on Free) |
| `domains connect <host>` | Domain Connect apply URL, or null |
| `domains verify <host>` | Check DNS with Unkey |
| `domains remove <host>` | Remove a hostname |
| `-h, --help` | Help (plain text) |
| `-v, --version` | Version |

If the same slug exists on two of the owner’s domains, pass `--domain`.

## Create and update flags

Slug is the public path. Name is only visible to you. Create takes **exactly one** of `--url`, `--file`, or `--text`.

| Flag | Notes |
| --- | --- |
| `--url <url>` | Destination URL |
| `--file <path>` | File to upload (create, or replace bytes on update) |
| `--text <text>` | Snippet |
| `--slug <id>` | Public path: `a-z0-9` and internal hyphens, 1–64 |
| `--name <label>` | Private label |
| `--tag <tag>` | Repeatable. Lowercase, no commas. Max 20 |
| `--starts <when>` | Go live after `7d`, `24h`, `30m`, or an ISO date |
| `--password <secret>` | Require this password to open |
| `--expires <when>` | Stop serving after `7d`, `24h`, `30m`, or an ISO date |
| `--max-openings <n>` | Pause after this many openings. Omit for unlimited |
| `--domain <host>` | Serve on this host instead of `lida.sh` |
| `--status <active\|paused>` | Pause or resume (update only) |
| `--preview-name <text>` | Public unfurl name |
| `--preview-description <text>` | Public unfurl description |
| `--preview-image <path>` | Public unfurl image |
| `--utm-source` `--utm-medium` `--utm-campaign` `--utm-term` `--utm-content` | URL links only |
| `--clear-password` `--clear-expires` `--clear-starts` `--clear-tags` `--clear-preview` `--clear-name` `--clear-max-openings` | Update only |

`--status` only accepts `active` or `paused`. Expired comes from `--expires`. Scheduled comes from `--starts`. Public resolve is not-found until start. After `--max-openings` is reached the link pauses; raise the number, or `--clear-max-openings`, to serve it again. Openings already used stay counted until you change or remove the limit.

`lida.sh` slugs are shared. If a word is taken, add a custom domain and retry with `--domain`. Custom domains have their own slug namespace. A branded-domain link does not also publish on `lida.sh`.

## List flags

| Flag | Notes |
| --- | --- |
| `--q <text>` | Search name, slug, target, and tags |
| `--tag <tag>` | Filter by tag |
| `--kind <url\|file\|text>` | Filter by kind |
| `--status <status>` | `active`, `paused`, `expired`, or `scheduled` |

## Stats

```bash
linkdash links stats <id> --last 24h
```

`--last` is a duration (`24h`, `7d`). Default `24h`. JSON includes `visits`, `downloads`, `plays`, `visitsInLast`, `countries`, `referrers`, and `visitors`.

## CSV

Columns: `slug,kind,name,url,text,fileName,status,domain,tags,starts,expires,maxOpenings,created`. No passwords.

```bash
linkdash links export > links.csv
linkdash links import links.csv
```

Import creates or updates URL (and text) rows by slug. File rows without bytes are skipped with a per-row error. Tags/name/starts/expires/maxOpenings/domain apply when present. Tags in CSV are `|` or `;` joined.

## Webhooks

HTTPS endpoints for `link.visit`, `link.download`, and `link.play`. Up to 10 per account. Secret is `whsec_…`, shown once on create/rotate. Keep it. Never put it in the JSON body.

```bash
linkdash webhooks create --url https://example.com/hooks
linkdash webhooks list
linkdash webhooks get <id>
linkdash webhooks rotate <id>
linkdash webhooks delete <id>
```

Headers: `svix-id`, `svix-timestamp`, `svix-signature`. Verify with `svix`. See [api.md](https://linkdash.dev/api.md#webhooks).

## Domains

Free includes 1 custom domain. Professional has 5. Growth is unlimited.

```bash
linkdash domains add docs.example.com
linkdash domains connect docs.example.com
linkdash domains verify docs.example.com
```

Point DNS (CNAME or apex ALIAS plus the TXT Unkey returns), then verify. `domains connect` prints a Domain Connect `applyUrl` when Unkey can one-click at the DNS provider. Assign with `--domain` on create/update.

## Examples

```bash
linkdash links create --url https://example.com/q4/deck.pdf --slug deck --name "Q4 deck"
linkdash links create --file ./deck.pdf --expires 7d --starts 1d
linkdash links create --url https://example.com --max-openings 1
linkdash links update k3m9xq --max-openings 5
linkdash links update k3m9xq --clear-max-openings
linkdash links create --text "release checklist" --tag launch --tag q4
linkdash links list --q deck --tag launch --kind file --status paused
linkdash links get welcome --domain go.example.com
linkdash links update deck --file ./new.pdf
linkdash links update k3m9xq --status paused
```

## Agent rules

1. Parse stdout as JSON. Do not pass `--json`.
2. Never invent flags, endpoints, or tokens.
3. Never confirm deletes unless the user asked for a confirmation step.
4. Never echo secrets (`--password`, tokens, API keys, webhook secrets).
5. If a command fails, read stderr or JSON `error` and fix flags.
6. Writes count toward the monthly event quota. Reads do not.
