Skip to content
Integrations

Uku CLI

Last updated: Sep 9, 2026

By the end of this article you will know what the command line is for, how to install it, how it treats your API key, and how it handles a write.

The Uku CLI is for people and programs that already work in a terminal: a developer scripting a change across many records, a CI job, and coding agents such as Claude Code or Cursor operating Uku on someone’s behalf.

It is not the right surface for non-technical staff. Someone who wants to ask a question in plain language should use the AI assistant instead — it needs nothing installed and no credential of its own on their machine.

Its advantage over the assistant is economy. Because a command is a single line of text, an agent can chain several operations into one step rather than spending a separate round trip on each. When the work is a list of changes rather than one question, that difference is the reason to reach for the command line.

The Uku CLI is a single self-contained program. Nothing has to be installed alongside it, and it does not need a particular version of anything on your machine once it is built. Building it needs Go 1.26.5 or newer, and nothing else.

  1. Clone the repository: git clone https://github.com/uku-owl/uku-cli && cd uku-cli
  2. Build it: scripts/build.sh, which writes bin/uku
  3. Put it on your PATH: cp bin/uku ~/.local/bin/, or anywhere else on your PATH
  4. Check it: uku --version, then uku doctor once you have signed in

Builds are produced for macOS and Linux on both Intel and Apple or ARM processors, and for Windows on Intel.

Signing the Uku CLI in is one command, uku auth login, which prompts for the two values every API v3 request needs: your Company UUID and an API key. It checks them against the API before writing anything to disk, so a mistyped key fails at the prompt rather than on your first real command.

Who can do this. Company Owners and Company Admins on the Elite plan can generate the key the CLI signs in with. Anyone holding a key can then use it.

Path: Settings & AppsPublic APIPublic API

Creating the key is described in full in Public API v3 — the CLI uses an ordinary v3 key and adds no credential of its own.

Once signed in, the credential is stored in a file under your home directory that only your own user account can read. The file is parsed, never executed: a damaged or tampered profile is treated as data, so nothing in it can run as a command.

You can also keep the key in your operating system’s credential store instead — macOS Keychain, or the Secret Service on Linux. This is off unless you switch it on, and it is worth being exact about what it buys: the key is then not in a file the CLI wrote, so a backup or a synced dotfiles folder does not carry it. It does not hide the key from anything already running as you.

If you hold several companies in Uku, the CLI keeps one profile per company under a name you choose, and you switch between them with a flag on a single command or by making one of them active. Keeping a sandbox company beside your real one is the recommended habit: rehearse a change there, then run the same command against the firm.

A key’s permissions are the API’s permissions — the CLI adds no rights of its own and removes none, so what a command may do is decided by the key you handed it.

When you generate a key you choose between the Read, Edit and All scopes, and you decide separately whether the key may reach financial data. That second choice is its own checkbox rather than part of a scope, which is the useful part here: read access and money access are independent. A request that touches financial data with a key that lacks the financial permission is refused by the API with 403 FINANCIAL_SCOPE_REQUIRED, whichever scope the key holds, and that check runs in Uku rather than in the CLI.

The practical consequence for agent work: give a coding agent a Read key and it can look things up and answer questions, while writes are refused. Add the financial permission only when the job genuinely needs invoice or rate data, and give each tool its own key so you can revoke one without disturbing the rest. The full permission model, including exactly which records count as financial, is in Public API v3.

Writes through the CLI are deliberate rather than incidental, because the same terminal that lists clients can also send an invoice.

  • A write outside a terminal needs --yes. Run without a person present — in a script, a CI job or an agent — and a write that carries no explicit confirmation flag is refused rather than performed.
  • A write in a terminal prompts. You confirm before anything is sent.
  • A deletion restates its target before asking, so you can see which record is about to go.
  • Every non-GET is written to a local log in the CLI’s own configuration directory, readable only by your user account.

That log is worth describing precisely, because it is the file people attach when something has gone wrong. Each write adds one tab-separated line: the time, which account was used, the method, the request path with any query string removed, and the status the API answered with. There are no request or response bodies in it and no key. Because the query string is stripped, a client name you searched for does not reach the file, while the record ids in the path do — a receipt has to say which record changed. The log rotates once it passes five megabytes or ten thousand lines, keeping one previous generation.

Almost never, and the exception is narrow enough to state exactly: the CLI resends a write automatically only when the API answered 428, which means it refused the request for want of a version stamp and nothing was changed. Resending that one is safe by construction — the CLI fetches the current stamp and sends the request once more.

