The CLI
Sign in, chat with agents, query databases, copy files, deploy apps, buy domains and forward webhooks from your terminal.
The noclick command is the platform from your terminal. It's a thin layer over the Python client: every command is a few of the calls you'd write yourself, so what works in your code works here, with the same errors.
Install
The CLI comes with the Python package. The cli extra adds what noclick login needs to keep your key in the OS keychain:
pip install "noclick[cli]"
noclick --versionpython -m noclick runs the same command, and noclick <command> --help lists every flag.
Sign in
noclick login signs you in from a browser. It prints a link and a short code; open the link (it opens for you at a terminal), sign in to NoClick if you aren't, check the code and the machine match, and approve:
noclick login
# Open https://noclick.com/dashboard/developers/cli and enter the code WDJB-MJHT
# Signed in as ana@acme.com.The page shows the machine's name and the address the sign-in came from. Approve only a sign-in you started yourself, just now: whoever you approve gets a key to your account. Deny ends it, and the CLI says so. A code lasts 15 minutes; after that, run noclick login again.
Approving creates an API key named after your machine, like "CLI on ana-mbp", with every scope except the ones that read secrets (raw provider tokens, card numbers and webhook signing secrets). It shows on the console's API keys page with the others. The CLI keeps it in the OS keychain (macOS Keychain, Windows Credential Manager or the Secret Service on Linux) and never writes it to a file. On a machine without a keychain, like a container, keep it in a file only you can read instead:
noclick login --store file # ~/.config/noclick/credentials.json, mode 600
noclick login --no-browser # print the link without opening a browsernoclick whoami shows the key the next command will use and where it came from, without calling the API:
noclick whoami
# Account ana@acme.com
# Key CLI on ana-mbp (nk_live_ab12…), in the OS keychain
# Profile default
# API https://api.noclick.comnoclick logout revokes the key and forgets it. A key it can't revoke (you're offline, say) stays stored, so you can run it again.
Use it in CI
In CI, set NOCLICK_API_KEY to a key with the scopes the job needs. It wins over anything login stored, and NOCLICK_API_URL points it at another API:
export NOCLICK_API_KEY=nk_live_...
noclick deploy booking --branch mainTo stay signed in to more than one account, say your product's and a test account, give each login a profile. Each keeps its own key, and remembers the API it was minted for:
noclick --profile test login
noclick --profile test agents list # or export NOCLICK_PROFILE=testAct for a user
Commands act for your account. --user makes one act for a user instead, by your id for them, the same as noclick.as_user in code:
noclick agents list --user user_42
noclick db query booking-db "select count(*) as n from bookings" --user acmeTalk to agents
noclick agents list shows your account's agents, and a user's own with --user. --all-users lists every user's too:
noclick agents list
# NAME MODEL USER CREATED ID
# support-bot openrouter/anthropic/claude-sonnet-5 account 2026-09-02 10:14 7b1e4f2a-...
# concierge openrouter/anthropic/claude-sonnet-5 account 2026-09-10 16:40 2c9d1e7b-...noclick agents chat talks to one. With --user, it talks as that user: to their own agent by that name, else to your account's. Each line you type is a turn; the reply prints when the turn finishes, with the tools it used above it. Ctrl-D or /exit leaves, and the CLI prints the conversation's id so you can pick it up again:
noclick agents chat support-bot --user user_42
# you> Where's my refund for order 1841?
# (stripe__list_refunds)
# It went out on September 24th and should reach your card within 5 business days.
#
# you> /exit
# Conversation conv_8f2a: continue it with --conversation conv_8f2a-m sends one message, prints the reply and exits, and piped lines are sent one turn at a time, which makes a quick script of it:
noclick agents chat support-bot -m "Summarize yesterday's tickets" --conversation daily-digest
printf 'hello\nbook Friday at noon\n' | noclick agents chat concierge --user acmeCtrl-C during a turn stops waiting, not the turn: the CLI prints the noclick ops wait command that reads its reply. See Agents.
Manage users
noclick users list --q ana --limit 20
noclick users create user_42 --name "Ana Silva" --email ana@example.com --spend-cap 50create refuses an id you've already used. See Users.
Query a database
noclick db query runs one statement on a database and prints its rows as a table. Pass values with -p, one for each ? in order; a value that parses as JSON (42, true, null) is sent as that, anything else as text:
noclick db query booking-db "select id, guest, starts_at from bookings where venue = ? limit ?" -p "Harbor Room" -p 20
# id guest starts_at
# 1841 Ana Silva 2026-10-02T12:00:00Z
# 1 row (4 ms)A query runs read-only, so a stray update can't change anything. --write lets a statement change data:
noclick db query booking-db "update bookings set guest = ? where id = ?" -p "Ana Lima" -p 1841 --write--json prints the rows as JSON for jq and scripts. noclick db shell is an interactive prompt on the same terms (add --write to change data). End each statement with ;, and use .tables, .schema [table] and .quit:
noclick db shell booking-db
# booking-db> .tables
# booking-db> select venue, count(*) as n from bookings
# ...> group by venue;The shell talks to the API like everything else, so it works the same on SQLite and Postgres, with nothing to install.
Copy files
noclick bucket ls lists your buckets, then the files in one, a level at a time like ls (-r lists every key under a prefix):
noclick bucket ls
noclick bucket ls uploads/invoices/
# SIZE UPDATED KEY
# - - invoices/2026-09/
# 84.2 KB 2026-09-27 08:00 invoices/0042.pdfnoclick bucket cp copies between your machine and a bucket. Write the bucket side as bucket:key; a key ending in / keeps the file's name, and - reads stdin or writes stdout:
noclick bucket cp invoice.pdf uploads:invoices/0042.pdf
noclick bucket cp uploads:invoices/0042.pdf .
noclick bucket cp -r ./dist public-assets:site/ # a whole folder
noclick bucket cp -r uploads:invoices/2026-09/ ./backup # every key under a prefix
pg_dump analytics | noclick bucket cp - uploads:backups/analytics.sqlAn upload's content type is guessed from its name; --content-type sets it. One upload carries up to 100 MB; for larger files, use S3 tools with the bucket's S3 credentials.
Deploy an app
noclick deploy pushes the commit you're on to your app's repo with plain git, then deploys exactly that commit and prints its URL. Run it inside the app's git checkout:
noclick deploy
# Pushed 3f2a1c9 to main.
# Building…
# https://booking.noclick.site
# Ready: production.Without a name it deploys the app named after the checkout's folder; noclick deploy booking (or --app booking) names it. The branch is the one you're on: the app's production branch (main unless you set another) goes live, and any other branch becomes a preview with a URL of its own. --branch pushes and deploys under another branch name.
Deploy ships commits, never your working tree, so it refuses while tracked files have uncommitted changes. That keeps what's live equal to a commit you can read in git log, roll back to by its sha, and deploy again from anywhere, and it means an untracked .env can never ride along. Files git ignores stay on your machine.
The first time, --create makes the repo and the app:
noclick deploy --create --budget 25 # and give it a $25 monthly budget
noclick deploy --branch new-form --no-wait # print the operation id and return--budget sets the app's monthly budget in dollars, on any deploy; left out, the budget stays as it is.
When the repo has commits your checkout doesn't, the push is refused. That happens when an app service commits code for you, such as sign-in. The CLI fetches them as noclick/<branch>; merge them and deploy again:
git merge noclick/main
noclick deployWhen a build fails, the CLI prints the end of its build log and exits with status 1. noclick logs shows what the running app prints. The push uses a write token that lasts ten minutes and never lands in your git config or the process list. See Repos.
From a coding agent
An agent connected to NoClick's MCP server gets its code in with the repos_push tool, which hands back one command to run in the project's folder:
noclick deploy --app booking --create --token nk_mcp_...--token takes a deploy token instead of your key: it lasts 30 minutes, reaches that one repo and the apps deploying it (finding them, pushing, creating the app, deploying and reading what follows), and can't change the app's budget or touch anything else. It acts as the agent's MCP connection, so the same rules hold: deploying the production branch needs the connection's deploy permission, your policy applies, and revoking the connection ends the token. The connection's own access never enters the shell.
Read logs
noclick logs booking # the latest 100 lines
noclick logs booking --since 2h # or 15m, 1d, or an ISO time
noclick logs booking --follow --level error--follow keeps printing new lines until Ctrl-C. Lines are kept for 7 days.
Buy a domain
noclick domains search shows which names are free and what they cost; noclick domains buy buys one, and refuses if it would cost more than --max-price. A domain is charged to your account, whoever it's for:
noclick domains search acmebooking --tld com --tld app
# DOMAIN AVAILABLE PRICE RENEWAL PREMIUM
# acmebooking.com yes $12.99 $21.99 no
# acmebooking.app yes $14.99 $26.99 no
noclick domains buy acmebooking.com --max-price 15
# acmebooking.com
# acmebooking.com is yours (active), registered until 2027-09-28 10:00.Your account's own domain is registered to its default registrant. A user's domain is registered to that person, so with --user the command needs their details: --registrant names a JSON file of their name, email, phone, address, city, region, postal_code, country and agreement_accepted_at (when they accepted the registration agreement; organization is optional), and without it the command says so and buys nothing:
noclick domains buy anasilva.dev --max-price 20 --user user_42 --registrant ana.jsonThe command finishes once the domain is registered and its DNS is set up; if your policy holds purchases for approval, it says so and waits for the approver. It registers for the TLD's shortest term; --years registers it for longer, and --no-wait returns at once with the operation's id.
Forward webhooks to your machine
Your laptop isn't reachable from the internet, so noclick webhooks listen brings events to it: it reads them as they happen and POSTs each one to your local URL, signed the way a real delivery is:
noclick webhooks listen --forward-to http://localhost:3000/noclick/events
# Forwarding every event to http://localhost:3000/noclick/events. Ctrl-C stops.
# Signing secret for this session: nk_whsec_5c1e...
# 10:04:12 operation.succeeded evt_9f2a -> 204 No Content
# 10:04:40 human.answered evt_9f31 -> 500 Internal Server ErrorYour receiver checks each delivery with noclick.Webhook.verify and the session's secret, exactly as it will in production. --secret signs with a secret you choose (the one in your .env, say), --events takes a comma-separated list of types (operation.*,human.answered), and --user forwards only one user's events; by default it forwards your account's and every user's, like one of your account's endpoints. A failed answer is printed, not retried: fix your handler and trigger the event again.
Wait on an operation
Anything that runs long hands you an operation. noclick ops wait waits for one, from any machine, and prints its result: an agent's reply as text, anything else as JSON:
noclick ops wait 3b9d6f2e-1c4a-4e8b-9f7d-2a6c5e8b1d40
noclick ops wait 3b9d6f2e-1c4a-4e8b-9f7d-2a6c5e8b1d40 --timeout 60 --jsonA timeout leaves the operation running.
Output and exit codes
Results go to stdout and progress, hints and errors to stderr, so url=$(noclick deploy) captures just the URL. Commands that list things print a table, or JSON with --json: each object as the API returns it.
| Exit status | Means |
|---|---|
| 0 | It worked. |
| 1 | The API or your machine refused; stderr says why and what to do next. |
| 2 | The command line was wrong (a missing argument, an unknown flag). |
| 124 | ops wait --timeout ran out; the operation is still running. |
| 130 | You pressed Ctrl-C. |
An error prints the message, its next step and the request id to quote to support, from the same error types the SDK raises:
noclick db query booking "select 1"
# error: No database is named 'booking'.
# next step: Create it with noclick.Database.create(...) or pass create_if_missing=True.