AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
The Clerk CLI is a powerful tool for interacting with Clerk's Backend and Platform APIs. It streamlines development workflows by handling authentication, environment key management, and instance configuration without requiring manual HTTP requests. It is designed to be used in both local development and automated environments, providing a structured way to manage users, organizations, and security settings.
What it does
- Manages user, organization, and session data via the Backend API.
- Handles instance configuration, including social providers and security settings.
- Automates environment key synchronization and local webhook testing.
- Provides diagnostic tools to verify project linking and CLI health.
- Supports secure impersonation for testing and debugging user flows.
- Enables platform-level management of applications and billing.
When to use it
- When you need to perform administrative tasks like listing or modifying users.
- When setting up or verifying environment variables for a Clerk-integrated project.
- When auditing or hardening the security configuration of a Clerk instance.
- When debugging API interactions or testing webhooks in a local environment.
- When you need to perform bulk operations or scripted management of Clerk resources.
Advantages
- Reduces manual effort by automating authentication and key resolution.
- Provides a consistent, structured interface for both Backend and Platform APIs.
- Includes safety features like dry-run modes for destructive operations.
- Integrates seamlessly into CI/CD pipelines and automated agent workflows.
- Offers built-in diagnostic commands to quickly resolve configuration issues.
Usage examples
- “List the first 50 users in my Clerk instance.”
- The agent runs 'clerk users list --json --limit 50' and processes the output to display the requested user information.
- “Check if my project is correctly linked to Clerk.”
- The agent executes 'clerk doctor --json' to verify the project's link status and overall health, reporting any issues found.
- “Pull the latest environment keys for my project.”
- The agent runs 'clerk env pull' to update the local .env.local file with the current project keys.
- “Dry-run a patch to my instance configuration.”
- The agent executes 'clerk config patch --dry-run' with the specified changes to preview the impact before applying them.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/clerk/clerk-cli/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip clerk-cli.zip -d .agents/skills
Documentation by Clerk
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.
For setup or init requests, use the clerk-setup skill as the source of truth. Its package-runner, accountless, migration, and init-first guidance takes precedence over the general operational prerequisites below.
This skill targets clerk
latest. Ifclerk --versiondisagrees with the latest available CLI, refresh it withclerk update, or invoke the latest through a package runner such asbunx clerk@latest. The binary is always the source of truth, so runclerk <command> --helpto 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 loginstores 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 byclerk env pull.Outbound network access to Clerk: every Backend and Platform API call.
Browser + localhost OAuth callback:
clerk auth loginneeds 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:
# 1. Prefer a globally installed binary when it matches the skill's target version.
command -v clerk >/dev/null 2>&1 && clerk --versionIf 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 for non-setup operations)
Except when following clerk-setup, verify the CLI is authenticated, linked, and healthy before running other Clerk commands in a session:
clerk --version # confirm the binary is on PATH
clerk doctor --json # structured health check; exit 1 if anything failedAlways 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. The reverse also applies: --accountless and the accountless JSON keys ship in CLI 3.3+ — on an older CLI, use the --keyless alias or upgrade.
Accountless setup
clerk init does not require clerk auth login: on a framework with accountless support, an unauthenticated bootstrap with no --app, or an unauthenticated agent run with no --app or existing project link, mints an unclaimed accountless app with temporary development keys — no account, no browser, no flag. (A signed-out human re-running it in an existing project gets the login flow instead — --accountless forces the temporary-keys path.) Most instance commands then work on that key alone.
Two things to know before you use it: the accountless 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 # list every Backend API endpoint
clerk api ls users # filter by keyword (matches path, summary, tag, operationId)
clerk api ls --platform apps # list Platform API endpointsUse 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.
# GET requests
clerk api /users # list users
clerk api /users/user_abc123 # fetch one
clerk api /users?limit=5&order_by=-created_at # query params work inline
# Mutating requests
clerk api /users -d '{"email_address":["[email protected]"]}' # POST (auto-detected from body)
clerk api /users/user_abc123 -X PATCH -d '{"first_name":"A"}'
clerk api /users/user_abc123 -X DELETE
# Body from file or stdin
clerk api /users --file payload.json
cat payload.json | clerk api /users
# Always preview mutations first
clerk api /users/user_abc123 -X DELETE --dry-run
clerk api /users/user_abc123 -X DELETE --yes # skip confirmation once you've verified
# Target a specific app/instance
clerk api /users --app app_abc123 --instance prod
# Include response headers when debugging
clerk api /users --include
# Platform API (account-level, not tenant data)
clerk api /v1/platform/applications --platform
# Frontend API (the instance's public client-facing API — what clerk-js calls.
# Unauthenticated; --fapi and --platform cannot be combined, --secret-key is ignored)
clerk api --fapi /environmentIn 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:
# 1. Persist the response. Use --limit 250 to maximize page size for users list.
clerk users list --json --limit 250 > /tmp/users.json
clerk apps list --json > /tmp/apps.json
clerk api /users/user_abc123 > /tmp/user.json
# 2. Inspect only what you need.
jq '.data | length' /tmp/users.json # current page size
jq '.hasMore' /tmp/users.json # are more pages available?
jq '.data[0] | keys' /tmp/users.json # discover the user shape once
jq '.data[] | {id, email_addresses}' /tmp/users.json # project to a few fields
jq '[.data[] | select(.banned)] | length' /tmp/users.json # aggregate without reading rowsIf 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.
Commands
Read the command catalog when choosing a subcommand or flag. Confirm unfamiliar syntax with clerk <command> --help.
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 (
linkwithout--app,unlinkwithout--yes,userswithout a subcommand) either auto-resolve or exit with a usage error.clerk apiwith no args prints usage guidance and exits 0; pass an endpoint (orls) explicitly. Always pass explicit flags in scripted calls:--yes, and--appfor an app the person named that's already in the account.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 agentorCLERK_MODE=agentwhen you want the CLI's non-interactive behavior and sandbox warning path to apply deterministically.linksupports deterministic agent flows. In agent mode,clerk link --app <id>links directly. Without--app, newer CLIs signed in to an account first claim the appclerk initcreated in this folder; otherwise the CLI tries silent key-based autolink, and if it cannot determine the app unambiguously, it exits with a usage error.If a Clerk CLI command returns an error with code
not_linked, follow When the folder isn't linked. It covers sandbox checks, unclaimed apps, competing claim files, and older CLIs. Do not assumeclerk linkis the next step.For
init, followclerk-setup. It owns framework-support, accountless, app-linking, migration, and login-order guidance so setup instructions have one source of truth.--freshis destructive. It replaces the temporary app and overwrites the env keys and.clerk/keyless.jsonbreadcrumb with no prompt, orphaning the previous app and its users. Never pass it just to re-runinit.A few commands require
--yesin agent mode.unlink,security fix(unless--dry-run), anddisable orgswhile organization billing is on exit with a usage error without it.updatewithout--yesprints a hint and exits0without updating.Every other confirmation is skipped in agent mode.
config patch/put,apimutations,users create, andenable/disableexecute with no prompt and no error. Passing--yesis harmless but changes nothing. Do not treat it as a safety gate ---dry-runis the real one.Before
security fix, tell the user what changes. Several fixes add a CAPTCHA, verification, second-factor, or passkey step, or force a password reset; findings with acustomFlowsnote break custom sign-in flows until they are updated.mfaandpasswordless-authare product decisions: ask which--factors/--strategyrather than picking. See Audit and harden security.impersonaterequires the[user]positional in agent mode. If a search term matches multiple users, it exits2listing candidate user IDs — retry with a specificuser_...ID. Output is a JSON object ({url, id, userId, actor, ...}); surfaceurlto the user and captureid— it is the only chance to record the revoke handle.webhooks listenis long-running. Run it in the background. In agent mode (or with--json) it emits NDJSON: onereadyline ({type:"ready", relay_url, forward_to}), then oneeventline per delivery — each event line can be fed back toclerk webhooks verify --delivery.doctor --fixis ignored. Parsedoctor --jsonoutput'sremedyfield and act on it yourself.apps listandapps createdefault to JSON when piped.usersdefaults to JSON when piped, likeapps.clerk users listandclerk users createemit JSON in agent mode. Bareclerk users(no subcommand) is a usage error in agent mode - passlist,create, oropenexplicitly.clerk users openrequires theuser-idpositional in agent mode and prints a JSON descriptor instead of launching a browser.deploy --domainneeds a domain the user owns, anddeploy statusis the gate. Ask the user for the domain before runningclerk deploy --domain <domain>. When the wizard is needed (Apple's private key, or the user prefers it), have the user runclerk deployin a separate terminal from the project directory, never through your session or Claude Code's!shell escape. Verify withclerk deploy status --mode agent.deployhas no--appflag: to deploy a specific app, link the folder first withclerk link --app <id>. See Deploy to production.--input-json <json|@file|->expands JSON into flags on any command (e.g.clerk init --input-json '{"framework":"next","yes":true}'). Stdin needs the explicit-marker (echo '{"yes":true}' | clerk init --input-json -); bare piped stdin is not auto-detected, so shell loops and self-reading commands (cat body.json | clerk api …) are untouched. Place--input-jsonafter the leaf subcommand. Full rules in references/agent-mode.md.
Full matrix and sandbox details in references/agent-mode.md.
Output format and errors
JSON output:
--jsononapps listanddoctor. Forclerk api, the response body is the raw API JSON, so pipe intojqfreely.Exit codes:
0success,1runtime error,2usage/validation error.doctorreturns1if any check failed.Error format: User-facing errors print a single line to stderr and set a non-zero exit code. Use
--verbosefor stack traces when debugging.
Safety rules for autonomous use
Discover before acting:
clerk api ls <keyword>beforeclerk api <path>.Preview mutations:
--dry-runon everyconfig patch,config put,security fix,api -X POST/PATCH/PUT/DELETE.Target explicitly in production: pass
--instance prodrather than relying on defaults, and confirm with the user before any production mutation.Never commit secrets:
env pullwrites to.env.local(which should be gitignored). Don't paste secret keys into code or chat.Use
doctor --jsonto diagnose before assuming the CLI is broken.Confirm before impersonating in production:
clerk impersonatebypasses the user's MFA and may count against the impersonation quota.
References
references/auth.md - auth flow, key resolution order, host-vs-sandbox behavior,
--app/--instancetargeting, Backend vs Platform API.references/recipes.md - copy-pasteable recipes for common Clerk tasks.
references/agent-mode.md - agent-mode behavior matrix, sandbox warning semantics, exit codes, error format.
references/commands.md - command catalog with each command's purpose and key flags.
Files
- ATTRIBUTION.mdAdded by AutoGineer753 B
- SKILL.md20.8 KiB
- references/agent-mode.md40.5 KiB
- references/auth.md14.0 KiB
- references/commands.md19.1 KiB
- references/recipes.md11.7 KiB
references/agent-mode.md
Clerk CLI - Agent mode reference
The Clerk CLI has a first-class "agent" mode that's designed for non-interactive and AI-driven use. Read this before writing scripts or letting an LLM drive the CLI.
Sandbox warning semantics
Agent mode and sandboxing are related but not identical:
Agent mode controls non-interactive behavior.
Sandboxing controls whether the CLI can actually reach host-only Clerk state and host system capabilities.
In agent mode, the CLI now performs a best-effort warn-once check at the host-sensitive integration boundaries. The first time an invocation hits a blocked host capability, 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 still continue and return an ordinary Clerk error, but any auth/link/env/config/API/browser/OAuth failure from that invocation should be treated as suspect until rerun on the host.
The warning can be triggered by:
home-directory Clerk config / credential file access
OS keychain access
outbound Clerk network requests
browser launch
localhost callback server binding for OAuth
If your harness does not obviously look non-interactive, force agent behavior
with --mode agent or CLERK_MODE=agent so the CLI's non-interactive and
sandbox-warning paths apply deterministically.
How agent mode is detected
Priority (first match wins):
--mode agentflag on the command lineCLERK_MODE=agentenvironment variableStdout is not a TTY (piped, redirected, or running under an agent harness)
Force human mode with --mode human or CLERK_MODE=human. Typical AI-agent invocations automatically land in agent mode because stdout is piped.
What changes in agent mode
| Behavior | Human mode | Agent mode |
|---|---|---|
Interactive pickers (link without --app, api with no args) | Show a TUI picker | Print structured guidance and exit, or auto-resolve |
clerk link --app <id> | Links directly | Links directly |
clerk link without --app | Interactive picker / create UI | Tries silent autolink from detected publishable keys; if no deterministic match exists, exits with a usage error. The next step depends on what the folder holds: see auth.md |
unlink confirmation | Prompt y/n | Requires --yes; exits with a usage error without it |
security fix confirmation | Prompt y/n (no ids: checklist picker) | Requires --yes unless --dry-run, and ids or --all; mfa / passwordless-auth need --factors / --strategy. Exits with a usage error otherwise |
Other mutation confirmations (config patch / put, api -X POST/PATCH/DELETE, users create, enable / disable) | Prompt y/n | Silently skipped - the mutation executes. These gates are isHuman() && !options.yes, so agent mode bypasses them entirely: --yes is neither required nor meaningful, and nothing errors. --dry-run is the only safety net |
clerk doctor --fix | Interactively offers fixes | Ignored; output the remedy field and let the caller act |
clerk apps list default output | Table | JSON (when piped) |
clerk apps create <name> output | Human-readable summary | JSON (auto-detected, same as apps list); --json also works explicitly |
clerk users list / clerk users create default output | Table / human-readable | JSON (auto-detected when piped); --json also works explicitly |
clerk users (no subcommand) | Interactive action picker | Prints the action list and exits with a usage error (code 2) - pass list / create / open |
clerk users open [user-id] | Picks a user interactively, opens browser | Requires user-id; prints {url, appId, appName, instanceId, instanceLabel, userId, opened: false} and does not open a browser |
clerk open [subpath] | Opens the browser to the URL | Does not open a browser. Prints a JSON descriptor ({url, appId, appName, instanceId, instanceLabel, subpath, opened: false}) on stdout so the agent can surface it |
clerk impersonate [user] | Picker when [user] omitted; confirms; prints URL + revoke hint | Requires the [user] positional (usage error 2 without it). Ambiguous search terms exit 2 listing candidate user IDs - retry with a user_... ID. Never opens a browser. Prints one JSON object {url, id, userId, actor, appId, appLabel, instanceId, instanceLabel, expiresInSeconds} on stdout - capture id; it is the only chance to record the revoke handle |
clerk webhooks listen --forward-to <url> | Banner + one formatted line per delivery | NDJSON on stdout: one {type:"ready", relay_url, forward_to} line, then one event line per delivery (feed lines to webhooks verify --delivery), plus {type:"reconnecting"} if the relay drops. Long-running - run it in the background |
clerk deploy | Interactive production deploy wizard | Without --domain, prints the report on stdout and exits 0 for linked projects. With --domain <domain>, creates the production instance (or resumes the existing one on that domain) and prints the report. Never prompts, writes OAuth credentials, triggers DNS checks, or polls. |
clerk deploy status | Verify production deploy state | Read-only verification gate. Triggers one DNS check for active production domains, waits briefly, reads one live status snapshot, prints the report on stdout, exits 0 when complete and 1 when incomplete. It does not keep waiting or back off in agent mode unless --wait is passed. Use --wait when the user asks the agent to keep waiting for DNS, SSL, email DNS, or final Clerk-side readiness. |
clerk auth login when already authenticated | Prompt to re-auth (newer CLIs, with an init token: ask whether to claim it into the account or workspace it names) | Skips the browser, but tries to claim the app behind .clerk/keyless.json in this folder |
clerk auth login when not authenticated | Opens a browser, binds a localhost callback | Identical - it still opens a browser and binds a localhost callback. There is no guidance-only branch, so it stalls in a sandbox. Use CLERK_PLATFORM_API_KEY for headless flows |
clerk init | Full interactive scaffold flow | Runs non-interactively. Explicit --app or a linked profile uses the real-app auth/link/env flow, but --app that would replace an existing application link stops with a usage error unless --relink is passed. With --json, it also emits an input-required status. Inside a Git repository, the link is shared by every project in it. On a framework with accountless support, an authenticated agent creates a real app and links it. Without --app or an existing link, an unauthenticated agent on a framework with accountless support mints an unclaimed app with temporary dev keys — no login, no flag, no browser. On those frameworks, --accountless forces the temporary-keys path over both a session and an existing link — the escape hatch when a stale link would otherwise trigger a browser login; --login forces the authenticated flow (usage error while signed out in agent mode). Non-native frameworks without temporary-key support print manual setup guidance when no app is selected. iOS and macOS apps (--starter included) require a real Clerk application and reject --accountless; with --json, they report setup as JSON statuses. Follow the iOS setup prompt. |
| Color / spinners | Enabled | Disabled |
In addition, sandboxed agent-mode invocations may emit the warning above once per CLI invocation when a host-sensitive operation is blocked.
Rule of thumb: always pass --yes for mutations and --json for structured output where available. Pass --app / --instance when you intentionally target a real app. Bootstrapping without authentication needs no flag at all — temporary development keys are the default; --accountless forces that path over both a signed-in session and an existing linked profile. --keyless is a deprecated compatibility alias and should not be used in new instructions.
Passing options as JSON: --input-json
Every command accepts --input-json <json|@file|->. Keys convert from camelCase/snake_case to kebab-case and expand into flags before Commander parses argv - so anything a command accepts as a flag can come from JSON instead.
clerk init --input-json '{"framework":"next","yes":true}'
clerk config pull --input-json '{"keys":["auth_email","session"]}' # arrays → repeated flags
clerk init --input-json @init-opts.json # read JSON from a file
clerk init --input-json - # read JSON from stdin
echo '{"framework":"next","yes":true}' | clerk init --input-json - # same, over a pipeStdin is read only with the explicit - marker (--input-json -). Bare piped stdin is never consumed, so shell loops and self-reading commands (cat body.json | clerk api …) are untouched.
Positional arguments (e.g. the <name> in clerk apps create <name>) cannot come from JSON - only flag-style options can.
| JSON | Expansion |
|---|---|
"str" / number | --flag <value> |
true | --flag |
false / null | omitted |
["a","b"] | --flag a --flag b (empty arrays omit) |
{…} (nested) | rejected - invalid_json, exit 2 |
Placement. Put --input-json after the leaf subcommand. Before it, flags land on the root program, so only --mode / --verbose work there - subcommand flags (--json, --app, etc.) error as unknown. Explicit flags after --input-json override its values (last-flag-wins).
Errors use the standard agent-mode format: bad JSON → invalid_json, missing @file → file_not_found, unknown expanded flags → Commander's unknown option. All exit 2.
Exit codes
| Code | Meaning |
|---|---|
0 | Success |
1 | Runtime error (auth failure, API error, file I/O, etc.) |
2 | Usage or validation error (bad flags, malformed JSON body, unknown endpoint) |
clerk doctor exits 1 when any check fails (warnings alone still exit 0).
Error output format
Human mode:
Single-line error message on stderr.
Stack traces hidden unless
--verboseis passed.API errors include the first message from the response body, prefixed with a human context string (e.g.,
Failed to fetch config: unauthorized).
Agent mode:
Structured JSON on stderr:
{"error":{"code":"...","message":"...","docsUrl?":"...","errors?":[...],"examples?":[...]}}.codeis a machine-readable error code (e.g.,auth_required,api_error,unexpected_error).errorsarray is present for API errors and mirrors the Clerk API error shape ({code?, message?, meta?}).docsUrlis present when the error has associated documentation.examplesis present when the error names commands to run next, such as anot_linkederror on newer CLIs:[{command, description}]. The message says which commands are alternatives; see auth.md.
Both modes:
User-aborted commands exit cleanly with no error output.
When handling errors programmatically, read stderr, check the exit code, and re-run with
--verboseto get a trace if you need to debug.
Structured outputs you can rely on
| Command | Structured output |
|---|---|
clerk doctor --json | [{name, status, message, detail?, remedy?, fix?}] |
clerk apps list --json | Array of application objects |
clerk apps create --json | Single application object |
clerk users list (agent mode or --json) | {data: [...users], hasMore} envelope (BAPI user shape inside data) |
clerk users create (agent mode or --json) | Single user object (raw BAPI shape) |
clerk users open (agent mode) | {url, appId, appName, instanceId, instanceLabel, userId, opened: false} |
clerk api <path> | Raw API JSON (Backend or Platform) on stdout |
clerk api <path> --include | Response headers on stderr, body on stdout |
clerk config pull | Instance config JSON |
clerk config schema | JSON Schema |
clerk open [subpath] | {url, appId, appName, instanceId, instanceLabel, subpath, opened: false} (agent mode) |
clerk open --print | Plain dashboard URL on stdout |
clerk deploy (agent mode) | The deploy report: complete, state, domain status, OAuth status, and nextAction |
clerk deploy status (agent mode) | The same report, plus exit 0 complete or 1 incomplete |
| Any command (agent mode) | On error: {"error":{"code","message","docsUrl?","errors?","examples?"}} on stderr |
For commands without an explicit --json flag, clerk api is your escape hatch: hit the underlying endpoint directly.
Patterns for agent-driven use
Diagnose before acting
clerk doctor --json --spotlightParse the output, then for each failing check read remedy and act. Never call --fix from an agent - it's interactive.
In agent mode, doctor also includes a Host execution check when it can
detect that Clerk's host-side state is not writable. If that check warns, stop
trusting auth/link/env/API failures from the same sandboxed run and rerun the
relevant command on the host.
Preview every mutation
# Dry run first
clerk api /users/user_abc123 -X DELETE --dry-run
# If the preview is what you expected, run it with --yes
clerk api /users/user_abc123 -X DELETE --yesAudit and harden security
# Grade the instance; JSON in agent mode. Exit code is 0 whatever the grade.
clerk security audit --instance prod > /tmp/audit.json
jq '.score' /tmp/audit.json
jq '[.findings[] | select(.status != "met") | {id, severity, status, decision, customFlows}]' /tmp/audit.json
# Ask the user for --factors / --strategy first. Preview and apply with identical flags.
clerk security fix --all --factors <factors> --strategy <strategy> --instance prod --dry-run
clerk security fix --all --factors <factors> --strategy <strategy> --instance prod --yesOmitting --factors or --strategy skips mfa or passwordless-auth in both commands; that is a valid choice if the user isn't ready for it.
Findings are met, unmet, or blocked (mfa-required waits on mfa; both in one call works). --all covers critical and recommended checks; add --good-to-have or name ids for the rest. oauth-custom-credentials stays manual. fix prints {changed, dryRun, applied, decisions, skipped, score: {before, after}, remaining}. clerk security checks lists every id offline.
Target explicitly
# Don't rely on the linked profile for critical operations
clerk api /users --app app_abc123 --instance prodThe same advice applies to linking in agent mode: clerk link --app app_abc123 is deterministic and works non-interactively, for an app the person named that's already in the account. If you omit --app, the command succeeds only when silent autolink can prove the target app from existing publishable keys, or, on newer CLIs, when it claims the app clerk init created in this folder.
Deploy to production
clerk deploy --mode agent prints a JSON report on the production deploy. With --domain, it first creates the production instance by cloning development, or resumes the existing one. clerk deploy status --mode agent checks progress and prints the same report. The report's nextAction says what to do next.
# 1. Print the report without changing anything.
clerk deploy --mode agent
# 2. With no production instance yet, ask the user for a domain they own, then create it:
clerk deploy --mode agent --domain example.com
# 3. Verify, and keep waiting when the user asks:
clerk deploy status --mode agent
clerk deploy status --mode agent --waitFor not_linked, follow When the folder isn't linked. clerk deploy has no --app flag: to deploy a specific app, link the folder first with clerk link --app <app_id>.
clerk deploy --mode agent exits 0 once its own work succeeds, even when production isn't complete yet. With --domain, it can fail with these errors:
usage_error(exit 2): the domain is malformed or a hosting provider's, such as*.vercel.app. The CLI doesn't catch every hosting domain, so confirm the user can change the domain's DNS before runningclerk deploy --domain.provider_domain_not_allowed: Clerk rejected the domain as a hosting provider's. Ask the user for a domain they own and can add DNS records to.home_url_taken: another instance already uses that domain. Ask the user for another.plan_insufficient: the plan doesn't cover features enabled in development, and nothing was created. The user upgrades their plan in the Clerk Dashboard or turns off the features the message lists.production_instance_exists: the production instance is on another domain, which the message names. Ask the user whether to continue with that domain (runclerk deploy status --mode agent) or change it in the Clerk Dashboard. Don't retry with another--domain.deploy_domain_unchecked: Clerk hasn't returned the existing instance's domain yet, and nothing was changed in Clerk. Wait, then runclerk deploy status --mode agent. Don't rerunclerk deploy --domainuntil the report shows the domain.deploy_instance_unresolved: the instance exists, but Clerk can't return it or its status yet. Wait and runclerk deploy status --mode agent. If it keeps failing, stop and tell the user.
Any other error from Clerk arrives in the same error envelope, so follow its message.
The user can run the wizard instead, with clerk deploy in a separate terminal, from the project directory. It reads answers from stdin, so never run it through your session, including Claude Code's ! shell escape. After the user finishes, run clerk deploy status --mode agent.
clerk deploy status --mode agent exits:
| Exit | Meaning |
|---|---|
0 | Deploy is complete and verified. |
1 | Incomplete deploy or runtime failure. A deploy report on stdout describes incompleteness. An error envelope on stderr describes failure. |
2 | Usage or validation error. Read the error envelope on stderr. |
When nextAction includes a Clerk Dashboard URL, show it to the user and offer to open it. urls.domains is the instance's Domains page and urls.instance its Dashboard home.
The report has this shape:
{
"complete": false,
"state": "domain_pending",
"domain": "example.com",
"productionInstanceId": "ins_...",
"domainStatus": { "dns": "pending", "ssl": "pending", "mail": "complete" },
"pendingDnsRecords": [{ "type": "CNAME", "host": "clerk.example.com", "value": "...", "required": true }],
"oauth": { "complete": true, "configured": ["google"], "pending": [], "unsupported": [], "setup": [] },
"urls": { "domains": "https://dashboard.clerk.com/apps/app_.../instances/ins_.../domains", "instance": "https://dashboard.clerk.com/apps/app_.../instances/ins_..." },
"hostSetup": [],
"nextAction": "DNS records not found yet for example.com. Add the records in `pendingDnsRecords` at the domain's DNS provider if you haven't already, then re-run `clerk deploy status --wait`. Propagation usually takes minutes. Ask the user to visit the Clerk Dashboard domains page, or offer to open it: https://dashboard.clerk.com/apps/app_.../instances/ins_.../domains"
}The example leaves these fields empty or out:
oauth.setup: one entry per provider missing production credentials, withredirectUri,authorizedOrigins,note(a warning to pass on to the user, ornull),payload,command, andfields, wherefileContents: truemarks a value that is a file's contents.oauth.unsupported: providers the CLI can't configure. They don't blockcomplete, so tell the user to configure them in the Clerk Dashboard.nativeAppleReadinessIssue: present when Sign in with Apple is native-only and not ready in production.nextActiongives the fix, and Apple has nooauth.setupentry until then.hostSetup: atcomplete, the command that copies the production keys to Vercel. Vercel only, and[]in every other state.
Run the oauth.setup and hostSetup commands as given; don't replace a quoted path with clerk. On Windows these commands and the env pull --print command the user runs call plain clerk (in the hostSetup command, inside keys=$(...)). If clerk isn't installed, replace that call with the package-runner form from Invoking the CLI and keep the rest of the command.
complete is true only when the domain is complete and every supported OAuth provider enabled in development has production credentials. Read state, not domainStatus: its DNS, SSL, and email DNS can all be complete while Clerk is still finishing.
What to do in each state:
| State | What to do |
|---|---|
not_started | Ask the user for a domain they own and can add DNS records to, then run clerk deploy --mode agent --domain <domain>. If that just succeeded, rerun it with the same domain: Clerk may not list the instance yet. |
domain_provisioning | Wait, then run clerk deploy status --mode agent. |
domain_pending | Have the user add pendingDnsRecords at their DNS provider, and meanwhile save any oauth.setup entries as in Save OAuth credentials. Rerun clerk deploy status --mode agent after propagation. |
oauth_pending | Save the oauth.setup entries as in Save OAuth credentials, then run clerk deploy status --mode agent. |
complete | Follow Copy the production keys to the host, and for an iOS or macOS app also follow nextAction's clerk doctor step, which checks the Release build. |
Save OAuth credentials
Follow these steps rather than the shorter ones in nextAction, which leave out the dry run and the confirmation. Steps 2 to 6 need bash or zsh. In any other shell, after step 1, fix any nativeAppleReadinessIssue as nextAction says, then have the user run clerk deploy in a separate terminal, from the project directory, to enter every provider's credentials. Then go to step 8.
Have the user create each provider's OAuth app with its entry's
redirectUriandauthorizedOrigins, and pass on itsnotewhen there is one.For each entry with no field marked
fileContents: true, create a file outside the project withmktemp(it prints the path, and only your user can read the file). Write the entry'spayloadinto it with a shell command rather than a file tool (printf '%s' '<payload>' > <path>), placeholders included. Ask the user to replace each<placeholder>with the real value. Don't ask for the values or read the file.Dry-run each entry's
commandon its file with--dry-runadded. Its output shows the values of secret-named keys as[REDACTED]. Don't add--verbose, whose output can include raw error bodies.If a dry run fails with exit 2, read the error. "Nothing was saved:" names a field that still holds its
<placeholder>or[REDACTED].invalid_jsonmeans the file's JSON broke while the user edited it. Ask the user to fix the file, then dry-run again.Confirm once with the user before saving the credentials to production, covering every provider. If they decline, or you stop before saving, remove each file with
rm -f "<path>".Save each entry by running its
commandin this wrapper, with the file's path in place of<file>and<path>. The wrapper keeps the command's exit status and removes the file whether or not the save succeeds:( rc=0; <command> || rc=$?; rm -f "<path>"; exit "$rc" )If a save fails, the file is already gone: create a new one as in step 2 and have the user fill it in again.
For an entry with a field marked
fileContents: true(Apple's private key), skip steps 2 to 6 and have the user runclerk deployin a separate terminal, from the project directory. The wizard reads the key from its file and skips providers already saved, so save the other entries first.Verify with
clerk deploy status --mode agent.
Copy the production keys to the host
On Vercel, you usually run the report's hostSetup command. On any other host, the user copies the keys.
On Vercel:
If
vercelisn't installed, or you can't run bash or zsh, follow the instructions for any other host. Otherwise, runvercel whoamiyourself (it doesn't start a login just because you're logged out). If it prints "Logged out.", have the user runvercel loginin their own terminal, then check again.The
hostSetupcommand writes to the Vercel project this directory is linked to. Ask the user to confirm that project serves this app in production. If it doesn't, follow the instructions for any other host.Run the
hostSetupentry'scommandin bash or zsh. It pipes the keys into Vercel's CLI without showing them. Ifvercel whoamior thehostSetupcommand doesn't exit, Vercel is waiting for a browser login it started: stop it, have the user run the command that hung in their own terminal, from the project directory, and finish the login it opens, then rerun.If the
hostSetupcommand exits 3, a key is already set as a production Vercel variable that also targets another environment (Preview, Development, or a custom one). Ask the user before replacing it, because--forcemay rewrite that shared variable and put the production secret key in those deployments. If they agree, run the same command again with itsallow_shared=0changed toallow_shared=1. The consent applies to that one run.If it exits 1, read its stderr:
A message starting "Couldn't read the variable list" or "vercel env ls printed escape codes" means the command couldn't read the output of
vercel env lsand stopped before uploading anything. Don't rerun it or change itsallow_shared. Tell the user, have them check the project's variables in Vercel, and follow the instructions for any other host.Any other message is the error of the
env pull,vercel env ls, orvercel env addcommand that failed. Fix the cause, such as having the user runvercel loginorvercel link, then rerun the command: a failed upload can leave earlier keys already replaced and later ones not uploaded.
On any other host, have the user run the env pull --instance <productionInstanceId> --print command from nextAction, copied as given, in a separate terminal from the project directory, never through your session. They add the printed lines to the host's production environment. Don't pipe the keys into the host's CLI yourself, even though nextAction suggests it: some host CLIs print the values they set. On macOS, the | pbcopy form in nextAction copies the lines without showing them.
Then, on every host:
The report's commands copy only the publishable and secret keys. Have the user also add the other Clerk variables their app needs from their env file, such as
NEXT_PUBLIC_CLERK_SIGN_IN_URL, to the host's production environment. Don't open the env file yourself, because it also holds the development secret key, and don't ask the user to share the values.Once the keys and the other Clerk variables are on the host (on Vercel, once the
hostSetupcommand exits 0), have the user redeploy, then sign up on the domain to confirm.
Use the catalog, not hard-coded paths
clerk api ls users # discover available user endpoints
clerk api ls --platform apps # platform-side endpointsSurface doctor remedies to the user
When clerk doctor --json reports a failure, show the user the name, message, and remedy - don't just silently try to fix it, because the underlying fix (e.g., clerk auth login) usually requires human interaction.
clerk doctor --fix is disabled in agent mode, so you cannot rely on it. If a caller wants to attempt remediation anyway, map the failing check to the command that would fix it in human mode. Each check exposes this mapping via the optional fix.label field on the JSON result:
| Failing check | Manual remediation |
|---|---|
Logged in | clerk auth login |
Authentication valid | clerk auth login |
CLI configuration | clerk auth login |
Project linked | The step in remedy; if it only says clerk link, see auth.md |
Application reachable | clerk link |
Instance IDs | clerk link |
Environment variables | clerk env pull |
CLI version | (no auto-fix; run clerk update) |
Shell completion | (no auto-fix; see clerk completion --help) |
All three remediation commands are themselves interactive by default: auth login opens a browser, link prompts for an app when --app is omitted, and env pull writes a file. In agent mode, use clerk link --app <id> for an app the person named that's already in the account; for a folder that isn't linked, follow When the folder isn't linked. Before clerk auth login in a folder with .clerk/keyless.json, see Older CLIs.
What NOT to do in agent mode
Don't ignore the sandbox warning. If the CLI says host-only Clerk state or system capabilities may be unavailable, rerun the same command on the host before trusting the result.
Don't run
clerk auth loginjust to bootstrap or configure a project - it is not required.clerk initand most instance commands work with no account at all; see auth.md for the current list of which commands those are and which stay account-only. Log in only to claim an appclerk initcreated, link a real app, or use account-only commands. Logging in can't claim an app a Clerk SDK created: a person claims it by runningclerk openin their own terminal.Don't assume an unclaimed app means a throwaway app. The CLI follows the local
sk_key,sk_live_included — in an unlinked repo holding a production key,enable orgs/config patchmutate production unconfirmed. Pass--app <id>when you mean a real app.Don't assume
clerk auth loginis fully unattended from an agent - it opens a browser and waits for a callback. PreferCLERK_PLATFORM_API_KEYfor headless automation.clerk init --app <id>or init in an already linked project may still invoke the normal login fallback when a real app target is explicit.Don't call
clerk linkwithout--appand assume the agent can pick for you - it only succeeds when silent autolink can determine the app from detected keys, or, on newer CLIs, when it claims the appclerk initcreated.Don't run
clerk unlinkin agent mode without--yes- it exits with a usage error instead of prompting.Don't run
clerk config putwithout--dry-runfirst - it's a full replacement and is destructive.Don't skip
--yesonunlink,security fix, orupdate- the first two exit with a usage error, andupdateexits0without updating.Don't read secrets into your context or logs - never read or print
env pull --printoutput or a filled-in OAuth credentials file, never copy[REDACTED]fromconfig patchorconfig putoutput back into a payload, and don't add--verbosearound secrets.
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs Python, Needs Node.js
Security
What our scanner noticed· Low risk
- Instructions that use the network
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- Clerk
- Publisher
- Clerk (Official)
- Source
- clerk/skills · skills/clerk-cli · commit c5d2c0d
- Licence
- MIT License (redistribution allowed)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Clerk or Clerk.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download clerk-cli.zip