CLI reference
The agentnotify command-line client talks to the local AgentNotify broker over HTTP. Every command except health (unauthenticated probe fallback), token, and relay requires the broker to be running and a valid bearer token. Relay commands talk directly to the configured Relay and local protected provider store.
Binary name:
agentnotify.exeon Windows (%LOCALAPPDATA%\Programs\AgentNotify\agentnotify.exeafter installation, added to the current user'sPATH).- Reachable from WSL as
agentnotify.exe. After installation open a new WSL shell so the updated WindowsPATHis imported.
Source: src/AgentNotify.Cli/Program.cs.
Invocation
agentnotify <command> [options]
agentnotify "Title" ["Message"] [options] shorthand for send
agentnotify --help | -h | help [<command>]
agentnotify --version
When no command is given the usage text is printed and the process exits 0. When the first argument does not match a known command (send, list, get, resolve, dismiss, health, relay, token, install-skill, install, help/--help/-h, --version) it is treated as a positional send invocation.
All commands that contact the broker use a 10-second HTTP timeout. Connection failure prints to stderr and exits non-zero (see Exit codes).
Common options
--port and --token are accepted by every broker command. They override file and environment discovery for a single invocation.
| Flag | Short | Value | Required | Default |
|---|---|---|---|---|
--port |
— | integer | no | 47821 from %LOCALAPPDATA%\AgentNotify\config.json port, or AGENTNOTIFY_PORT when ConfigStore is constructed with environment overrides |
--token |
— | string | no | authToken from the same config file, or AGENTNOTIFY_TOKEN (see Authentication) |
Token resolution for broker calls (src/AgentNotify.Cli/Program.cs):
--tokenwhen supplied.AGENTNOTIFY_TOKENenvironment variable whenConfigStore(applyEnvOverrides: true)is used.authTokenfrom%LOCALAPPDATA%\AgentNotify\config.json.
If no token is available a warning is written to stderr but the request is still sent; the broker replies 401.
Base URL is http://127.0.0.1:{port}.
--help / -h on send and list prints command help and exits 0.
Value parsing
Type identifiers
--type values are normalized by AgentNotify.Protocol.NotificationTypes.Normalize (src/AgentNotify.Protocol/NotificationTypes.cs):
- Trim, replace
-with_, lower-case. - Map
inputrequiredtoinput_requiredandpermissionrequiredtopermission_required. - Accept only
^[a-z][a-z0-9_]{0,63}$(1–64 characters, starting with a letter). Otherwise--typefails with:--type must be a valid identifier containing letters, numbers, underscores, or hyphens (maximum 64 characters).
Built-in IDs: info, success, warning, error, input_required, permission_required, completed, blocked. Custom IDs use the same rule; hyphen and underscore spellings are equivalent.
Priority and status enums
Enum flags use Program.TryParseEnum (src/AgentNotify.Cli/Program.cs):
value.Replace('-', '_').Replace("_","") then Enum.TryParse(ignoreCase:true)
That makes high, HIGH, high-priority styles accepted as long as letters match. status filtering on list uses the same normalization in src/AgentNotify.Api/ApiHost.cs.
Valid priorities for --priority: low, normal, high, critical. On failure: --priority must be low, normal, high, or critical.
Valid statuses: active, dismissed, resolved (see src/AgentNotify.Protocol/NotificationStatus.cs).
Commands
send — create a notification
Creates a notification or, when key matches an active notification, updates it in place.
agentnotify send --title T --message M [options]
agentnotify "Title" ["Message"] [options]
agentnotify --title T --msg M [options]
| Flag | Value | Required | Default | Notes |
|---|---|---|---|---|
--title |
text | yes (or first positional) | — | Trimmed, 1–200 characters |
--message / --msg |
text | yes (or second positional) | — | Trimmed, 1–4000 characters |
--type |
identifier | no | info |
Normalized as above |
--priority |
enum | no | type default or normal |
low/normal/high/critical |
--agent |
string | no | AGENTNOTIFY_AGENT or cli |
Trimmed, max 100 |
--agent-instance |
string | no | — | Trimmed, max 100 |
--project |
string | no | — | Trimmed, max 200 |
--key |
string | no | — | Trimmed, max 100; triggers deduplication |
--cwd |
path | no | Directory.GetCurrentDirectory() |
Trimmed, max 1024 |
--pid |
integer | no | — | Parsed with long.TryParse; invalid value becomes absent |
--port |
integer | no | 47821 |
As above |
--token |
string | no | file/env | As above |
Positional shorthand (src/AgentNotify.Cli/Program.cs):
- First positional sets
--titlewhen--titlewas not supplied. - Second positional sets
--messagewhen--messagewas not supplied. - A single positional sets both title and message to the same value.
Other behavior:
agentdefaults toAGENTNOTIFY_AGENTorcli.cwddefaults to the current directory (failure to read returns no value).- Unknown
sendflags produceunknown option '<flag>' for send. Run 'agentnotify help send'.
Output: on success the broker returns 201 with a NotificationDto; the CLI pretty-prints the JSON with indentation to stdout. On failure it prints Error {code} {Status}: {error} to stderr where {error} is the broker's { "error": "..." } field or the raw body.
Examples:
# Explicit flags
agentnotify.exe send --title "Build done" --message "All tests passed" --type success
# Positional shorthand
agentnotify.exe "Need input" "Which branch should I use?" --type input_required --key my-task
# With metadata derived from environment
AGENTNOTIFY_AGENT=opencode agentnotify.exe send --title "Task blocked" --message "Missing SDK" --type blocked --priority high --project myrepo --key build-42
list — list notifications
agentnotify list [options]
| Flag | Value | Required | Default | Notes |
|---|---|---|---|---|
--unresolved |
true/false or no value |
no | not filtered | Bare --unresolved means true. With a value, parsed by bool.TryParse |
--type |
identifier | no | — | Exact normalized type filter |
--status |
enum | no | — | active/dismissed/resolved (overridden by --unresolved true) |
--project |
string | no | — | Exact trimmed match |
--agent |
string | no | — | Exact trimmed match |
--limit |
integer | no | 20 |
Parsed by int.TryParse; server clamps 1–500 (default 100 when absent) |
--json |
— | no | false |
When present, output raw JSON instead of table |
--port |
integer | no | 47821 |
As above |
--token |
string | no | file/env | As above |
Unknown flags produce unknown option '<flag>' for list.
Output:
- Without
--json: zero or more lines"{id} [{type}/{priority}] {status} {title} ({agent})"or(no notifications). - With
--json: pretty-printed JSON arrayNotificationDto[]. - On HTTP error:
Error {code} {Status}: {error}to stderr.
Examples:
agentnotify.exe list --unresolved --limit 20
agentnotify.exe list --type error --status active --json
agentnotify.exe list --project myrepo --agent opencode --limit 50
get — fetch one notification by id
agentnotify get <id> [--port N] [--token T]
<id>is required and must not start with-. Otherwise:get requires an <id>. Usage: agentnotify get <id>.- Only
--portand--tokenare accepted after the id.
Output: pretty-printed NotificationDto on success. On failure Error {code} {Status}: {error} to stderr.
Example:
agentnotify.exe get 9e8a3f2b7c6d4e5f8a1b2c3d4e5f6a7b8
resolve — mark a notification resolved
agentnotify resolve <id> [--port N] [--token T]
<id>required; same validation asget.- Implemented as
PATCH /v1/notifications/{id}with{ "status": "resolved" }(src/AgentNotify.Cli/Program.cs).
Output: pretty-printed updated NotificationDto on success.
Example:
agentnotify.exe resolve 9e8a3f2b7c6d4e5f8a1b2c3d4e5f6a7b8
dismiss — dismiss a notification
agentnotify dismiss <id> [--port N] [--token T]
<id>required.- Tries
POST /v1/notifications/{id}/dismiss; on404falls back toPATCHwithdismissed(src/AgentNotify.Cli/Program.cs).
Output: pretty-printed updated NotificationDto on success.
Example:
agentnotify.exe dismiss 9e8a3f2b7c6d4e5f8a1b2c3d4e5f6a7b8
health — check broker health
agentnotify health [--port N] [--token T]
Authentication detection (src/AgentNotify.Cli/Program.cs):
wantAuthis true when--tokenis supplied or a token file exists (ConfigStore(applyEnvOverrides: false).Load().AuthTokenis non-empty).- When
wantAuthis true the CLI callsGET /v1/health(authenticated). On401it falls back toGET /healthand prints the unauthenticated body. - When
wantAuthis false it callsGET /healthdirectly.
Output:
- Success: pretty-printed JSON. Authenticated shape is
HealthResponse(status,version,pid,uptimeSeconds,activeCount,apiVersion,serverTimeUtc). Unauthenticated shape is{ "status": "ok" }. - On
HttpRequestException:Could not reach AgentNotify at http://127.0.0.1:{port}: {message}plusIs the app running? Check the tray icon.to stderr.
Example:
agentnotify.exe health
agentnotify.exe health --port 47821 --token "$TOKEN"
token — print the local bearer token
agentnotify token
Reads ConfigStore().Load() (default %LOCALAPPDATA%\AgentNotify\config.json). On success prints the token to stdout with no additional formatting. Failures:
No token found. Has AgentNotify run at least once? Look at: {ConfigPath}to stderr.- Any exception message to stderr.
Example:
TOKEN="$(agentnotify.exe token)"
curl -H "Authorization: Bearer $TOKEN" http://127.0.0.1:47821/v1/health
relay — pair and verify Relay providers
agentnotify relay pair --url URL [--name NAME] [--sender-name NAME] [--allow-private] [--json]
agentnotify relay status [--json]
relay pair uses the same device-authorization client as the Windows Settings panel. It validates
the base URL, checks /.well-known/agentnotify-relay for API v1, creates a sender pairing request,
prints the verification URL and short code, polls until approval, verifies the returned installation,
and writes the provider configuration and credential together through ProviderProfileService.
The credential is never printed. A matching Relay URL updates its existing profile; a new profile is
created disabled so delivery remains opt-in.
| Flag | Value | Default | Notes |
|---|---|---|---|
--url |
absolute URL | — | Required; HTTPS except HTTP localhost development |
--name |
text | verified Relay/display name | Provider profile name, at most 100 characters |
--sender-name |
text | machine name | Label visible to the Relay and paired devices |
--allow-private |
— | false | Explicit consent for private/loopback destinations; required for HTTP localhost |
--json |
— | false | Writes one compact JSON object per state transition to stdout |
Plain output writes the verification URL and code to stdout and countdown updates to stderr. Press
Ctrl+C to cancel. Polling honors Relay slow_down, tolerates four consecutive transient network
failures, and stops on rejection, expiry, consumption, invalid authentication, or cancellation.
relay status loads every saved Relay provider and verifies its protected credential through
GET /v1/installation. It never displays the credential. Without --json, each line reports the
profile name, connection state, verified installation display name/ID, and whether the provider is
disabled. With --json, it emits one object per profile.
Examples:
agentnotify relay pair --url https://relay.example.com --name "Home relay"
agentnotify relay pair --url http://localhost:4000 --allow-private --json
agentnotify relay status
install-skill — install the bundled agent skill
agentnotify install-skill <codex|claude> [options]
agentnotify install skill <codex|claude> [options]
The skill payload is embedded in the CLI, so installation is offline and needs no npm, Python, or separate download.
| Flag | Value | Default | Notes |
|---|---|---|---|
--scope |
user or project |
user |
Select personal or current-repository discovery |
--path |
directory | agent-specific skills root | Overrides --scope; the agentnotify folder is created beneath it |
--force |
— | false | Replaces changed AgentNotify-owned files after explicit review |
--dry-run |
— | false | Reports the destination without writing |
Default personal destinations:
Codex: ~/.agents/skills/agentnotify
Claude Code: ~/.claude/skills/agentnotify
Codex installs SKILL.md and agents/openai.yaml; Claude Code installs SKILL.md. Identical files
are treated as already up to date. A changed existing file is never overwritten unless --force is
passed. Files outside the agentnotify skill directory are never modified.
Examples:
agentnotify install-skill codex
agentnotify install-skill claude --scope project
agentnotify install-skill codex --dry-run
agentnotify install-skill codex --path /custom/skills/root
help and --version
agentnotify help [send|list|get|resolve|dismiss|relay|install-skill]
agentnotify --help
agentnotify -h
agentnotify --version
help <topic>prints the topic help (send,list,get,resolve,dismiss,relay,install-skill). Unknown topic prints the general usage.helpwith no topic prints general usage (PrintUsage).--version(RunVersion) printsagentnotify {InformationalVersion}derived from the CLI assembly.
Exit codes
| Code | Meaning | When |
|---|---|---|
0 |
Success | Command completed; also returned when no arguments are given (usage printed) or --help/--version was requested |
1 |
General failure | Validation error, unknown option, missing required argument, connection failure, request timeout, or any HTTP error not covered below |
2 |
Authentication failure | send received 401 or 403 (HandleCreateResponse maps 401/403 to 2) |
3 |
Not found | get, resolve, or dismiss/PATCH returned 404 |
Additional failure messages written to stderr:
Could not reach AgentNotify: {message}/Is the tray app running?onHttpRequestException(top-level catch,src/AgentNotify.Cli/Program.cs).AgentNotify did not respond before the request timed out.onTaskCanceledException.Could not reach AgentNotify at {baseUrl}: {message}/Is the app running? Check the tray icon.forhealth.
Environment variables
| Variable | Used by | Effect |
|---|---|---|
AGENTNOTIFY_AGENT |
send |
Default for --agent when the flag is absent |
AGENTNOTIFY_PORT |
all broker commands | Overrides port from config.json (ConfigStore.cs) |
AGENTNOTIFY_TOKEN |
all broker commands | Overrides authToken from config.json (ConfigStore.cs) |
Error payload
Broker errors are JSON { "error": "<message>" }. The CLI extracts the error field for the Error {code} {Status}: {message} line; otherwise it prints the raw body or (empty response).