herald CLI

herald is a command line tool that sends notifications to Herald and controls it from a shell. It is a thin client over the HTTP API: it finds the running app, adds the bearer token, sends one request and prints the reply. Use it from scripts, cron jobs, build steps and terminals where writing a curl command is more work than the task. Every command below names the endpoint it calls, so the API reference documents the fields in full.

Install the tool#

The tool ships inside Herald.app at Herald.app/Contents/Helpers/herald. That copy is always the one that matches the installed app, and you can run it by its full path. To put a copy on your shell path:

  1. Open Herald's Settings and choose the MCP tab.
  2. Under Command line tool, press Install herald command line tool.
  3. If macOS asks for an administrator password, enter it. The tool is copied to /usr/local/bin/herald, which needs it when that folder is not writable by you.

The installer makes a copy, not a link, so the copy on your path does not change when you update Herald. To bring a stale copy up to date, press the same button again: it replaces /usr/local/bin/herald with the copy inside the app. To see whether the two differ, compare their versions:

shell
herald --version
/Applications/Herald.app/Contents/Helpers/herald --version

The MCP server is different: the Settings > MCP installers point your agent at the copy inside the app, so it is always current.

Check the result in a terminal:

shell
herald health
JSON
{
  "ok" : true,
  "pid" : 69080,
  "version" : "1.8.1"
}

herald health prints the version of the running app. Building from source with make install-cli installs the same tool:

  • to /usr/local/bin, or
  • to ~/bin when /usr/local/bin is not writable.
The MCP tab: the button for the command line tool sits next to the MCP server installers.
Herald Settings on the MCP tab with the button that installs the command line tool

The MCP tab: the button for the command line tool sits next to the MCP server installers.

How the tool finds Herald#

Herald writes its port and bearer token to its support folder, ~/Library/Application Support/Herald/, in the files port and token. The tool reads them on every call, so you never pass them by hand. If the port file is missing, the tool uses port 48617.

Replies are JSON. The tool prints them pretty-printed with sorted keys. Commands that write a file (template export, template import) print one line of text instead.

Global options#

Global options work in front of or after any command.

OptionTypeDescription
--port NintegerThe port to call. A number from 1 to 65535. --port=N works too. Default: the port file in the support folder, else 48617.
--token TstringThe bearer token. Default: the token file. Prefer the file, because a flag shows up in the output of ps.
--json FILEpathA JSON object to use as the request body, or - to read it from standard input. Other flags are merged on top of it. Only notify and register take it.
-h, --helpflagPrint the usage text. herald help <command> prints the same text.
--versionflagPrint herald 1.1.0. The string is fixed in the tool; herald health reports the app's own version.

Every value option also accepts the form --option=value.

HERALD_SUPPORT_DIR is an environment variable, not an option. It points the tool at another Herald's support folder, such as the second instance used for testing.

Exit codes#

CodeMeaning
0The request succeeded. The reply is on standard output.
1The command line was wrong, or Herald answered with an HTTP error. The output of each case is listed under the table.
2Herald is not running. The tool prints Herald is not running. Launch Herald.app first. to standard error.

What a failure with code 1 prints:

  • A usage error prints herald: <message> and Try 'herald --help'. to standard error.
  • An HTTP error prints Herald's reply to standard output.
  • A 401 adds herald: unauthorized (check the token file or --token) to standard error.

A script can wait for Herald to start:

shell
until herald health >/dev/null 2>&1; do sleep 1; done

Commands#