Every other failure is handed back to you, because in every other case the outcome is genuinely unknown:

The API answeredWhat it meansWhat the CLI does
428 — version stamp missingThe write was refused; nothing changedFetches the stamp and resends once
412 — someone else changed the recordThe write was refused; nothing changedStops. Re-read the record, re-apply your change, write again
429 — rate limitedFor a write, the outcome is unknownStops. Check with a read before sending anything else
5xx or a timeoutOutcome unknownStops, for the same reason

The reasoning behind the 412 case is the one to internalise: a blind retry there would overwrite the edit someone just made in Uku. Reads are different — they change nothing, so the CLI may retry one when the connection itself failed.

An API key opens your firm’s live data, so the CLI is built to keep it out of the places credentials usually leak from.

  • It is not on the process list. Authentication headers are set inside the program rather than passed as arguments, so no ps output carries the key while a request is in flight.
  • It is masked in output. Status messages and errors show only a fragment of the key. The single exception is the command whose whole purpose is to print the credential for use with another tool, and it warns on the error stream every time it does.
  • Connections are TLS 1.2 or better, and a plain http:// address is refused before anything is sent unless the host is your own machine — the exemption covers the loopback addresses and nothing wider, so a key cannot leave in the clear because a base URL was mistyped.
  • A non-default host announces itself. If the CLI is ever pointed at somewhere other than Uku’s own API, it prints one line naming the host your credentials are about to travel to. The quiet flag does not suppress that line — where your key went is not chatter, and it is the line a person supervising an agent needs to see.

How do you teach a coding agent to use it?

Section titled “How do you teach a coding agent to use it?”

Coding agents learn the CLI from a file the CLI writes for them. Running uku setup agents installs a skill file for Claude Code, Cursor and Codex, plus a block in the project’s AGENTS.md — so an agent picks up the commands, the exit-code model and, most importantly, the safety rules without anyone pasting instructions into a chat.

There is a second, live channel for the same job: adding --help --agent to any command returns a machine-readable card describing that command. Those cards are generated from the CLI’s own command surface rather than written by hand, which is the point — they cannot describe a command that does not exist, and they cannot fall behind one that changed.

Both channels lean on the negative knowledge, the “this does not work the way you would assume” kind, which is what stops an agent guessing. What an agent may do with any of it is still bounded by the key you gave it.

What if there is no command for the thing I need?

Section titled “What if there is no command for the thing I need?”

The Uku CLI has an escape hatch: uku api <METHOD> <path> sends a request to any API v3 endpoint, using the credentials and the write confirmation you have already set up. First-class commands exist for the everyday work, and the escape hatch covers the rest, so a missing command never blocks you.

Run uku --help for the current command surface. This article deliberately does not list the commands — the CLI’s own help is generated from what the tool actually does, and a copy here would age.

How does this relate to the API and the AI assistant?

Section titled “How does this relate to the API and the AI assistant?”

The command line, the public API and the AI assistant are three doors into one API, not three systems. Permissions, rate limits and the financial gate are enforced in one place behind all of them, and all three are part of the Elite plan.

Choosing between them is mostly a question of who is holding the tool. A person asking a question in plain language wants the assistant. Software running on a schedule wants the API. A developer or a coding agent working through a list of changes wants the command line. Uku for AI agents and developers compares them side by side, and Your data and AI assistants covers what Uku records and what travels to an AI provider when an assistant is involved.

Why does the install command refuse to run?

Section titled “Why does the install command refuse to run?”

curl -fsSL https://getuku.com/install-cli | sh stops before it touches the network, on purpose. The published installer still serves the shell script this program replaced, so running it would overwrite uku with the old script and report success. Build from source instead, as above.

Why does uku update say it cannot install anything?

Section titled “Why does uku update say it cannot install anything?”

For the same reason. uku update fetches the published installer and checks its signature, then stops rather than hand you the old shell script. Rebuild from source to move to a newer version.

Why was my write refused in a script or CI job?

Section titled “Why was my write refused in a script or CI job?”

A write with no person present needs --yes. Without a terminal to prompt in, the CLI refuses rather than acting unattended. Add --yes to the command, or run it interactively and confirm at the prompt.

Why did re-running a command create two records?

Section titled “Why did re-running a command create two records?”

Because you asked twice. Every write carries an idempotency key, but the CLI mints a fresh one on each run, so a second run is a genuinely new request. Pass --idempotency-key with a value you choose and reuse it on the retry, or use --batch with --resume for a run of many changes.