CLI

The nodd command sends notifications and waits for responses. Exit codes make it easy to use from a shell.

Install

brew install blipdrop/tap/nodd

You can also download a binary from Releases.

Log in

nodd login

A QR code appears. Scan it under Settings > Log in to CLI in the app and allow it with Face ID within 10 minutes. The send key is saved in ~/.config/nodd/config.json, or under XDG_CONFIG_HOME if set. --name sets the key name, and the default is the host name.

In CI, use environment variables instead of logging in.

NODD_KEYSend key. Takes priority over the config file
NODD_API_URLAPI URL. Default https://api.nodd.blipdrop.net
NODD_PROFILE, --profileConfig profile name, one for each channel if you use several. Default default

Commands

nodd loginGets a send key by QR code
nodd logoutRemoves the key from this computer. Revoke it in the app
nodd whoamiShows the profile, the last four characters of the key, and the API mode
nodd send TITLESends a notification and prints its ID
nodd ask TITLEAsks for approval or a choice and waits
nodd wait IDWaits for the response to a sent message. Takes --json
nodd status IDPrints the state as JSON
nodd cancel IDCancels a pending question
nodd done IDReports the result. --status succeeded|failed (default succeeded) and --summary
nodd resolve ALERT_KEYResolves an alert. --note
nodd heartbeat NAME --every SECONDSReports that a job is alive and prints the next due time. --every is 60 to 2,678,400. --grace defaults to 10% of --every
nodd mcpRuns an MCP server over stdio. See MCP and hooks
nodd hookThe Claude Code PreToolUse hook

Options for send and ask

--body TEXTBody. - reads standard input
--level low|normal|highPriority
--field LABEL=VALUEA field, up to 6 times
--link LABEL=URLA link, up to 2 times
--sensitiveHides the body on the lock screen
--markdownFormats the body as Markdown and allows 4,000 characters
--image FILEAttaches a JPEG or PNG up to 40 KB. Add a description with --image-alt
--jsonPrints the result as JSON

nodd send also takes --kind notice|alert and --alert-key.

nodd ask

--option ID=LABELAn option, 2 to 4 times. Asks for a choice instead of approval
--expires-in SECONDSResponse deadline. Default 3600
--input PROMPTShows a text field. Read the answer from stderr or --json. Add --input-required to require it
--secretAsks for a password or other secret in a masked field. The value arrives encrypted to a key that exists only for this run, and only the value goes to stdout. It refuses to print to a terminal, so pipe it or use --env
--env NAME -- COMMANDRuns COMMAND with the value in an environment variable instead of printing it. Use this with AI agents so they never see the value
--approvals NPeople who must approve, 1 to 3. More than 1 needs Team
--escalate-after SECONDSTells other people after this many seconds without a response. Team only
--no-waitPrints the send result without waiting

Answering --input or --secret needs Nodd 1.1 or later. MCP has no equivalent of --secret. Have agents use --env so the value never reaches the model.

nodd ask "Deploy to production?" --approvals 2 --escalate-after 600 --expires-in 3600 && ./deploy.sh
nodd ask "Where should this go?" --option staging=Staging --option prod=Production
nodd ask "Production DB password" --secret --env PGPASSWORD -- psql -h db.example.com app

Standard output gets approved, rejected, the chosen option ID, or a state such as expired. A rejection reason goes to standard error as Note: reason, so standard output stays safe to parse.

Exit codes

These apply to nodd ask and nodd wait.

0Approved, or an option was chosen
10Rejected
11Ended without a response (expired, cancelled, or failed)
12Timed out while waiting
1Any other failure

It waits until 15 minutes after the response deadline. Network failures do not end the wait, and only a recorded response counts as a result.

Monitoring a scheduled job

0 3 * * * /usr/local/bin/backup.sh && nodd heartbeat nightly.backup --every 86400

If the next report does not arrive within --every plus --grace seconds, the channel is told.