CommandWhat it does
herald notifyShow a notification.
herald registerRegister or update an app.
herald speakSay text aloud with no banner.
herald composeOpen the Composer window.
herald snoozeHide a banner and bring it back later.
herald unsnoozeBring a snoozed banner back now.
herald dismissClose one banner.
herald dismiss-allClose every banner of an app, or one stack.
herald stacksList the stacks on screen.
herald quietStart quiet hours for a while.
herald quiet statusShow whether quiet hours are active.
herald quiet offEnd the current quiet period.
herald historyShow or clear an app's History.
herald history searchSearch History.
herald history reshowShow a past notification again.
herald history deleteDelete one History item.
herald history exportExport History as JSON.
herald template listList saved templates.
herald template putSave a template from a JSON file.
herald template deleteDelete a template.
herald template duplicateCopy a template.
herald template renameRename a template.
herald template defaultSet or clear an app's default template.
herald template exportWrite a template and its animations to a bundle file.
herald template importAdd a bundle file's template and animations.
herald manifest deleteDelete an app's manifest.
herald appsList registered apps.
herald apps settingsShow per-app settings.
herald apps setChange per-app settings.
herald approvalsList template command approvals.
herald approvals revokeWithdraw a template command approval.
herald settingsShow Herald's settings.
herald settings setChange Herald's settings.
herald assets listList an app's Rive files and images.
herald assets addAdd a Rive file or an image to an app.
herald assets rmRemove a Rive file or an image.
herald symbolsSearch SF Symbol names.
herald voiceShow or manage the Kokoro voice engine.
herald mcpShow or install the MCP server in a client.
herald healthCheck that Herald is running.

The tool has no command that saves a manifest or reads the component schema. Use the API, the MCP server or the Designer for those. No command grants an approval for buttons that run commands or call back: only the person at the Mac does that, in Settings.

Sending#

herald notify#

Shows a notification as a banner and stores it in History. Sending the same --id again replaces the banner in place. There are three ways to give the content:

  • --app and --title.
  • --app and --template, when the template supplies the title.
  • The whole notification as JSON with --json. Flags are merged on top of the JSON.

Options

OptionTypeDescription
--app IDstringThe app id. Required, here or in --json.
--id IDstringYour id for the notification. Reusing an id replaces the banner on screen.
--title TstringThe headline. Required unless --template is given.
--subtitle TstringA second line under the title.
--body TstringThe main text.
--image PATH|URL|data:stringA picture: a file path, an https URL or a data: URI.
--url URLstringA link the banner opens when clicked.
--group GstringThe stack key. Banners of one app with the same group fold into one stack.
--sound NAME|PATH|nonestringThe sound to play, a file, or none.
--timeout SECONDSnumberSeconds before the banner closes itself. 0 keeps it until it is closed.
--persistent, --no-persistentflagKeep the banner until it is closed, or not.
--snooze, --no-snoozeflagShow the snooze menu on the banner, or hide it.
--priority PstringOne of low, normal, high, urgent. urgent breaks quiet hours only for apps that allow it.
--button "Label=target"stringAdd a button. Repeat the option for more. See the targets below.
--follow-up-after DURATIONstringHow long the banner may go unattended before the action runs: seconds, or 90s, 10m, 2h. From 5 seconds to 7 days.
--follow-up-shortcut NAMEstringThe follow-up runs this Apple Shortcut.
--follow-up-script FILEstringThe follow-up runs this file from Herald's scripts folder.
--follow-up-command CMDstringThe follow-up runs this shell command.
--follow-up-ref IDstringThe follow-up runs the action of this id, or label, that the notification offers.
--follow-up-input TEXTstringText for the Shortcut or script, with {tokens} filled. Not valid with a command or a reference.
--reminder "Title|ISO8601"stringAdd an Add-to-Reminders action. The text is split on the last |; a bare string is only a title.
--metadata JSONJSONAn object of extra values. Templates read it for their {tokens}.
--template NAMEstringA saved template for this app.
--layout LstringOne of imageLeft, imageRight, hero, compact.
--accent #RRGGBBstringThe accent colour for the title, buttons and links.
--no-subtitle, --no-body, --no-timeflagHide the subtitle, the body or the timestamp.
--max-body-lines NintegerThe most body lines to show. A positive integer.
--speakflagSay the title, then the body, aloud.
--speak-text TstringSay T instead of the title and body.
--voice NAMEstringThe voice to speak with.
--speed NnumberSpeaking speed, from 0.5 to 2.0.
--lang LstringThe language code, such as en-us.
--audio PATH|URL|data:stringA recorded voice message to play. At most 20 MB.
--presentation PstringOne of banner, voice, both. voice speaks without a banner and keeps the History entry.

The --button target decides what the button does:

Target formWhat the button does
Label=https://example.comOpens the URL.
Label=cmd:shell commandRuns the command. The app must be allowed to run commands, scripts and Shortcuts.
Label=script:file.shRuns a file from Herald's scripts folder. The app must be allowed to run commands, scripts and Shortcuts.
Label=shortcut:NameRuns an Apple Shortcut. The app must be allowed to run commands, scripts and Shortcuts.
Label=cb:{"k":1}Sends the JSON as the payload of a callback to the app's callback URL. cb: alone sends an empty payload.

