| name | clerk-cli |
| description | Operate the Clerk CLI (`clerk` binary) for authentication, user/org/session management, impersonation, local webhook testing, deploy verification, instance config, env keys, feature toggles, and any Clerk Backend, Platform, or Frontend API call. Use when the user mentions Clerk management tasks, "list clerk users", "impersonate a user", "test webhooks locally", "enable orgs", "enable billing", "clerk env pull", "clerk doctor", "clerk deploy", "clerk api", or any ad-hoc Clerk API request. Prefer the CLI over raw HTTP: it handles auth, key resolution, app/instance targeting, and formatting automatically. |
| license | MIT |
Clerk CLI
The clerk binary is a pre-authenticated gateway to Clerk's Backend API and Platform API, plus project-level tooling (auth, linking, env pulls, instance config). When the user asks anything that touches a Clerk resource, reach for clerk first instead of hand-rolling curl.
This skill targets clerk latest. If clerk --version disagrees with the latest available CLI, refresh it with clerk update, or invoke the latest through a package runner such as bunx clerk@latest. The binary is always the source of truth, so run clerk <command> --help to verify anything this skill claims.
Execution environment (prefer the host, understand the sandbox warning)
Most AI coding agents default to running shell commands in a sandbox where the
user's home directory, OS keychain, browser launch, localhost callback
binding, or network access may be blocked. The Clerk CLI depends on all of
those host capabilities:
- OS credential store:
clerk auth login stores the OAuth token in the
system keychain. A sandbox without keychain access reports "not logged in"
even when the host is authenticated.
- Home-directory Clerk state: saved config, cached metadata, and fallback
credentials live under the user's Clerk config/data directories.
- Linked project metadata: resolved from the repo's git remote plus Clerk
config. Sandboxes with stripped repo state or blocked home-dir reads can
misreport "not linked".
- Local
.env* files: publishable and secret keys materialized by
clerk env pull.
- Outbound network access to Clerk: every Backend and Platform API call.
- Browser + localhost OAuth callback:
clerk auth login needs both.
In agent mode, the CLI now does a best-effort warn-once check at the
host-sensitive library boundaries. When it detects that host-only Clerk state
or system capabilities are unavailable, it emits:
Host-only Clerk state or system capabilities may be unavailable in agent mode. This may be a sandboxed run.
Re-run this command on the host shell before trusting auth, link, env, or API failures.
Treat that warning as authoritative. The command may continue, but any auth,
link, env, config, API, browser, or OAuth callback failure from that
invocation is untrusted until you rerun the same command on the host.
Prefer these commands on the user's host shell, not in a sandbox:
clerk doctor, clerk whoami, clerk auth login, clerk link, clerk env pull,
clerk apps ..., clerk config ..., clerk api ....
If a command was accidentally run in a sandbox and it reports Not logged in,
auth_required, not linked, missing env, keychain/file permission errors,
or network failures, do not treat the result as authoritative. Rerun it on
the host before acting on it or reporting it to the user.
Invoking the CLI
Before running any clerk command, figure out which binary to invoke and bind that choice for the rest of the session:
command -v clerk >/dev/null 2>&1 && clerk --version
If that prints latest or any version you trust, use bare clerk for the rest of the session.
Otherwise fall back to a package runner, in this order (matches the CLI's own preferredRunner logic, which prefers the runner that matches the project's lockfile):
| Project package manager | Invocation |
|---|
bun (bun.lock*) | bunx clerk@latest |
npm (package-lock.json) | npx -y clerk@latest |
pnpm (pnpm-lock.yaml) | pnpm dlx clerk@latest |
yarn >= 2 (yarn.lock) | yarn dlx clerk@latest |
Yarn Classic (v1) has no dlx; treat those projects as "no preferred runner" and fall back to the first runner from the list above that's on PATH.
The published npm package is clerk, not @clerk/cli. Never teach npm install -g clerk as the primary path. If the global CLI is stale or behaves differently from this skill, either upgrade the global install or fall back to the latest runner form above.
Prerequisites (run at session start)
Before running any other Clerk command in a session, verify the CLI is authenticated, linked, and healthy:
clerk --version
clerk doctor --json
Always run clerk doctor --json first. It catches the common setup failures (not logged in, project not linked, missing keys, stale CLI version) up front, so later commands don't fail with confusing errors. In agent mode it also includes a Host execution check that warns when Clerk's host-side config / credential directories are not writable, which is the canonical signal that the current invocation is likely sandboxed.
Each result has name, status (pass/warn/fail), message, optional detail, optional remedy (how to fix it), and optional fix (label for auto-fixable issues). Parse that and act on it, or surface it to the user. If Host execution warns, rerun the command on the host before trusting any auth/link/env/API failures from the same sandboxed run. Rerun clerk doctor --json whenever a later command starts misbehaving.
If clerk --version reports a newer CLI than this skill covers, trust clerk <command> --help first and refresh this skill bundle from its source.
No login required
clerk init does not require clerk auth login: an unauthenticated run mints an unclaimed app with temporary development keys — no account, no browser, no flag. Most instance commands then work on that key alone.
Two things to know before you use it: the no-account path follows whatever sk_ key is local — sk_live_ included, claimed or not — so pass --app <id> when you mean a real app; and clerk open returns a credential-equivalent claim URL, never safe to paste into a log or PR.
Which commands need an account, and the full rules: auth.md.
The mental model
| Layer | What it does | Commands |
|---|
| Session / project | Auth, link a repo to a Clerk app, pull env keys | auth login, link, unlink, whoami, env pull, doctor |
| Instance config | Manage the configuration (social providers, session lifetimes, etc.) for a specific instance | config pull, config schema, config patch, config put |
| Backend API (default) | Runtime data: users, orgs, sessions, invitations, JWT templates, webhooks | clerk api <path> |
Platform API (--platform) | Account-level: applications, instances, billing | clerk api --platform <path> |
Frontend API (--fapi) | The instance's public client-facing API (what clerk-js calls) | clerk api --fapi <path> |
A project is "linked" to an application via clerk link. Once linked, most commands auto-resolve the target app and dev instance from the repo's git remote. To target something else, pass --app <id> and/or --instance dev|prod|<instance_id>. See references/auth.md for the full resolution order.
Discover endpoints - don't memorize them
The CLI ships with the Clerk OpenAPI catalog. Always discover endpoints dynamically instead of guessing paths:
clerk api ls
clerk api ls users
clerk api ls --platform apps
Use this before clerk api <path>. If you don't see the endpoint you expected, it probably isn't exposed.
The clerk api command (the workhorse)
clerk api makes authenticated HTTP calls. It auto-resolves keys, auto-detects method from body presence, supports stdin, and can preview mutations with --dry-run.
clerk api /users
clerk api /users/user_abc123
clerk api /users?limit=5&order_by=-created_at
clerk api /users -d '{"email_address":["a@b.co"]}'
clerk api /users/user_abc123 -X PATCH -d '{"first_name":"A"}'
clerk api /users/user_abc123 -X DELETE
clerk api /users --file payload.json
cat payload.json | clerk api /users
clerk api /users/user_abc123 -X DELETE --dry-run
clerk api /users/user_abc123 -X DELETE --yes
clerk api /users --app app_abc123 --instance prod
clerk api /users --include
clerk api /v1/platform/applications --platform
clerk api --fapi /environment
In human mode, clerk api with no arguments opens an interactive request builder; in agent mode it prints usage guidance and exits 0 — always pass an endpoint (or ls) explicitly from scripts.
For instance config, prefer the dedicated clerk config ... commands over raw Platform API /config paths. They handle dry-run, diffing, and confirmation more cleanly than the raw endpoint form.
Always --dry-run a mutation before running it for real. Then re-run without --dry-run (add --yes if you're sure). In agent mode, interactive confirmation is bypassed, so --dry-run is the only safety net for destructive calls.
JSON bodies must be valid JSON. The CLI validates and rejects malformed payloads.
Endpoint paths may be given with or without /v1/ prefix - both work for Backend API calls. The CLI normalizes.
See references/recipes.md for concrete patterns: listing/filtering users, creating orgs, impersonation sessions, etc.
Inspecting large outputs (do not flood your context)
users list, apps list, config pull, and most clerk api GETs return payloads that can be many kilobytes or megabytes. Production tenants commonly have thousands of users; an instance config can be hundreds of fields deep. Reading those responses into the conversation costs context window for no benefit. Save the response to a file first, then query just what you need with jq:
clerk users list --json --limit 250 > /tmp/users.json
clerk apps list --json > /tmp/apps.json
clerk api /users/user_abc123 > /tmp/user.json
jq '.data | length' /tmp/users.json
jq '.hasMore' /tmp/users.json
jq '.data[0] | keys' /tmp/users.json
jq '.data[] | {id, email_addresses}' /tmp/users.json
jq '[.data[] | select(.banned)] | length' /tmp/users.json
If jq is not available, fall back to Python or Node - both can stream the file without printing it whole:
python3 -c 'import json; d=json.load(open("/tmp/users.json")); print(len(d["data"]), d["hasMore"])'
node -e 'const d=require("/tmp/users.json"); console.log(d.data.length, d.hasMore)'
cat / head the file only when you genuinely need to see the raw structure for one-off debugging. When walking pages, write each page to its own file (e.g. page-${offset}.json) so individual pages stay independently inspectable.
Core commands at a glance
| Command | Purpose | Key flags |
|---|
clerk init | Scaffold Clerk into a project. Unauthenticated runs write temporary development keys by default — no login required (see No login required). --starter only supports bootstrap for Next.js, React Router, Astro, Nuxt, TanStack Start, React, Vue, and JavaScript. --keyless with --app/--login, or --template/--fresh on any run that targets a real app (including while merely signed in), is a usage error — and it fires after scaffolding, so a starter run leaves the project behind. | --framework, --pm, --name (with --starter), --app, --starter, --keyless, --login, --template, --fresh, -y, --no-skills |
clerk auth login | OAuth browser login (stores token). Not required for bootstrap or no-account instance config. Agent mode: no-op if already logged in. With no stored session it still opens a browser and binds a localhost callback, so it is not unattended; prefer CLERK_PLATFORM_API_KEY for headless flows. Aliases: signup, signin, sign-in. Top-level shortcut: clerk login. | - |
clerk auth logout | Clear stored credentials. Aliases: signout, sign-out. Top-level shortcut: clerk logout. | - |
clerk whoami | Print the logged-in email. | - |
clerk link / clerk unlink | Link this repo to a Clerk app, or remove the link. unlink requires --yes in agent mode. | (see --help) |
clerk env pull | Write publishable + secret keys to the framework's env file (merge, not clobber). Resolves → framework-preferred file → ; override with . |
clerk <command> --help is the source of truth for flags. This table is a hint, not a spec. Before running an unfamiliar command or flag combination, run clerk <command> --help once per session. Every command also defines setExamples([...]) in source, which --help renders as a copy-pasteable Examples block, so you rarely need to guess syntax.
Agent-mode behavior (important)
The CLI auto-detects agent mode when stdout is not a TTY, or when --mode agent / CLERK_MODE=agent is set. In agent mode:
- Interactive prompts are disabled. Commands that would normally show pickers (
link without --app, unlink without --yes, users without a subcommand) either auto-resolve or exit with a usage error. clerk api with no args prints usage guidance and exits 0; pass an endpoint (or ls) explicitly. Always pass explicit flags (--app, --yes) in scripted calls.
- Host-sensitive operations emit a sandbox warning once per invocation. Home-directory Clerk state, keychain access, networked Clerk calls, browser launch, and localhost OAuth callback setup can trigger the warning shown above. If it appears, rerun the same command on the host before trusting the result.
- If your harness does not clearly present as agent mode, force it. Use
--mode agent or CLERK_MODE=agent when you want the CLI's non-interactive behavior and sandbox warning path to apply deterministically.
link supports deterministic agent flows. In agent mode, clerk link --app <id> links directly. Without --app, the CLI will try silent key-based autolink first; if it cannot determine the app unambiguously, it exits and tells you to pass --app.
init needs no login — do not log in first. An unauthenticated agent run mints an unclaimed app with temporary dev keys: no flag, no account, no browser. --app <id> or a pre-link targets a real app instead; --keyless forces the temporary-keys path over both a session and an existing link. A framework without temporary-key support and no app target prints manual guidance and exits cleanly. Flag exclusivity is in the command table above.
--fresh is destructive. It replaces the temporary app and overwrites the env keys and .clerk/keyless.json with no prompt, orphaning the previous app and its users. Never pass it just to re-run init.
unlink requires --yes in agent mode. It gates on isAgent() && !options.yes and exits with a usage error without it. This is the exception, not the pattern - see the next bullet.
- Only
unlink actually requires --yes. Every other confirmation gate is written as , so agent mode skips it outright: the mutation executes with no prompt and no error. Passing is harmless but changes nothing. Do not treat it as a safety gate - is the real one.
Full matrix and sandbox details in references/agent-mode.md.
Output format and errors
- JSON output:
--json on apps list and doctor. For clerk api, the response body is the raw API JSON, so pipe into jq freely.
- Exit codes:
0 success, 1 runtime error, 2 usage/validation error. doctor returns 1 if any check failed.
- Error format: User-facing errors print a single line to stderr and set a non-zero exit code. Use
--verbose for stack traces when debugging.
Safety rules for autonomous use
- Discover before acting:
clerk api ls <keyword> before clerk api <path>.
- Preview mutations:
--dry-run on every config patch, config put, api -X POST/PATCH/PUT/DELETE.
- Target explicitly in production: pass
--instance prod rather than relying on defaults, and confirm with the user before any production mutation.
- Never commit secrets:
env pull writes to .env.local (which should be gitignored). Don't paste secret keys into code or chat.
- Use
doctor --json to diagnose before assuming the CLI is broken.
References