Quoin docs

The quoin CLI

Change flags, run experiments, attach guards and declare metrics from a terminal.

Every quoin command has the same shape: a group, a verb, its key, and --env naming exactly one environment. There is no default environment: a command without --env refuses to run rather than guess. Metrics are the one exception; they belong to the workspace.

Every write shows what it will change and asks before it writes. --yes skips the question but never the printed change; with no terminal to ask in and no --yes, the command refuses and writes nothing. quoin flags off is the one write that never asks: it is the kill switch.

The CLI acts as you. Everything it writes is recorded in your name with cli beside it, in the flag's history and the audit log.

Running it

The CLI is not published to npm yet. Inside the Quoin repository, run it from source with Node 22.18 or later: node packages/cli/src/index.ts --help.

Signing in

quoin login --url <origin> opens <origin>/cli/authorize in your browser. Pick a workspace, create a CLI key, and paste it into the terminal; the page shows the key once. --token - reads a key from standard input instead, for a machine with no browser.

A CLI key is an operator key for you in one workspace. Revoke it under Settings → API keys. The CLI sends it only over https, or plain http to this machine.

quoin login

quoin login --url <origin>

Opens <origin>/cli/authorize, where you create a CLI key, and waits for you to paste it. The key is stored for this workspace.

Options
--url <origin>Where Quoin runs, like https://quoin.example.com
--token <key|->Paste the key instead of opening a browser; - reads it from standard input

quoin logout

quoin logout [--workspace <slug>] [--revoke]

Removes the stored CLI key for a workspace from this machine. With --revoke it first revokes the key in Quoin, then removes it.

Options
--revokeRevoke the key in Quoin first, then remove it here

Where the key is kept

For each command the CLI looks for a key in this order: the QUOIN_OPERATOR_KEY environment variable (it needs QUOIN_URL beside it, and the server says which workspace the key belongs to), then the key stored by quoin login, then a hidden prompt for that command only.

On macOS quoin login keeps the key in the Keychain. Elsewhere it keeps it in ~/.config/quoin/credentials.json, readable by you alone; a file is the weaker store, since any program running as you can read it. QUOIN_STORE=file or QUOIN_STORE=keychain picks one, and QUOIN_CONFIG_DIR moves the file.