The speech options build the notification's speak field. See Voice for the field and its limits.

The --follow-up-* options build the notification's followUp object. Give --follow-up-after together with exactly one of --follow-up-shortcut, --follow-up-script, --follow-up-command and --follow-up-ref. How a follow-up works is in Follow-ups.

Exampleshell
herald notify --app example.bidbot --id bid-42 \
  --title "Bid accepted" --body "Your bid of \$4,200 was accepted" \
  --url "https://example.com/bids/42" \
  --button "Open=https://example.com/bids/42" --sound Glass --snooze
OutputJSON
{
  "id" : "bid-42",
  "ok" : true
}

The same notification from JSON on standard input:

shell
echo '{"app":"example.bidbot","title":"Bid accepted","buttons":[{"label":"Open","url":"https://example.com/bids/42"}]}' \
  | herald notify --json -

Exit status

  • 1 with herald: notify needs --app or herald: notify needs --title (or --template) when those are missing.
  • 1 with a message such as herald: a follow-up needs --follow-up-after (seconds, or 90s | 10m | 2h) when the follow-up options are incomplete, out of range or name the action twice.
  • 1 and Herald's error when it rejects the notification.

HTTP route

POST /v1/notify

herald register#

Registers an app, or updates one that exists: its name, icon, callback address and default banner settings. You do not have to register an app before it sends, because Herald registers an unknown id on its first notification. Register when you want a proper name and icon, a callback URL, or defaults.

Options

OptionTypeDescription
--app IDstringThe app id. Required, here or in --json.
--name NAMEstringThe name shown on banners (appName).
--icon PATH|data:stringThe app's icon: a file path or a data: URI.
--bundle-id IDstringThe app's bundle identifier, used by actions that open the app.
--callback-url URLstringWhere Herald sends the payload of a callback button.
--allow-commands, --no-allow-commandsflagAsk to let the app's buttons run commands. This is a request: the person confirms it in Settings.
--sound NAMEstringThe default sound for the app's banners.
--timeout SnumberThe default timeout in seconds.
--persistent, --no-persistentflagThe default for keeping banners until they are closed.
--corner CstringWhere the app's banners appear: topRight, topLeft, bottomRight or bottomLeft.
--json FILE|-pathA base JSON object. The flags above are merged on top.
Exampleshell
herald register --app example.bidbot --name BidBot --icon ~/Pictures/bidbot.png \
  --callback-url http://127.0.0.1:5123/herald --sound Glass --corner topRight
OutputJSON
{
  "ok" : true
}

Exit status

1 with herald: register needs --app when the app id is missing.

HTTP route

POST /v1/register

herald speak#

Says text aloud with no banner. The text is still stored in History. Use it when a spoken message is all you need.

Options

OptionTypeDescription
--app IDstringThe app id. Required.
--text TstringWhat to say. Required.
--voice NAMEstringThe voice to use.
--speed NnumberSpeaking speed, from 0.5 to 2.0.
--lang LstringThe language code, such as en-us.
--id IDstringYour id for the notification.
Exampleshell
herald speak --app example.bidbot --text "Deploy finished" --speed 1.1
OutputJSON
{
  "id" : "7C1D0E52-3F0B-4B8E-9A55-0F3A1C0D2E11",
  "ok" : true
}

HTTP route

POST /v1/speak

herald compose#

Opens the Composer window in Herald, where a person fills in a notification by hand and sends it. It takes no options.

Exampleshell
herald compose
OutputJSON
{
  "ok" : true
}

HTTP route

POST /v1/compose

Closing and snoozing banners#

herald snooze#

Hides a banner and shows it again after a number of minutes. The banner keeps its id.

Options

OptionTypeDescription
--app IDstringThe app id. Required.
--id IDstringThe notification id. Required.
--minutes NnumberHow long to hide it. Required. Greater than 0 and at most 43200; fractions are allowed.
Exampleshell
herald snooze --app example.bidbot --id bid-42 --minutes 15
OutputJSON
{
  "ok" : true,
  "until" : "2026-10-02T13:15:00.000Z"
}

