Agents & API
The CLI
Install krinoa, sign in with a device grant or an API key, and use the JSON-first output contract — --fields, --all, --yes and honest exit codes.
krinoa gives you the same jobs, applications, reads, threads and ledger the web app shows, driven from a terminal, a script or an agent. This page is enough to be productive with it. The CLI reference lists every command, and each command's --help names its flags and the exact fields it answers with, without leaving the shell.
Install#
krinoa is a single ESM file. It needs Node 22 or newer and nothing else at runtime.
npx krinoa --help # no install, always the latestnpm i -g krinoa # or install it onceThe package is not published to npm yet; it goes up with the first tagged release. krinoa completion <shell> prints a completion script for bash, zsh or fish to source from your shell's startup file.
Sign in as yourself#
krinoa login with no flags runs the device-authorization grant: it prints a short code, tries to open /device in your browser — printing the URL if it cannot — and waits while you approve it. What it stores is a session token, so the CLI acts as you, with your role: an interviewer's scoping applies, and the activity ledger names the human. krinoa logout ends the session on the server and forgets the credential.
krinoa loginkrinoa whoamiSign in with an API key#
An API key is the other way in: a workspace credential rather than a person, acting at admin level, with no browser, and the path for agents, cron jobs and CI. Create one in Settings › CLI access, or with krinoa keys create <name> from a signed-in session; a key cannot create another key, and it lives until it is revoked unless --expires 90 gives it a horizon, up to a year, after which it stops verifying on its own. Exporting it stores nothing on disk:
export KRINOA_API_KEY=krinoa_…krinoa whoamiTo persist it in a named profile instead, read it from stdin so it never enters argv, your shell history or ps:
krinoa login --api-key - --profile ci < key.txtProfiles and environment variables#
Credentials live in one file — $XDG_CONFIG_HOME/krinoa/config.json, falling back to ~/.config/krinoa/config.json — with the directory 0700 and the file 0600, re-applied on every write, and written only by krinoa login and krinoa logout. --profile ci picks a profile per command, at any depth: krinoa --profile ci jobs list and krinoa jobs list --profile ci are the same call. Three variables let an agent or a CI job run with no config file at all.
| Variable | What it does |
|---|---|
KRINOA_SERVER | The server for this call, overriding the profile |
KRINOA_API_KEY | Authenticate with this key, overriding any stored credential |
KRINOA_PROFILE | Which stored profile to start from |
Precedence, highest first: flags, then environment, then the profile in the config file, then the built-in default of https://app.krinoa.com. KRINOA_API_KEY beats a stored session, so exporting a key is how you hand your terminal to an agent for one command.
The output contract#
Output mode follows stdout: a terminal gets a table, anything else gets JSON. --json forces JSON on a terminal. The JSON is the API's response, unchanged — nothing renamed, reordered or summarised — and a list prints a page object, { items, nextCursor }.
--fields publicId,candidate.nameprojects the JSON client-side, keeping the model's own nesting rather than flattening it. A path whose first segment is not a field of that command's output is refused before the request goes out, and the error names the valid ones. On a table it is a note on stderr and nothing else.--allfollows the cursors for you and prints one complete object with nonextCursorkey at all. Lists return 50 items by default and 200 at most with--limit.--yesconfirms. The CLI has no interactive prompts: a command that would delete data or send mail to a candidate refuses without--yes, exits2, and hands back both the consequences and the exact line that would go through. See Agents.
Exit codes#
Exit codes are the branch an agent should take: a failure does not exit 0, and a code always means the same thing. Every failure prints one line on stderr in the shape error: CODE: message, and under --json the API's own error body goes to stdout as well.
| Code | Meaning |
|---|---|
0 | Success |
1 | Unexpected error, including a network failure |
2 | Usage error, or a destructive command refusing without --yes |
3 | Not authenticated — run krinoa login, or set KRINOA_API_KEY |
4 | Not found |
5 | Permission denied |
6 | Validation failed |
7 | Conflict with the current state |
8 | Rate limited |
9 | The CLI and the server disagree about the contract version |
A session, end to end#
Every command below is read-only or refuses before it writes, so you can run the whole thing against your own workspace and change nothing. Substitute your own job and application ids.
krinoa whoami --fields user.name,org.name,role,viakrinoa jobs list --fields publicId,title,statuskrinoa apps list --job RES4A7 --fields publicId,stage,screening.score,candidate.namekrinoa apps show A-21 --fields publicId,candidate.name,screeningDetail.score,screeningDetail.summarykrinoa apps reject A-13 --reason "no systems experience"The first four answer. The fifth refuses, and nothing is written:
error: CONFIRMATION_REQUIRED: Rejecting A-13 (Daniel Okafor) is final. The stage moves to rejected and the candidate is told.Re-run: krinoa apps reject A-13 --reason 'no systems experience' --yesUpdated 2026-09-10