One command asks a typed question; others send a request file as written, upload images, stream a session and run batch jobs. Each prints what the API answered: readable lines on a terminal, JSON in a pipe. dn is one binary for macOS, Linux and Windows; only --video needs more, ffmpeg and ffprobe on your PATH.
Install#
curl --proto '=https' --tlsv1.2 -fsSL https://raw.githubusercontent.com/Bynn-Intelligence/decision-node-cli/main/install.sh | sh- The script picks the archive for your platform from the latest release, checks it against the release's
checksums.txtand installs it to~/.local/bin. When cosign is installed it first verifies the release's signature ofchecksums.txt, and refuses to install if it does not verify; without cosign it says so and relies on the checksum alone. SetDN_INSTALL_DIRto install elsewhere, orDN_VERSION(for examplev0.1.0) to pin a version. - Every method installs the same program twice, as
dnand asbynn-dn. - Updating:
brew upgrade dn,scoop update dn, or run the install script again. - Release notes and every archive are on the releases page.
dn --version
# dn 0.1.0 (commit 1a2b3c4, built 2026-10-20T09:00:00Z, darwin/arm64, DecisionNode SDK spec 1.3)It prints the version, the commit and the time of the build, your platform and the API spec it follows; dn version --json prints the same as an object.
Your API key#
dn auth login # prompts for the key without echoing it
dn auth status # where the key comes from, and whether it works- The order:
DECISIONNODE_API_KEYfirst, then the keydn auth loginstored. There is no flag that takes the key itself: a key on a command line ends up in shell history and process lists. - Where it is kept:
dn auth loginkeeps the key in your operating system's keychain (macOS Keychain, Windows Credential Manager, the Secret Service on Linux). Where there is no keychain it writes~/, readable only by you, and says so; if that file's mode is ever wider than.config/ decisionnode/ credentials.json 0600,dndoes not use it and tells you thechmod 600to run. - From a script:
printf '%s' "$KEY" | dn auth login --key-stdin. Keys are kept per profile:dn auth login --profile work.
Ask a question#
dn truth "Is the customer asking for a refund?" -s "Refund me now or I cancel."
# in a script: exit 0 only when the probability is at least 0.9
dn truth "Is the customer asking for a refund?" -s "Refund me now or I cancel." --min 0.9 -q && echo refundOn a terminal dn prints the answer in a few aligned lines; in a pipe it prints the answer object as the API sent it, one line of JSON such as {"type":"truth","truth":0.97}. --min turns a Truth into an exit code a shell can branch on.
Configuration#
| Setting | Flag | Environment variable | Default |
|---|---|---|---|
| API key | none | DECISIONNODE_API_KEY | the stored key |
| Base URL | --base-url | DECISIONNODE_BASE_URL | https://api.decisionnode.comhttps://api.decisionnode.com |
| Model | -m, --model, --flash | DECISIONNODE_MODEL | decisionnode-1.0 |
| Profile | --profile | DECISIONNODE_PROFILE | default |
A flag beats the environment, which beats the stored configuration, which beats the default. Stored settings live in ~/ ($XDG_CONFIG_HOME is honoured; %AppData%\decisionnode on Windows), written by dn config set. A profile's settings fall back to the top-level ones.
model = "decisionnode-1.0-flash"
[profiles.selfhosted]
base_url = "https://decisions.example.com"--model takes decisionnode-1.0 (the default) or decisionnode-1.0-flash, each its current release, a pinned id from dn models, or one of your fine-tuned models; --flash is short for --model decisionnode-1.0-flash. A base URL must be https://https://, or http://http:// only to localhost, 127.0.0.1 or ::1.
Commands#
| Command | What it does |
|---|---|
dn truth <instructions> | A yes-or-no question, answered with the probability of yes. --true and --false say what a yes and a no mean; --min P exits 1 when the probability is below P. |
dn choice <instructions> -o NAME[=DESCRIPTION] ... | One option of a set. --options-file takes a JSON object of name to description, or one name a line. |
dn score <instructions> -l LEVEL ... | A place on an ordered scale, levels lowest first. |
dn number <instructions> [--min N] [--max N] [--step S] | A value on the grid min, min+step, ... up to max. A bound left out is not sent, so the API's default applies. |
dn decide <request.json | -> | The full request body (state, questions, images, videos, boosters, model) from a file or stdin, sent as written; --booster replaces the file's boosters. A request file is how dn decide and dn batch ask point and box questions. |
dn upload <image> ... | Uploads images (PATH or ID=PATH) and prints one line per image, with its upload_id, for requests written by hand. |
dn session <open.json> | A live session: frames as JSON Lines on stdin, replies on stdout. dn session end ID ends one from another terminal. |
dn batch run <requests.jsonl | -> | Many requests, one a line, through the Batch API, or as live calls with --live. |
dn batch create | add | finalize | get | list | results | cancel | The Batch API's calls one by one, each printing what the API sent. |
dn models | The models your key may use, with their limits, your fine-tuned models included. |
dn auth login | status | logout | Store, check and remove the API key. |
dn config get | set | list | The stored base URL, model and profiles. |
dn version, dn help, dn completion <shell> | Version, help, and shell completion for bash, zsh, fish and PowerShell. |
dn truth "Is the customer asking for a refund?" -s "Refund me now or I cancel."- Pictures and video on
truth,choice,scoreandnumber:--image(a path or a URL, up to 16) and--video(a local file, up to 4, cut into frames on your machine;--fps,--start,--end,--max-frames). - State:
-s, --statefor text or JSON,-f, --state-filefor a file (-reads stdin). - Boosters:
--booster NAME, orNAME:requiredto fail without its reading, up to 4. - No fallback:
--no-fallbackwaits for DecisionNode MoE instead of an answer from DecisionNode Flash.
Output#
Results go to stdout and everything else to stderr.
| Mode | When | What is printed |
|---|---|---|
| Text | stdout is a terminal | The answer in a few aligned lines: the value, the confidence, the probabilities highest first. Colour on a terminal only, off with NO_COLOR |
| JSON | stdout is not a terminal, or --json | One line of compact JSON: the answer object as the API sent it for truth, choice and score; the response body for decide and models |
| Value | --value | Only the value and a newline; dn decide --value prints id<TAB>value lines when there is more than one question |
--fullprints the whole body fortruth,choice,scoreandnumber;--metaadds the request id, the model, the latency, the input tokens and the safety probabilities.-q, --quietprints no result and no notices; errors are still printed.- Errors are a sentence on stderr when stderr is a terminal, and one JSON object otherwise, with the status, the
error_type, the message and the request id.
Exit codes#
| Code | Meaning |
|---|---|
0 | Answered |
1 | Answered, and --min was not met |
2 | The command line or the input is wrong; nothing was sent. Anything the API would refuse by a fixed rule is checked before sending, and a video that cannot be read or a missing ffmpeg is a 2 too |
3 | The key is missing or rejected, or the account is out of credit (401, 402, 403, a booster turned off for your workspace included) |
4 | The safety check refused the request |
5 | The API rejected the request (400, 404, 413, 422, over the token limit, any other status without a meaning of its own) |
6 | Temporary: rate limit, overload, timeout or network, after the retries (429, 529, a required booster that failed, other 5xx) |
7 | dn batch finished and some lines failed, or dn batch run timed out before the batch ended |
8 | dn session ended with a close code other than 1000 |
dn help exit-codes prints the same table.
Timeouts and retries#
--timeoutis the time allowed for each attempt, in seconds or as30s: 10 s by default, 30 s for a request naming theocrbooster, which is never retried after a timeout, since the API finishes that attempt and may bill it.- Temporary errors are retried twice by default (
--retries N), waiting as long as the server asks (retry-after-ms, thenRetry-After, at most 60 seconds) or backing off. A request the server rejected or refused is never retried, and redirects are never followed. - Uploads have 60 seconds per attempt, apart from
--timeout.