Exit status

  • 1 and herald: --minutes needs a number greater than 0 and at most 43200 for a value outside that range.
  • 1 and Herald's 404 when the notification is unknown.

HTTP route

POST /v1/snooze

herald unsnooze#

Brings a snoozed banner back now, without waiting for the time to pass.

Options

OptionTypeDescription
--app IDstringThe app id. Required.
--id IDstringThe notification id. Required.
Exampleshell
herald unsnooze --app example.bidbot --id bid-42
OutputJSON
{
  "ok" : true
}

HTTP route

POST /v1/unsnooze

herald dismiss#

Closes one banner. The notification stays in History. Use it when the event behind a banner is over.

Options

OptionTypeDescription
--app IDstringThe app id. Required.
--id IDstringThe notification id. Required.
Exampleshell
herald dismiss --app example.bidbot --id bid-42
OutputJSON
{
  "ok" : true
}

HTTP route

POST /v1/dismiss

herald dismiss-all#

Closes every banner of an app, or only the banners of one stack. The spelling dismissAll also works.

Options

OptionTypeDescription
--app IDstringThe app id. Required.
--group GstringClose only the banners sent with this group.
Exampleshell
herald dismiss-all --app example.bidbot --group "acme"
OutputJSON
{
  "ok" : true
}

HTTP route

POST /v1/dismissAll

herald stacks#

Lists the stacks of banners that are on screen, with the banners in each. See Stacking for what a stack is.

Options

OptionTypeDescription
--app IDstringOnly the stacks of this app. Default: every app.
Exampleshell
herald stacks --app example.bidbot
OutputJSON
{
  "stacks" : [

  ]
}

The list is empty when no stack is on screen.

HTTP route

GET /v1/stacks

Quiet hours#

Quiet hours silence speech and sounds, and optionally banners, for a stretch of time. The weekly schedule is set in Settings. The commands here start and end a one-off quiet period. Quiet hours explains the rules.

herald quiet#

Starts a quiet period that ends at a clock time, or after a number of minutes. Give exactly one of --until and --for.

Options

OptionTypeDescription
--until HH:MMstringEnd at this time, in 24-hour form with two-digit minutes, such as 07:30.
--for MINUTESnumberEnd after this many minutes.
--bannersflagSilence banners as well. Without it, only speech and sounds are silenced.
Exampleshell
herald quiet --for 60 --banners

Output

The reply is the quiet-hours state after the change.

Exit status

  • 1 and herald: quiet needs exactly one of --until HH:MM or --for MINUTES (or off, status) when both or neither are given.
  • 1 and herald: --until needs a time such as 07:30 for a malformed time.

HTTP route

PUT /v1/settings/quiet-hours

herald quiet status#

Shows whether quiet hours are active now, and the weekly windows that are set.

Exampleshell
herald quiet status
OutputJSON
{
  "status" : {
    "active" : false,
    "banners" : false,
    "sounds" : false,
    "speech" : false
  },
  "windows" : [
    {
      "banners" : false,
      "days" : [

      ],
      "end" : "06:00",
      "id" : "window-1",
      "sounds" : true,
      "speakSummary" : false,
      "speech" : true,
      "start" : "22:30"
    }
  ]
}

HTTP route

GET /v1/settings/quiet-hours

herald quiet off#

Ends the current quiet period now, including one started by herald quiet.

Exampleshell
herald quiet off

Output

The reply is the quiet-hours state after the change, in the shape described under PUT /v1/settings/quiet-hours.

HTTP route

PUT /v1/settings/quiet-hours with {"resume": true}.

History#

History is the record of every notification Herald has shown.

herald history#

Shows the notifications Herald stored for one app, newest first, or clears them.

Options

OptionTypeDescription
--app IDstringThe app id. Required.
--limit NintegerThe most items to return. A positive integer. Default: 50.
--clearflagDelete the app's History instead of showing it.
Exampleshell
herald history --app example.bidbot --limit 10
OutputJSON
{
  "items" : [

  ]
}

The list is empty when the app has sent nothing. Each item is the full record of one notification.

HTTP route

GET /v1/history to show.

DELETE /v1/history with --clear.

Finds notifications that match the words you give. The words are separate arguments, with no quotes needed.

Options

