CLI
The furniops command line tool. Install it, sign in, use profiles, and script it with JSON output and exit codes.
furniops is the FurniOps command line tool. With it you can:
- sign in with your FurniOps account in the browser, or save an API key;
- forward live events to a webhook handler on your machine, signed like real deliveries, with no
tunnel (
furniops listen); - emit sample events in test mode, tail the event stream, read a product and start a source run.
Every command is listed in the command reference.
Install
Not on npm yet
Version 0.1.0 is released as the git tag release/cli/v0.1.0 of the
softup-llc/furniops-packages repository; the CLI is not published to npm yet (the
SDK it builds on is). This page documents 0.1.0 as built.
It needs Node.js 22.12 or later. Check what you have with furniops --version.
Sign in
There are three ways to give the CLI a credential. The first one found wins:
| Credential | How | Mode |
|---|---|---|
FURNIOPS_API_KEY | An environment variable. Never stored. | The key's mode |
| An API key saved to a profile | furniops login --with-api-key | The key's mode |
| Your FurniOps account | furniops login (in the browser) | Live, or test with --mode test |
With your account (device flow)
furniops loginYour confirmation code: WDJB-MJHT
Confirm it at https://auth.furniops.com/…
Opened your browser. Check the code matches before you confirm.
Signed in as [email protected] in org_… (profile default, live mode). Stored in the keychain.The CLI opens your browser at a page showing a code. Check that it matches the one in your terminal, then confirm. The CLI never sees your password.
- The session acts in live mode unless you pass
--mode test(or setFURNIOPS_MODE=test), which reaches your sandbox with the same sign-in. - It asks for every permission a credential may hold, limited by your role. Ask for fewer with
--scopes products:read,events:read. - The access token is refreshed automatically shortly before it expires. If the session was
revoked, the next command exits with code
3and asks you to sign in again. - On a machine without a browser (over SSH), use
--no-browserand open the link anywhere.
With an API key
furniops login --with-api-key # prompts, hidden; nothing is echoed
pbpaste | furniops login --with-api-key # or pipe it inThe key is read from standard input or a hidden prompt, never from a flag, so it never lands in
your shell history. A test key (fo_test_…) puts the profile in test mode, which
furniops trigger needs.
In CI, set FURNIOPS_API_KEY instead: it overrides any saved credential and is never written
anywhere.
Check and sign out
furniops whoami # who, which mode, where the credential came from
furniops logout # revokes the session and forgets the profile's credentialWhere credentials are kept
The CLI keeps credentials in the operating system's secret store, and everything else in a config file.
| macOS | Linux | Windows and elsewhere | |
|---|---|---|---|
| Credentials | The login keychain, service furniops-cli | The Secret Service (GNOME Keyring, KWallet) through secret-tool, when it is installed and a session bus is running | credentials.json in the config directory, mode 0600 |
| Config | ~/.config/furniops/config.json | $XDG_CONFIG_HOME/furniops/config.json, else ~/.config/furniops/config.json | %APPDATA%\furniops\config.json |
- Secrets are handed to
securityandsecret-toolon standard input, never on a command line that other users could see inps. - The config directory is created 0700 and its files 0600, written whole through a temporary file and a rename, so a crash never leaves half a file.
FURNIOPS_CREDENTIAL_STOREpicks the store:keychain,secret-serviceorfile. Usefileon a headless Linux machine without a session bus.FURNIOPS_CONFIG_DIRmoves the whole config directory.
Profiles
A profile is a named set of settings and a credential, so you can keep, say, a live account and a test key side by side:
furniops login # the "default" profile
furniops login --profile sandbox --with-api-key # a test key, in "sandbox"
furniops trigger offer.changed --profile sandbox
FURNIOPS_PROFILE=sandbox furniops events tailA profile name is letters, digits, - and _. When you sign in with --api-url, --issuer or
--audience, the profile remembers them, so later commands go to the same place.
Global options
Every command takes these. Each setting is taken from the flag, then the environment, then the profile, then the default.
| Flag | Environment | Default | Meaning |
|---|---|---|---|
--profile <name> | FURNIOPS_PROFILE | default | The profile to use. |
--api-url <url> | FURNIOPS_API_URL | https://api.furniops.com | The API's base URL. |
--issuer <url> | FURNIOPS_ISSUER | https://auth.furniops.com | The sign-in server. |
--audience <uri> | FURNIOPS_AUDIENCE | the API URL's origin | The API audience tokens are asked for. |
--mode <mode> | FURNIOPS_MODE | the credential's | live or test. A signed-in session acts in live mode unless this says test; an API key's mode is its own, and naming the other one exits 2. |
--json | off | Print results as JSON: one object, or one per line for streams. | |
-v, --version | Print the version. | ||
-h, --help | Help for any command: furniops listen --help. |
Other environment variables: FURNIOPS_API_KEY (a credential, above), FURNIOPS_CONFIG_DIR and
FURNIOPS_CREDENTIAL_STORE.
Scripting
With --json, results go to standard output as JSON, and progress messages are not printed. A
failure prints one JSON object:
{ "message": "not signed in (profile default)", "code": "not_signed_in", "hint": "Run `furniops login`." }API failures add status and request_id. Without --json, a failure is one line on standard
error, such as Error: … [code, HTTP 403, request req_…], followed by a hint when there is one.
Exit codes
| Code | Meaning |
|---|---|
0 | Success, including --help and --version. |
1 | Any other error. |
2 | Usage: an unknown command, option or argument, or an invalid value. |
3 | Authentication: not signed in, the session expired or was revoked, or the API refused the credential. |
4 | Permission: signed in but not allowed; a missing scope, or a test-mode command with a live credential. |
5 | Network: the API or the sign-in server could not be reached. |
furniops whoami --json > /dev/null
case $? in
0) ;;
3) echo "sign in first: furniops login" >&2; exit 1 ;;
5) echo "FurniOps is unreachable; try again later" >&2; exit 1 ;;
*) exit 1 ;;
esac