To remove the key from this machine, run quoin logout (add --workspace <slug> for one that isn't current). That clears only this machine's copy; quoin logout --revoke first revokes the key in Quoin so it stops working anywhere, then removes it. If the revoke is refused, the key stays stored and the command says why. If the macOS Keychain refuses to delete the key (it is locked, or its prompt was denied), the key stays stored and the command exits with a sentence saying so; an entry whose Keychain item is already gone is removed, with a note that nothing could be revoked.

Signed in to more than one workspace? --workspace <slug> picks one; otherwise the last one you signed in to is used.

Every command

Options
--workspace <slug>A workspace you signed in to (default: the last one)
--helpShow this help

flags

List, read, explain and change flags

quoin flags list

quoin flags list --env <env>

Every flag and what it serves in that environment

Options
--env <env>The environment, like production — every command names one

quoin flags get

quoin flags get <key> --env <env>

One flag: its definition, its rules and the stored value

Options
--env <env>The environment, like production — every command names one

quoin flags history

quoin flags history <key> --env <env>

Who changed it, when, why, and what it held before

Options
--env <env>The environment, like production — every command names one
--limit <n>How many revisions (1 to 100, default 20)
--before <revision>Older than this revision, like r-0418

quoin flags explain

quoin flags explain <key> --env <env> [--user <id>] [--email <address>]

What one person gets and why, rule by rule; records nothing

Options
--env <env>The environment, like production — every command names one
--user <id>The user id to explain for
--email <address>People by email, comma-separated; @domain matches a whole domain
--tier <tier>A plan tier, comma-separated
--client <client>A client, comma-separated
--ownerWorkspace owners
quoin flags explain exports.csv_v2 --email mia@tessellate.io --env production

quoin flags doctor

quoin flags doctor --env <env>

Flags past their expiry date and stored values that no longer validate; exits 1 when it lists any

Options
--env <env>The environment, like production — every command names one
quoin flags doctor --env production

quoin flags rollout

quoin flags rollout <key> <percent> --env <env>

Serve the value to that share of people; the same people stay in as it rises

Options
--env <env>The environment, like production — every command names one
--note <text>Why, for the history (three characters or more)
--yesWrite without asking; the diff still prints
--value <json>The value to serve (JSON; true for a boolean when left out)
--email <a,b>People by email, comma-separated; @domain matches a whole domain
--user <id,…>People by user id, comma-separated
--tier <tier,…>A plan tier, comma-separated
--client <client,…>A client, comma-separated
--ownerWorkspace owners
--signed-up-before <day>Signed up before this day, YYYY-MM-DD
--signed-up-after <day>Signed up after this day, YYYY-MM-DD
--confirm "resume anyway"Type "resume anyway" to resume above the coverage a guard paused at
quoin flags rollout onboarding.checklist_v2 50 --env production
quoin flags rollout exports.csv_v2 25 --tier pro --note "pro first" --env production

quoin flags force

quoin flags force <key> <value> --env <env> --email <a,b>

Pin a value for named people, above every other rule

Options
--env <env>The environment, like production — every command names one
--note <text>Why, for the history (three characters or more)
--yesWrite without asking; the diff still prints
--email <a,b>People by email, comma-separated; @domain matches a whole domain
--user <id,…>People by user id, comma-separated
--tier <tier,…>A plan tier, comma-separated
--client <client,…>A client, comma-separated
--ownerWorkspace owners
--signed-up-before <day>Signed up before this day, YYYY-MM-DD
--signed-up-after <day>Signed up after this day, YYYY-MM-DD
quoin flags force checkout.new_summary --email @tessellate.io true --env production

quoin flags unforce

quoin flags unforce <key> --env <env> --email <a,b> | --rule <id>

Take a pin away

Options
--env <env>The environment, like production — every command names one
--note <text>Why, for the history (three characters or more)
--yesWrite without asking; the diff still prints
--email <a,b>People by email, comma-separated; @domain matches a whole domain
--user <id,…>People by user id, comma-separated
--tier <tier,…>A plan tier, comma-separated
--client <client,…>A client, comma-separated
--ownerWorkspace owners
--signed-up-before <day>Signed up before this day, YYYY-MM-DD
--signed-up-after <day>Signed up after this day, YYYY-MM-DD
--rule <id>The rule id to remove (quoin flags get shows them)

quoin flags set

quoin flags set <key> <value|@file> --env <env>

Store a flag's JSON as it is

Options
--env <env>The environment, like production — every command names one
--note <text>Why, for the history (three characters or more)
--yesWrite without asking; the diff still prints

quoin flags clear

quoin flags clear <key> --env <env>

Remove the stored rules: back to the default in code

Options
--env <env>The environment, like production — every command names one
--note <text>Why, for the history (three characters or more)
--yesWrite without asking; the diff still prints

quoin flags on

quoin flags on <key> --env <env>

Lift the kill switch; the rules serve again

Options
--env <env>The environment, like production — every command names one
--note <text>Why, for the history (three characters or more)
--yesWrite without asking; the diff still prints
--confirm "resume anyway"Type "resume anyway" to resume above the coverage a guard paused at
quoin flags on exports.csv_v2 --confirm "resume anyway" --env production

quoin flags off

quoin flags off <key> --env <env>

The kill switch: serves the default at once, keeps the rules, never asks

Options
--env <env>The environment, like production — every command names one
--note <text>Why, for the history (three characters or more)
quoin flags off exports.csv_v2 --env production

quoin flags revert

quoin flags revert <key> --to <revision> --env <env>

Go back to what a revision stored; the revert is itself a new revision

Options
--env <env>The environment, like production — every command names one
--note <text>Why, for the history (three characters or more)
--yesWrite without asking; the diff still prints
--to <revision>The revision to go back to, like r-0418
--confirm "resume anyway"Type "resume anyway" to resume above the coverage a guard paused at
quoin flags revert onboarding.checklist_v2 --to r-0418 --env production

quoin flags promote

quoin flags promote <key> --from <env> --to <env>

Copy a flag's rules from one environment to another, showing both halves first

Options
--note <text>Why, for the history (three characters or more)
--yesWrite without asking; the diff still prints
--from <env>The environment to copy from
--to <env>The environment to copy to
--confirm "resume anyway"Type "resume anyway" to resume above the coverage a guard paused at
quoin flags promote exports.csv_v2 --from staging --to production

experiments

Plan, start, read, stop and conclude experiments

quoin experiments list

quoin experiments list --env <env>

Every experiment in that environment, its status and how it reads

Options
--env <env>The environment, like production — every command names one

quoin experiments status

quoin experiments status [key] --env <env>

What is running or stopped and how each reads today; with a key, one experiment

Options
--env <env>The environment, like production — every command names one

quoin experiments get

quoin experiments get <key> --env <env>

One experiment: its pre-registered design and where it stands

Options
--env <env>The environment, like production — every command names one

quoin experiments report

quoin experiments report <key> --env <env>

The report: health checks first, then the verdict and the sequential 95% intervals

Options
--env <env>The environment, like production — every command names one
--phase <n>An earlier phase, by number
quoin experiments report trial-reminders --env production

quoin experiments new

quoin experiments new <key> --flag <flag> --metric <metric> --env <env>

Plan an experiment on a flag and save it as a draft; nothing runs until start

Options
--env <env>The environment, like production — every command names one
--flag <flag>The flag it runs on (boolean flags only)
--metric <metric>The primary metric, like activation_rate.7d
--hypothesis <text>What will change, for whom, and why you think so
--guardrail <key[:margin],…>Guardrail metrics, comma-separated; key:margin sets a margin, like errors.1d:0.5
--decision-rule <text>What result ships it (default: composed from the metric and the guardrails)
--mde <percent>The smallest change worth detecting, in percent (default: what the plan can detect)
--weights <a/b>The split, control first, like 50/50
--coverage <percent>The share of eligible people in the experiment, in percent (default 100)
--yesWrite without asking; the diff still prints
quoin experiments new trial-reminders-v2 --flag billing.trial_reminder_cadence --metric activation_rate.7d --hypothesis "Two reminders instead of three keep more trials without more cancellations" --env production

quoin experiments start

quoin experiments start <key> --env <env>

Lock the plan and start; the flag's rules freeze until it stops

Options
--env <env>The environment, like production — every command names one
--weeks <n>How long it runs (default: the weeks the plan chooses)
--anywayStart even though the plan warns
--yesWrite without asking; the diff still prints
quoin experiments start trial-reminders-v2 --weeks 4 --env production

quoin experiments stop

quoin experiments stop <key> --reason <text> --env <env>

Stop counting exposures; the record and the report stay

Options
--env <env>The environment, like production — every command names one
--reason <text>Why it stops, for the record
--yesWrite without asking; the diff still prints
quoin experiments stop digest-send-time --reason "sample ratio" --env production

quoin experiments conclude

quoin experiments conclude <key> --ship <variation> | --rollback | --inconclusive --why <text> --env <env>

Write the permanent decision record: ship a variation, roll back, or inconclusive

Options
--env <env>The environment, like production — every command names one
--ship <variation>Ship this variation
--rollbackRoll back to control
--inconclusiveRecord that it settled nothing
--why <text>Why, for the decision record
--confirm-harm <metric>Ship over a harmed guardrail: type the guardrail's name
--yesWrite without asking; the diff still prints
quoin experiments conclude trial-reminders --rollback --why "guardrail harm confirmed, whole interval outside ±0.5pt margin" --env production
quoin experiments conclude instant-search-results --ship treatment --why "clear lift, no guardrail harmed" --env production

quoin experiments discard

quoin experiments discard <key> --env <env>

Throw a draft away; anything that ran keeps its record

Options
--env <env>The environment, like production — every command names one
--yesWrite without asking; the diff still prints

guards

Attach guards that pause a rollout when a rate passes its ceiling

quoin guards list

quoin guards list --env <env>

Every guard in that environment and its state

Options
--env <env>The environment, like production — every command names one
--flag <flag>Only this flag

quoin guards attach

quoin guards attach <flag> <name> --event <name> --numerator <event> --denominator <event> --threshold <pct> --baseline <pct> --checks <n> --env <env>

Watch a rate on a flag, checked every hour; past its ceiling for enough checks in a row, it pauses the rollout

Options
--env <env>The environment, like production — every command names one
--event <name>The signal: event name for the guard, like error_rate
--window <1h|1d|7d>The window the rate is read over: 1h, 1d or 7d
--numerator <event>Events counted on top of the rate
--denominator <event>Events counted under the rate
--threshold <percent>Pause above this rate, in percent, like 1.2
--baseline <percent>The usual rate, in percent, like 0.6
--checks <n>Consecutive hourly checks over the ceiling before it pauses (1 to 24)
--note <text>Why, for the audit log (three characters or more)
--yesWrite without asking; the diff still prints
quoin guards attach exports.csv_v2 csv-errors --event error_rate --numerator export_failed --denominator export_started --window 1d --threshold 1.2 --baseline 0.6 --checks 2 --note "watch the widening" --env production

quoin guards detach

quoin guards detach <flag> <name> --env <env>

Stop watching; a guard that has paused its flag stays until you resume or roll back

Options
--env <env>The environment, like production — every command names one
--note <text>Why, for the audit log (three characters or more)
--yesWrite without asking; the diff still prints

metrics

List, read and declare the metrics experiments measure

quoin metrics list

quoin metrics list

Every metric in the workspace; metrics aren't per environment

Options
--env <env>Accepted and ignored: metrics belong to the workspace, not an environment

quoin metrics get

quoin metrics get <key>

One metric: what it counts, over how long, and which way is better

Options
--env <env>Accepted and ignored: metrics belong to the workspace, not an environment

quoin metrics declare

quoin metrics declare <key> --kind <kind> --event <name> --window <days> --better <higher|lower>

Declare a metric from an event you send

Options
--env <env>Accepted and ignored: metrics belong to the workspace, not an environment
--title <text>The name people read (default: the key)
--kind <binomial|count>binomial (did it happen) or count (how many times)
--event <name>The event it counts, like item_saved
--window <days>Days after first exposure the metric reads over (1 to 90)
--better <higher|lower>Which way is better: higher or lower
--winsorize <fraction>For a count, cap outliers at this quantile, like 0.99
--anywayDeclare it before the event has arrived; it reads 0 until it does
--yesWrite without asking; the diff still prints
quoin metrics declare items_saved.14d --kind count --event item_saved --window 14 --better higher

Exit codes

Code
0Done, or nothing needed doing.
1Quoin refused the change, the key was not accepted, or flags doctor found something. The reason is printed as Quoin wrote it.
2The command itself was wrong: a missing --env, an unknown option, or nothing to ask in.

Every command is copyable

Wherever the web app shows a command — a flag's page, an experiment's report, Home — it is a chip with the key and the environment already filled in, and the CLI runs it as shown. A test runs every chip the app and these docs print through the CLI's parser.

Next

On this page