OptionTypeDescription
WORDSstringOne or more words to find. Required. They come first, before any option.
--app IDstringSearch only this app. Default: every app.
--limit NintegerThe most items to return.
Exampleshell
herald history search invoice paid --app example.bidbot --limit 20
OutputJSON
{
  "count" : 0,
  "items" : [

  ],
  "query" : "invoice paid"
}

HTTP route

GET /v1/history/search

herald history reshow#

Shows a past notification again as a new banner.

Options

OptionTypeDescription
--app IDstringThe app id. Required.
--id IDstringThe id of the notification in History. Required.
Exampleshell
herald history reshow --app example.bidbot --id bid-42
OutputJSON
{
  "id" : "bid-42",
  "ok" : true
}

Exit status

1 and Herald's 404 (no such notification in History) when the id is not in History.

HTTP route

POST /v1/history/reshow

herald history delete#

Deletes one item from History.

Options

OptionTypeDescription
--app IDstringThe app id. Required.
--id IDstringThe id of the notification. Required.
Exampleshell
herald history delete --app example.bidbot --id bid-42
OutputJSON
{
  "ok" : true
}

HTTP route

DELETE /v1/history/item

herald history export#

Exports History as JSON. With --out, Herald writes the file and the command prints how many items it wrote. Without it, the items are printed.

Options

OptionTypeDescription
--app IDstringExport only this app. Default: every app.
--out FILEpathWhere to write the export. The name must end in .json and the folder must exist. -o also works.
Exampleshell
herald history export --app example.bidbot --out ~/Desktop/bidbot.json
OutputJSON
{
  "bytes" : 2048,
  "count" : 12,
  "path" : "/Users/you/Desktop/bidbot.json"
}

HTTP route

GET /v1/history/export

Templates and manifests#

A template is the saved layout of an app's banners. A manifest declares what an app sends.

herald template list#

Lists the saved templates of an app.

Options

OptionTypeDescription
--app IDstringThe app id. Default: every app.
Exampleshell
herald template list --app example.bidbot
OutputJSON
{
  "items" : [

  ]
}

HTTP route

GET /v1/templates

herald template put#

Saves one template from a JSON file. Herald validates it first and refuses a template with an error, naming the cell.

Options

OptionTypeDescription
FILEpathA file holding one template object. - reads standard input. Required.
Exampleshell
herald template put bid-accepted.json
OutputJSON
{
  "ok" : true
}

Exit status

1 and Herald's 400 (invalid template: ...) when validation fails.

HTTP route

PUT /v1/templates

herald template delete#

Deletes a template.

Options

OptionTypeDescription
--app IDstringThe app id. Required.
--name NAMEstringThe template name. Required.
Exampleshell
herald template delete --app example.bidbot --name bid-accepted
OutputJSON
{
  "ok" : true
}

HTTP route

DELETE /v1/templates

herald template duplicate#

Copies a template, in the same app or into another one.

Options

OptionTypeDescription
--app IDstringThe app that owns the template. Required.
--name NAMEstringThe template to copy. Required.
--new-name MstringThe name of the copy. Default: Herald picks a free name.
--to-app IDstringCopy into this app instead. Default: the same app.
Exampleshell
herald template duplicate --app example.bidbot --name bid-accepted --new-name bid-won
OutputJSON
{
  "app" : "example.bidbot",
  "copiedFrom" : {
    "app" : "example.bidbot",
    "name" : "bid-accepted"
  },
  "name" : "bid-won",
  "ok" : true
}

HTTP route

POST /v1/templates/duplicate

herald template rename#

Renames a template. If it was the app's default, it stays the default under the new name.

Options

OptionTypeDescription
--app IDstringThe app id. Required.
--name NAMEstringThe current name. Required.
--new-name MstringThe new name. Required.
Exampleshell
herald template rename --app example.bidbot --name bid-won --new-name bid-accepted

Output

The reply names the new name and the old one, and carries a note that approvals belong to the old name. The fields are under POST /v1/templates/rename.

HTTP route

POST /v1/templates/rename

herald template default#

Sets the template Herald uses when a notification names none, or clears it. Give --name or --clear, not both.

Options

OptionTypeDescription
--app IDstringThe app id. Required.
--name NAMEstringThe template to make the default.
--clearflagRemove the default, so the built-in layout is used.
Exampleshell
herald template default --app example.bidbot --name bid-accepted
OutputJSON
{
  "app" : "example.bidbot",
  "defaultTemplate" : "bid-accepted",
  "ok" : true
}

HTTP route

PUT /v1/templates/default

herald template follow-up#

Sets, or switches off, the follow-up of an app's template: one action that runs when a banner is left unattended. The command edits the template. With no --name it uses the app's default template, and creates one from the current layout when the app has none. It prints whether the action still needs your one-time approval. It never approves code: you approve at the Mac, in the banner's question.

Options

OptionTypeDescription
--app IDstringThe app id. Required.
--name TEMPLATEstringThe template to edit. Default: the app's default template.
--after DURATIONstringHow long the banner goes unanswered first: seconds, or 90s, 10m, 2h. From 5 seconds to 7 days.
--shortcut NAMEstringRun this Apple Shortcut.
--script FILEstringRun this file from Herald's scripts folder.
--command CMDstringRun this shell command.
--action-ref IDstringRun the action of this id, or label, that the notification offers.
--input TEXTstringText for the Shortcut or script, with {tokens} filled.
--label TEXTstringThe name shown on the banner line Follow-up ran: LABEL. Default: the Shortcut or script name, or Follow-up for a command.
--offflagSwitch the follow-up off, including one the issuer declares. Use it alone, with --app and --name.

Give one of --shortcut, --script, --command and --action-ref with --after, or give --off.

Exampleshell
herald template follow-up --app example.bidbot --name "Bid won" \
  --after 10m --shortcut "Forward to phone" --input "{title}"
OutputJSON
{
  "saved": true,
  "app": "example.bidbot",
  "template": "Bid won",
  "createdTemplate": false,
  "followUp": {"after": 600, "action": {"id": "follow-up", "label": "Forward to phone",
               "kind": "shortcut", "shortcut": "Forward to phone", "input": "{title}"}},
  "action": {"id": "follow-up", "label": "Forward to phone",
             "kind": "shortcut", "shortcut": "Forward to phone", "input": "{title}"},
  "origin": "template",
  "approval": "needs-approval",
  "needsApproval": true,
  "note": "Approval stays with the person at the Mac: the banner asks the first time the action would run, and Always allow lets later follow-ups run unattended. Nothing was approved here. list_shortcuts names the installed Shortcuts."
}

Exit status

  • 1 with the message when the options are incomplete, such as --after without an action.
  • 1 with herald: --off takes no other follow-up option when --off comes with --after, an action or --input.
  • 1 and Herald's error when it rejects the follow-up, for example for a kind that cannot follow up, an --action-ref the app does not offer, or a built-in layout named with --name.

HTTP route

PUT /v1/templates/follow-up

herald template export#

Writes a template and the Rive files it plays into one .heraldtemplate bundle file, so it can move to another Mac. See Packaging with a template for what the bundle holds.

Options

OptionTypeDescription
--app IDstringThe app that owns the template. Required.
--name NAMEstringThe template to export. Required.
--out FILEpathWhere to write the bundle. -o also works. Default: <name>.heraldtemplate in the current folder.
Exampleshell
herald template export --app example.bidbot --name bid-accepted
Outputtext
Wrote /Users/you/bid-accepted.heraldtemplate (1 animation file)

Warnings go to standard error: a missing animation, and scripts and Shortcuts that stay behind.

Exit status

1 and herald: no template "NAME" for APP when the template does not exist.

HTTP routes

The tool reads the template from GET /v1/templates.

It reads the manifest, whose Rive files go in the bundle, from GET /v1/manifest. The tool writes the file itself.

herald template import#

Adds a bundle's template and animation files to Herald. Rive files go into the app's assets folder and never replace a different file with the same name. Herald must be running, because the tool asks it to save the template.

Options

OptionTypeDescription
FILEpathThe bundle. Required. It may come first with no flag, or after --file.
--app IDstringImport for this app instead of the app the bundle names.
--keep-bothflagIf the name is taken, save the new template as NAME 2, NAME 3 and so on. This is the default.
--replaceflagOverwrite the template that has the name.
--failflagStop with an error if the name is taken.

Use at most one of --keep-both, --replace and --fail.

Exampleshell
herald template import bid-accepted.heraldtemplate --app example.bidbot --replace
Outputtext
Imported "bid-accepted" for example.bidbot; 0 new animation files, 1 already there

Exit status

The command exits with 1 in three cases:

  • The file cannot be read.
  • --fail is given and the name is taken.
  • Herald refuses the template.

HTTP routes

The tool finds taken names with GET /v1/templates.

It saves the template with PUT /v1/templates.

herald manifest delete#

Deletes an app's manifest. The app, its History and its templates stay.

Options

OptionTypeDescription
--app IDstringThe app id. Required.
Exampleshell
herald manifest delete --app example.bidbot
OutputJSON
{
  "ok" : true
}

HTTP route

DELETE /v1/manifest

Apps and settings#

Settings are written as KEY=VALUE words. Herald checks every key and applies all or none. The type of a value follows one rule:

  • A value that parses as JSON keeps its type: true, 3, 1.2, null, a quoted string, an array or an object.
  • Anything else is a string.

herald apps#

Lists the registered apps with their names, icons and defaults.

Exampleshell
herald apps
OutputJSON
{
  "apps" : [
    {
      "app" : "example.bidbot",
      "appName" : "BidBot",
      "defaults" : {
        "sound" : "Glass"
      }
    }
  ]
}

HTTP route

GET /v1/apps

herald apps settings#

Shows the per-app settings, with the schema that lists every key and its allowed values.

Options

OptionTypeDescription
--app IDstringShow one app. Default: every app.
Exampleshell
herald apps settings --app example.bidbot

Output

The reply lists each app with its settings, voice and approvals, and a schema that describes every key. The fields are under GET /v1/apps/settings.

HTTP route

GET /v1/apps/settings

herald apps set#

Changes per-app settings. The KEY=VALUE words may come before or after --app.

Options

OptionTypeDescription
--app IDstringThe app id. Required.
KEY=VALUEstringOne or more settings to change, such as muteBanners=true or corner=bottomLeft. Required.
Exampleshell
herald apps set --app example.bidbot muteBanners=true corner=bottomLeft sound=Ping timeout=8

Output

The reply lists the keys it applied, applied, and the app's settings after the change.

Notes

  • The command can withdraw an approval with revokeCommands=true. It cannot grant one: only the person at the Mac can.

HTTP route

PUT /v1/apps/settings

herald approvals#

Lists the approvals the person has given, such as the templates whose commands are allowed to run.

Exampleshell
herald approvals
OutputJSON
{
  "items" : [

  ],
  "note" : "Approvals are granted by the user when a banner asks; here they can only be listed and revoked."
}

HTTP route

GET /v1/actions/approvals

herald approvals revoke#

Withdraws the approval for one template's commands. The next time the template wants to run a command, the person is asked again.

Options

OptionTypeDescription
--app IDstringThe app id. Required.
--template NAMEstringThe template name. Required.
Exampleshell
herald approvals revoke --app example.bidbot --template bid-accepted
OutputJSON
{
  "ok" : true
}

HTTP route

DELETE /v1/actions/approvals

herald settings#

Shows Herald's own settings: their values, a schema that lists every key with its type and limits, and the choices for keys that have a fixed list. herald settings get does the same.

Exampleshell
herald settings

Output

The reply holds the current settings, a schema that describes every key, and the options that a key accepts, such as the sound names. The fields are under GET /v1/settings.

HTTP route

GET /v1/settings

herald settings set#

Changes Herald's settings.

Options

OptionTypeDescription
KEY=VALUEstringOne or more settings to change. Required.
Exampleshell
herald settings set muteAllSounds=true stacking=bySender voiceSpeed=1.2

Output

The reply is the settings after the change, with applied listing the keys that changed.

Exit status

Both failures exit with 1:

  • A word with no = prints herald: 'WORD' is not KEY=VALUE.
  • An unknown key or a value out of range prints Herald's 400, and nothing is applied.

HTTP route

PUT /v1/settings

Assets and symbols#

herald assets list#

Lists the Rive files and images stored for an app, with the templates that use each.

Options

OptionTypeDescription
--app IDstringThe app id. Required.
Exampleshell
herald assets list --app example.bidbot

Output

The reply names the app folder, the limits, and every Rive file and image. The fields are under GET /v1/assets.

HTTP route

GET /v1/assets

herald assets add#

Copies a Rive file or an image into an app's assets. A file ending in .riv is a Rive file; a picture file is an image.

Options

OptionTypeDescription
--app IDstringThe app id. Required.
--file PATHpathThe file to add. Required. A relative path is made absolute from the current folder.
--name NstringThe stored name. Default: the file's name.
--kind Kstringrive or image. Needed only when the file name does not show which.
Exampleshell
herald assets add --app example.bidbot --file ~/Desktop/bell.riv

Output

The reply gives the stored file name and path. For a Rive file it also gives the asset id and a ready rive component. The fields are under POST /v1/assets.

HTTP route

POST /v1/assets

herald assets rm#

Removes one stored file. The reply lists the templates that still use it, under stillReferencedBy.

Options

OptionTypeDescription
--app IDstringThe app id. Required.
--file NAMEstringThe stored file name. Required.
Exampleshell
herald assets rm --app example.bidbot --file bell.riv
OutputJSON
{
  "deleted" : "bell.riv",
  "ok" : true,
  "stillReferencedBy" : [

  ]
}

HTTP route

DELETE /v1/assets

herald symbols#

Searches the SF Symbol names a template's symbol component can use. The words are separate arguments, before any option. The reply also lists the categories.

Options

OptionTypeDescription
WORDSstringWords to search for. Default: all symbols.
--category CstringOnly symbols in this category, such as communication.
--limit NintegerThe most symbols to return.
--offset NintegerHow many symbols to skip, for paging.
Exampleshell
herald symbols bell --category communication --limit 2
OutputJSON
{
  "limit" : 2,
  "offset" : 0,
  "symbols" : [
    {
      "categories" : [
        "communication"
      ],
      "name" : "bell"
    }
  ],
  "total" : 1
}

The output above is shortened: the real reply also carries the list of categories.

HTTP route

GET /v1/symbols

Voice, MCP and health#

herald voice#

Shows the state of the Kokoro voice engine, or manages it. With no argument it shows the state. After an install, run herald voice again to see the progress. The words are in the Options table.

Options

OptionTypeDescription
statuswordShow the state. The default.
installwordInstall Kokoro.
cancelwordCancel a running install.
use-existingwordUse an existing installation instead of installing.
Exampleshell
herald voice use-existing
OutputJSON
{
  "action" : "useExisting",
  "next" : "Poll GET /v1/voice for progress.",
  "ok" : true
}

With no word, the reply is the state described under GET /v1/voice.

HTTP routes

To show the state: GET /v1/voice.

For install, cancel and use-existing: POST /v1/voice/install.

herald mcp#

Shows which agent clients have Herald's MCP server, or installs it in one. With no argument, or status, it shows the state of every client and whether the command line tool is installed. install CLIENT adds the server to a client and registers the agent as the app agent.<client>.

Options

OptionTypeDescription
statuswordShow the state. The default.
install CLIENTstringInstall in claudeCode, codex, claudeDesktop, cli or generic.
--reinstallflagReplace an existing installation.
--name NAMEstringThe agent's display name. Required for generic, whose app id becomes agent.<slug>.
--icon FILEpathAn icon file for the agent.
--opens APPstringWhat the banner's Open button brings forward: a bundle id or the path of an application.
Exampleshell
herald mcp install generic --name "My Bot" --icon ~/Pictures/bot.png

Output

The reply carries the configuration to paste into the client.

Exit status

Both failures exit with 1:

  • A missing client prints herald: mcp install needs one client: ....
  • Herald's 400 is printed when generic has no --name, or the icon file does not exist.

HTTP routes

To show the state: GET /v1/mcp.

To install: POST /v1/mcp/install.

The MCP guide walks through connecting an agent.

herald health#

Checks that Herald is running. It needs no token, so it works before the token file exists. Use it in scripts to wait for Herald.

Exampleshell
herald health
OutputJSON
{
  "ok" : true,
  "pid" : 69080,
  "version" : "1.8.1"
}

Exit status

2 when Herald is not running.

HTTP route

GET /v1/health

  • HTTP API: every endpoint these commands call, with all fields.
  • Send your first notification: the first herald notify or curl call.
  • MCP guide: connect an agent instead of scripting the tool.
  • Testing: run the tool against a second Herald that does not disturb yours.

Edit this page on GitHub

Esc
Getting started
Guides