Templates & assets

These tools let an agent design how an app's banners look and what their buttons do. A manifest says what an app sends (its fields and actions). A template says how a banner draws those fields on a grid of cells. Assets are the Rive animations and images a template can use. The tools read and write exactly what the Designer reads and writes, and they check a template with the same validation the Designer uses. This page is for an agent author, or for anyone who wants to know what an agent can change.

For the shared conventions (result format, errors, images) see the MCP server overview. The format of a template itself is in Grid and layout, the components are in Components, and the action rules are in Actions.

How these tools fit together#

The usual design session calls the tools in this order:

  1. get_manifest to see the fields the app sends. Each field key is a {token} a template can bind.
  2. component_schema once, to learn the grid and the components.
  3. put_template to save a draft. Errors name the JSON path and the cell id, and nothing is saved until there are none.
  4. render_preview to look at the result, in light and dark, with long or missing values passed in data.
  5. list_shortcuts and add_action_rule for buttons of your own.
  6. send_test to see the real banner on screen.

A worked example of this session is in Connect an agent.

Tools#

Manifests#

A manifest is registered by the app that sends notifications, or written by an agent for an app that does not. Its fields and actions are documented in Manifests. Agents installed from Settings > MCP get a manifest automatically; see Agent identity.

list_manifests#

Lists the manifests the issuing apps registered. A manifest declares the fields an app sends and the actions (buttons) it offers. The result has one summary per app. Use get_manifest for sample values.

Arguments

No arguments.

Example callJSON
{}
Example resultJSON
{
  "count": 1,
  "manifests": [
    {
      "app": "example.bidbot",
      "appName": "BidBot",
      "version": 1,
      "fields": ["title:text!", "item:text", "bids:number"],
      "actions": ["view", "withdraw"],
      "assets": [],
      "defaultTemplate": "bid-won"
    }
  ],
  "note": "A trailing ! marks a required field. get_manifest shows sample values."
}

Each field is written key:type, with a trailing ! when the field is required.

HTTP route

GET /v1/manifests.

Side effects

None. Read-only.

get_manifest#

Returns one app's whole manifest: the fields (type, required flag and sample value), the issuer's actions with their ids, the assets and the default template.

  • The field keys are the {tokens} a template binds.
  • The samples are what render_preview and send_test show.
  • The manifest is also readable as the resource herald://manifests/<app>, always shortened.

Strings over 1,200 characters, such as an embedded icon or a long sample, are shortened to a marker like <data:image/png;base64,... 5022 characters omitted>. Pass full: true to get the real values.

Arguments

NameTypeRequiredDescription
appstringrequiredThe app id, such as example.bidbot. list_manifests shows the registered ids.
fullbooleanoptionalReturns long strings in full instead of shortening them. Default false.
Example callJSON
{"app": "example.bidbot"}
Example resultJSON
{
  "app": "example.bidbot",
  "appName": "BidBot",
  "version": 1,
  "fields": [
    {"key": "title", "type": "text", "required": true, "sample": "Bid accepted"},
    {"key": "item", "type": "text", "sample": "Oak desk, 1920"},
    {"key": "bids", "type": "number", "sample": 3}
  ],
  "actions": [
    {"id": "view", "label": "View", "kind": "url", "url": "https://example.com/bids"},
    {"id": "withdraw", "label": "Withdraw", "kind": "callback", "style": "destructive"}
  ],
  "assets": [],
  "defaultTemplate": "bid-won"
}

When the app has no manifest the tool fails and lists the apps that do.

HTTP route

GET /v1/manifest.

Side effects

None. Read-only.

put_manifest#

Creates a manifest or replaces the whole one. Read it with get_manifest, edit it and send it back. Apps normally register their own manifest. Use this to describe an app that does not, to add sample values for the Designer, or to set defaultTemplate.

Rules for what you send:

  • A shortened marker that get_manifest wrote is swapped back for the stored value. A marker that matches nothing stored is refused: read the manifest again with full: true.
  • An issuer action is one of the kinds url, callback, command, script, shortcut, openApp, reply or dismiss. Each kind is described in Actions. A script or shortcut action runs only when the user has allowed the app to run commands, scripts and Shortcuts. A snooze action is authored in a template with add_action_rule. The manifest can also carry a default followUp, described in Manifests.
  • An invalid manifest is rejected with the path of the field.
  • Rive files named in assets are copied into Herald's assets folder.

Arguments

NameTypeRequiredDescription
manifestobjectrequiredThe whole manifest. app is required. The other fields are in Manifests.
Example callJSON
{
  "manifest": {
    "app": "example.bidbot",
    "appName": "BidBot",
    "fields": [
      {"key": "title", "type": "text", "required": true, "sample": "Bid accepted"},
      {"key": "bids", "type": "number", "sample": 3}
    ],
    "actions": [{"id": "view", "label": "View", "kind": "url", "url": "https://example.com/bids"}],
    "defaultTemplate": "bid-won"
  }
}
Example resultJSON
{
  "saved": true,
  "app": "example.bidbot",
  "fields": 2,
  "actions": 1,
  "assets": 0,
  "defaultTemplate": "bid-won",
  "restoredAbbreviatedValues": 0
}

HTTP route

PUT /v1/manifest.

Side effects

Overwrites the app's manifest. Existing templates are kept.

delete_manifest#

Deletes an app's manifest and the copies of its Rive assets. Templates stay. The app's notifications then use the generic look until a manifest is registered again.

Arguments

NameTypeRequiredDescription
appstringrequiredThe app id.
Example callJSON
{"app": "example.bidbot"}
Example resultJSON
{"ok": true}

HTTP route

DELETE /v1/manifest.

Side effects

Deletes stored data. The tool carries the destructive hint, so a client can ask before it runs.

The template format#

component_schema#

Returns the format of a template, for authoring: the grid, every component type with each property, allowed values and default, how {token} bindings and empty collapsing work, the action kinds and rules, symbol styling, and complete examples. Read it before writing a template. The same format is documented for people in Grid and layout and Components. It is also available as the short resource herald://docs/components.

The tool asks the running Herald for its own document and falls back to the one built into herald-mcp, so it works when Herald is down.

Arguments

NameTypeRequiredDescription
componentstringoptionalReturns only this component's schema plus the shared definitions it uses. A component name from the component reference.
sectionstringoptionalReturns only this part of the document. One of definitions, components, bindings, actions, examples, workflow.
Example callJSON
{"component": "badge"}

Example result

The result is a JSON document. A call with no arguments returns the whole document as text. An unknown name fails and lists the valid names. A narrowed call returns an object with these keys:

KeyMeaning
componentThe component name that was asked for.
schemaThe JSON schema of that component.
definitionsThe shared definitions the schema uses.
sourceherald when the running app answered, or herald-mcp (built in; ...) when the server answered from its own copy.

HTTP route

GET /v1/components.

Side effects

None. Read-only.

Templates#

Four built-in templates exist for every app and are generated on request: builtin.imageLeft, builtin.imageRight, builtin.hero and builtin.compact. They are read-only starting points: copy one with duplicate_template or read it with get_template and save it under another name.

list_templates#

Lists the saved templates of one app or of all apps, and the names of the built-ins. Each summary has the app, the name, the layout version, the cell count, the {tokens} the template reads, the number of action rules, and whether it is the app's default.

Arguments

NameTypeRequiredDescription
appstringoptionalOnly this app's templates. Default: all apps.
Example callJSON
{"app": "example.bidbot"}
Example resultJSON
{
  "count": 1,
  "templates": [
    {
      "app": "example.bidbot",
      "name": "bid-won",
      "layoutVersion": 2,
      "cells": 5,
      "tokens": ["bids", "item", "title"],
      "actionRules": 1,
      "collapseEmpty": true,
      "isDefault": true
    }
  ],
  "builtins": ["builtin.imageLeft", "builtin.imageRight", "builtin.hero", "builtin.compact"]
}

HTTP route

GET /v1/templates.

Side effects

None. Read-only.

get_template#

Returns the full JSON of one template, or of a built-in generated for the app. Edit it and save it back with put_template. It is also readable as the resource herald://templates/<app>/<name>; path parts are percent-encoded, so Bid won is Bid%20won.

Arguments

NameTypeRequiredDescription
appstringrequiredThe app id.
namestringrequiredA template name, or builtin.imageLeft, builtin.imageRight, builtin.hero or builtin.compact.
Example callJSON
{"app": "example.bidbot", "name": "builtin.compact"}

Example result

The result is the template as a JSON document, in the format described in Grid and layout. A name that does not exist fails and lists the saved and built-in names.

HTTP route

GET /v1/templates, filtered by name. Built-in templates are generated by the server.

Side effects

None. Read-only.

put_template#

Creates or overwrites a template. Herald validates it against the grid schema first. Errors carry the JSON path and the id of the cell at fault, and nothing is saved until there are none. Errors include overlapping cells, a cell outside the grid, an unknown component type, a property of the wrong type and a bad colour. Warnings are returned with the success and do not block: a {token} the manifest does not declare, or an unknown key that is probably a typo such as colspan for colSpan.

Names that start with builtin. are reserved. With setAsDefault: true the template also becomes the manifest's defaultTemplate; the app needs a manifest for that.

Arguments

NameTypeRequiredDescription
templateobjectrequiredThe whole template object. Its fields are in Grid and layout.
appstringoptionalThe app id. Needed only when the template object has no app. It must match the template's own app when both are given.
setAsDefaultbooleanoptionalAlso sets the manifest's defaultTemplate to this template. Default false.
Example callJSON
{
  "template": {
    "name": "bid-won",
    "app": "example.bidbot",
    "layoutVersion": 2,
    "grid": {"rows": 3, "cols": 3, "rowSizes": ["auto", "auto", "auto"], "colSizes": [40, "fill", "auto"], "gap": 6, "padding": 12, "width": 380},
    "cells": [
      {"id": "icon", "row": 0, "col": 0, "rowSpan": 2, "component": {"type": "issuerIcon", "size": 32}},
      {"id": "title", "row": 0, "col": 1, "component": {"type": "text", "binding": "{title}", "style": "title", "maxLines": 2}},
      {"id": "bids", "row": 0, "col": 2, "component": {"type": "badge", "binding": "{bids}"}},
      {"id": "item", "row": 1, "col": 1, "colSpan": 2, "component": {"type": "text", "binding": "{item}", "maxLines": 3}},
      {"id": "actions", "row": 2, "col": 0, "colSpan": 3, "component": {"type": "actions", "source": "merged", "layout": "wrap"}}
    ]
  },
  "setAsDefault": true
}
Example resultJSON
{
  "saved": true,
  "app": "example.bidbot",
  "name": "bid-won",
  "layoutVersion": 2,
  "cells": 5,
  "isDefault": true,
  "warnings": [],
  "notes": [],
  "next": "render_preview to look at it; send_test to see the real banner."
}

When validation fails the result has isError set and looks like this. Fix the cells it names and call again.

JSON
{
  "ok": false,
  "error": "Template not saved: 1 error(s). Fix them and call again.",
  "saved": false,
  "errors": [{"severity": "error", "path": "cells[2]", "cellId": "bids", "message": "cell 'bids' overlaps cell 'title' at row 0, col 1"}],
  "warnings": []
}

The notes array explains what the tool could not do, such as ignoring setAsDefault for an app with no manifest.

HTTP route

Side effects

Saves a template, overwriting one of the same name. The user is asked again before a changed command, script or Shortcut runs.

delete_template#

Deletes a saved template. Built-in templates cannot be deleted. Notifications that name a deleted template use the default look. If the deleted template was the manifest's defaultTemplate, the result says so in notes.

Arguments

NameTypeRequiredDescription
appstringrequiredThe app id.
namestringrequiredThe template name.
Example callJSON
{"app": "example.bidbot", "name": "bid-won"}
Example resultJSON
{
  "deleted": true,
  "app": "example.bidbot",
  "name": "bid-won",
  "notes": ["'bid-won' is still the manifest's defaultTemplate; put_manifest to change it."]
}

HTTP route

DELETE /v1/templates.

Side effects

Deletes stored data. The tool carries the destructive hint.

duplicate_template#

Copies a saved template, or a builtin.* layout, under a new name, optionally for another app. It is the Designer's Duplicate. Without newName the copy is called <name> copy, numbered when that name is taken.

Arguments

NameTypeRequiredDescription
appstringrequiredThe app the template belongs to.
namestringrequiredThe template to copy: a saved name or builtin.*.
newNamestringoptionalThe name of the copy. It must not start with ., _ or builtin., and must not contain / or :.
toAppstringoptionalCreates the copy for this app instead.
Example callJSON
{"app": "example.bidbot", "name": "builtin.hero", "newName": "bid-lost"}
Example resultJSON
{"ok": true, "app": "example.bidbot", "name": "bid-lost", "copiedFrom": {"app": "example.bidbot", "name": "builtin.hero"}}

HTTP route

POST /v1/templates/duplicate.

Side effects

Saves a new template. Changes no existing one.

rename_template#

Renames a saved template. The app's default template follows the new name. Command, script and Shortcut approvals belong to the old name, so the user is asked again the first time those buttons run. Built-in templates cannot be renamed; duplicate them instead.

Arguments

NameTypeRequiredDescription
appstringrequiredThe app id.
namestringrequiredThe current name.
newNamestringrequiredThe new name.
Example callJSON
{"app": "example.bidbot", "name": "bid-won", "newName": "bid-accepted"}
Example resultJSON
{
  "ok": true,
  "app": "example.bidbot",
  "name": "bid-accepted",
  "renamedFrom": "bid-won",
  "isDefault": true,
  "note": "Command, script and Shortcut approvals belong to the old name; the user is asked again the first time they run."
}

HTTP route

POST /v1/templates/rename.

Side effects

Renames stored data and resets the user's approvals for the template. The tool carries the destructive hint.

set_default_template#

Makes a template the app's default: the one used by notifications that name no template. This sets the manifest's defaultTemplate, so the app needs a manifest. Omit name to clear the default.

Arguments

NameTypeRequiredDescription
appstringrequiredThe app id.
namestringoptionalThe template name, saved or builtin.*. Omit it to clear the default.
Example callJSON
{"app": "example.bidbot", "name": "bid-won"}
Example resultJSON
{"ok": true, "app": "example.bidbot", "defaultTemplate": "bid-won"}

HTTP route

PUT /v1/templates/default.

Side effects

Changes the manifest's defaultTemplate.

Check a design#

Three tools let an agent see a design without leaving a banner behind. validate_template checks the structure. render_preview draws the banner. designer_snapshot draws the editor.

validate_template#

Checks a template without saving it. Give a draft as template, or a saved one as app and name.

  • The result lists errors and warnings, each with a path and the cell id.
  • When a manifest is available it also reports, for the manifest's sample data, which cells are empty, which rows and columns collapse, and the resulting button list.
  • It works without Herald running. The manifest check is then skipped and the result says so in note.

Arguments

NameTypeRequiredDescription
templateobjectoptionalA draft template object. Omit it when you use app and name.
appstringoptionalThe app id. Tokens are checked against its manifest. Required with name.
namestringoptionalA saved template, or builtin.*, to validate instead of a draft.

One of template or name is needed.

Example callJSON
{"app": "example.bidbot", "name": "bid-won"}
Example resultJSON
{
  "valid": true,
  "errors": [],
  "warnings": [],
  "manifestChecked": true,
  "tokens": ["bids", "item", "title"],
  "withSampleData": {
    "emptyCells": [],
    "collapsedCells": [],
    "collapsedRows": [],
    "collapsedCols": [],
    "actions": [
      {"id": "view", "label": "View", "kind": "url", "origin": "issuer"},
      {"id": "withdraw", "label": "Withdraw", "kind": "callback", "style": "destructive", "origin": "issuer"}
    ]
  }
}

Each issue has the keys listed under Validation issues. cellId is present when the problem is inside a cell.

HTTP route

None for the check itself: the tool validates locally. When it needs data it reads:

Side effects

None. Read-only.

render_preview#

Draws a template offscreen with Herald's real banner renderer and returns the picture. The result has two parts: an image block (a PNG) and a text block with the saved file path and the pixel size. Preview a saved template with name (or builtin.*), an unsaved draft with template (validated first; errors name the cell), or neither, which draws the app's default template.

What the picture is drawn from:

  • The data is the manifest's sample values by default, or the app's latest real notification with source: "last".
  • data overrides single fields. Pass a very long value to see how text wraps, and leave a key out to see how its cell collapses.
  • Sample data stands in for an issuer that names every action its manifest declares, so the buttons shown are all of those. A real notification shows only the actions it sends.
  • Rive components are drawn as placeholders and symbol effects are not drawn.
  • Render light and dark to check both.

Arguments

NameTypeRequiredDescription
namestringoptionalA saved template's name, or builtin.*. Needs app.
templateobjectoptionalAn unsaved draft template, instead of name.
appstringoptionalThe app id. Defaults to the draft's own app. Required with name or with neither argument.
sourcestringoptionalsample (default) uses the manifest's samples. last uses the app's most recent notification in History.
dataobjectoptionalField values that override the source's, such as {"bids": 12, "item": "A very long item name"}.
appearancestringoptionallight (default) or dark.
scalenumberoptionalThe pixel scale, from 1 to 3. Default 2.
Example callJSON
{"app": "example.bidbot", "name": "bid-won", "appearance": "dark", "data": {"bids": 14, "item": "Oak desk with three drawers, restored in 1962"}}

Example result

The first content block is the image, a PNG of the banner. The second is text:

JSON
{
  "path": "/var/folders/xy/T/herald-previews/preview-example.bidbot-bid-won-dark-20261004-150210-E3BC.png",
  "bytes": 41873,
  "app": "example.bidbot",
  "template": "bid-won",
  "appearance": "dark",
  "scale": 2,
  "data": "sample+overrides",
  "width": 760,
  "height": 300
}

The reply carries these extra facts:

  • data says where the values came from: sample, last, or either followed by +overrides.
  • A warnings array is added when a draft has warnings.
  • The server keeps the newest 40 preview files in its preview folder.

HTTP route

POST /v1/preview.

Side effects

Writes a PNG to the preview folder and removes the oldest ones beyond 40. Shows nothing on screen.

designer_snapshot#

Draws the Designer window's content offscreen and returns it as a PNG: the grid canvas with its handles, the inspector and the live preview. No window opens and nothing takes focus. Use it to check how the editor looks for a template; use render_preview to see the banner itself.

Arguments

NameTypeRequiredDescription
appstringoptionalThe app to open in the Designer.
templatestringoptionalA saved template name to open.
selectstringoptionalA cell id to select, so the inspector shows it.
widthintegeroptionalThe image width, from 600 to 4000. Default 1100.
heightintegeroptionalThe image height, from 400 to 3000. Default 820.
Example callJSON
{"app": "example.bidbot", "template": "bid-won", "select": "title", "width": 1200, "height": 800}

Example result

The result is an image block (a PNG) and a text block such as {"bytes": 188402, "width": 1200, "height": 800}.

HTTP route

GET /v1/designer/snapshot.

Side effects

None. Read-only. Opens no window.

Buttons#

A template can change the buttons the issuing app sent and add buttons of its own. The rules live in the template's actionRules; see Actions. An agent cannot press a button and cannot grant the permission to run a command. The user confirms a command, script or Shortcut in Herald the first time its button is pressed, or the first time a follow-up would run it.

list_shortcuts#

Lists the names of the Apple Shortcuts installed on this Mac. Use one in an action of kind shortcut (see add_action_rule): Herald runs it with the notification as input when the user presses the button.

Arguments

No arguments.

Example callJSON
{}
Example resultJSON
{
  "count": 2,
  "shortcuts": ["Create follow-up", "Log to Notes"],
  "use": "add_action_rule with {\"add\": {\"id\": \"...\", \"label\": \"...\", \"kind\": \"shortcut\", \"shortcut\": \"<one of these names>\", \"input\": \"{title}\\n{url}\"}}"
}

HTTP route

GET /v1/shortcuts.

Side effects

None. Read-only.

add_action_rule#

Adds one rule to a saved template's actionRules. A rule either changes an issuer action or adds an action of your own.

  • To change an issuer action, set match to its id, its label or *, together with the change to make. The fields are in Action rules.
  • To add an action, set add to an action object. The kinds and their fields are in Action object and Fields by kind. For a Shortcut, take the name from list_shortcuts.

Rules that apply to every rule:

  • A script action runs a file in ~/Library/Application Support/Herald/scripts/, which herald_status lists. The script receives the notification JSON on stdin. This server only talks to Herald's API, so an agent that wants a script writes the file itself and then adds the rule.
  • A command, script or Shortcut you add is confirmed by the user the first time its button is pressed, and again whenever it changes. A follow-up uses the same confirmation, so a button and a follow-up that run the same Shortcut share it.
  • An add with the id of one of the issuer's own actions overwrites that button, and the confirmation says so. An action id that an earlier add rule already uses overwrites that rule.
  • Any action can carry an SF Symbol, as a name (checkmark.circle) or a full styling object. An unknown name is a warning.
  • The tool validates the template before saving it. It warns about a script that is missing or cannot run, a Shortcut that is not installed, and a match that matches nothing.

Arguments

NameTypeRequiredDescription
appstringrequiredThe app id.
templatestringrequiredThe saved template's name. Built-in templates are read-only: copy one with duplicate_template first.
ruleobjectrequiredOne rule. Its fields are in Action rules.
Example callJSON
{
  "app": "example.bidbot",
  "template": "bid-won",
  "rule": {
    "add": {
      "id": "followup",
      "label": "Follow up",
      "kind": "shortcut",
      "shortcut": "Create follow-up",
      "input": "{title}\n{item}"
    }
  }
}
Example resultJSON
{
  "saved": true,
  "app": "example.bidbot",
  "template": "bid-won",
  "ruleIndex": 0,
  "replacedExistingRule": false,
  "actionRules": 1,
  "resultingActions": [
    {"id": "view", "label": "View", "kind": "url", "origin": "issuer"},
    {"id": "withdraw", "label": "Withdraw", "kind": "callback", "style": "destructive", "origin": "issuer"},
    {"id": "followup", "label": "Follow up", "kind": "shortcut", "origin": "template"}
  ],
  "warnings": []
}

HTTP route

Side effects

Saves the template with the rule added. The user must confirm a new command, script or Shortcut in Herald before it first runs.

set_follow_up#

Gives a template a follow-up: one action that runs when a banner from the app is left unattended, such as a Shortcut that forwards it to a phone. Use it when you want to add or change a follow-up without reading and rewriting the whole template. The tool saves the template with the follow-up and reports what the person still has to do.

This tool never approves code. A Shortcut, script or command that follows up asks the person at the Mac the first time, in the banner. The result's approval and note tell you where things stand. Tell the person, and do not retry to get around it. How a follow-up works is in Follow-ups.

Arguments

NameTypeRequiredDescription
appstringrequiredThe app id. Cloud connectors are cloud.NAME.
templatestringoptionalThe saved template to edit. A built-in layout is refused. Default: the app's default template, created from the current layout when the app has none.
afternumber or stringoptionalSeconds from 5 to 604800, or "90s", "10m", "2h". Required unless enabled is false.
shortcutstringoptionalAn installed Shortcut. Take the name from list_shortcuts.
scriptstringoptionalA file name in Herald's scripts folder.
commandstringoptionalA shell command.
actionRefstringoptionalThe id, or label, of an action the notification offers.
inputstringoptionalText for the Shortcut or script, with {tokens} filled.
labelstringoptionalThe name shown in Follow-up ran: LABEL. Default: the Shortcut or script name, or Follow-up for a command.
enabledbooleanoptionalfalse switches the follow-up off, including one the issuer declares.

Give exactly one of shortcut, script, command and actionRef, unless enabled is false.

Example callJSON
{
  "app": "example.bidbot",
  "template": "Bid won",
  "after": "10m",
  "shortcut": "Forward to phone",
  "input": "{title}"
}
Example resultJSON
{
  "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."
}

The result's fields are the ones in PUT /v1/templates/follow-up, with the approval values listed there. A refused call returns the same message as the route's 400 or 404.

HTTP route

PUT /v1/templates/follow-up

Side effects

Saves the template, and creates it when the app has none. Runs nothing and approves nothing. The person must approve code at the Mac before the follow-up first runs.

Share a template#

A template bundle is one .heraldtemplate file that holds a template and the Rive files it plays. It is what the Designer's Export and Import use.

export_template_bundle#

Packs a saved template, or a built-in generated for the app, and the Rive files it plays into one bundle. With path Herald writes the file on this Mac. Without it the bundle comes back as base64.

Arguments

NameTypeRequiredDescription
appstringrequiredThe app id.
namestringrequiredThe template name.
pathstringoptionalWhere to write the file. It must end in .heraldtemplate and its folder must exist.
Example callJSON
{"app": "example.bidbot", "name": "bid-won", "path": "~/Desktop/bid-won.heraldtemplate"}
Example resultJSON
{
  "ok": true,
  "app": "example.bidbot",
  "name": "bid-won",
  "file": "bid-won.heraldtemplate",
  "bytes": 2481,
  "assets": [],
  "warnings": [],
  "path": "/Users/you/Desktop/bid-won.heraldtemplate"
}

Without path, the result has a base64 field instead of path.

HTTP route

GET /v1/templates/export.

Side effects

Writes a file when path is given. Changes no stored template.

import_template_bundle#

Imports a .heraldtemplate bundle. Give the bundle in one of two ways, not both:

  • path, a file on this Mac, up to 64 MB.
  • base64, up to about 700 KB.

app retargets the template to another app. The bundle's Rive files go into that app's assets folder. Script actions inside a bundle still need their script files and the user's approval.

Arguments

NameTypeRequiredDescription
pathstringoptionalA .heraldtemplate file on this Mac.
base64stringoptionalThe bundle, base64 encoded.
appstringoptionalImports the template for this app instead of the app in the bundle.
onConflictstringoptionalWhat to do when the name exists: keepBoth (default; saves it as name 2), replace or fail.

One of path or base64 is needed.

Example callJSON
{"path": "~/Desktop/bid-won.heraldtemplate", "app": "example.bidbot", "onConflict": "keepBoth"}
Example resultJSON
{
  "ok": true,
  "app": "example.bidbot",
  "name": "bid-won 2",
  "replaced": false,
  "installedAssets": [],
  "reusedAssets": [],
  "missingAssets": [],
  "warnings": [],
  "renamedFrom": "bid-won"
}

renamedFrom appears only when keepBoth renamed the template.

HTTP route

POST /v1/templates/import.

Side effects

Saves a template and copies its Rive files. With onConflict: "replace" it overwrites a template of the same name. The tool carries the destructive hint.

Assets and symbols#

Assets are the Rive animations (.riv, at most 10 MB each, 32 per app) and images (at most 10 MB each, 100 per app) stored for an app. A template refers to a Rive file with {"type": "rive", "path": "confetti.riv"}. See the Rive component.

list_assets#

Lists the Rive animations and images stored for an app, with their size, whether the manifest declares them, which templates play them, and the component snippet to use.

Arguments

NameTypeRequiredDescription
appstringrequiredThe app id.
Example callJSON
{"app": "example.bidbot"}
Example resultJSON
{
  "app": "example.bidbot",
  "assets": [
    {
      "kind": "rive",
      "id": "confetti",
      "file": "confetti.riv",
      "path": "/Users/you/Library/Application Support/Herald/assets/example.bidbot/confetti.riv",
      "bytes": 48213,
      "declared": true,
      "usedBy": ["bid-won"],
      "component": {"type": "rive", "path": "confetti.riv"}
    },
    {"kind": "image", "file": "logo.png", "path": "/Users/you/Library/Application Support/Herald/assets/example.bidbot/images/logo.png", "bytes": 5120}
  ],
  "folder": "/Users/you/Library/Application Support/Herald/assets/example.bidbot",
  "limits": {"bytes": 10485760, "riveFiles": 32}
}

HTTP route

GET /v1/assets.

Side effects

None. Read-only.

upload_asset#

Adds a Rive animation or an image to an app's assets, like the Designer's Add. Give path (a file on this Mac) or base64 (up to about 700 KB, with name). The reply has the stored path and the component to use. An existing file of the same name is overwritten. Images are checked by their bytes, so a wrong extension is caught. Accepted images are PNG, JPEG, GIF, WebP, HEIC, TIFF and BMP.

Arguments

NameTypeRequiredDescription
appstringrequiredThe app id.
pathstringoptionalA file on this Mac.
base64stringoptionalThe file, base64 encoded. Needs name.
namestringoptionalThe file name, such as confetti.riv or logo.png. Required with base64.
kindstringoptionalrive or image. Inferred from the name when omitted.

Give exactly one of path or base64.

Example callJSON
{"app": "example.bidbot", "path": "~/Downloads/confetti.riv"}
Example resultJSON
{
  "ok": true,
  "kind": "rive",
  "app": "example.bidbot",
  "id": "confetti",
  "file": "confetti.riv",
  "path": "/Users/you/Library/Application Support/Herald/assets/example.bidbot/confetti.riv",
  "bytes": 48213,
  "replaced": false,
  "component": {"type": "rive", "path": "confetti.riv"}
}

An image reply has format and usage fields instead of id and component; usage says to use the path as an image field's value or as a fixed image source.

HTTP route

POST /v1/assets.

Side effects

Writes a file into the app's assets folder, overwriting one of the same name.

delete_asset#

Deletes a Rive file or an image from an app's assets by file name. Templates that play a deleted animation show a placeholder, and the reply lists them.

Arguments

NameTypeRequiredDescription
appstringrequiredThe app id.
filestringrequiredThe file name, such as confetti.riv. list_assets shows the names.
Example callJSON
{"app": "example.bidbot", "file": "confetti.riv"}
Example resultJSON
{"ok": true, "deleted": "confetti.riv", "stillReferencedBy": ["bid-won"]}

HTTP route

DELETE /v1/assets.

Side effects

Deletes a file. The tool carries the destructive hint.

list_symbols#

Searches the SF Symbol names available on this Mac, with their categories, for a component's symbol property. See SF Symbols. The arguments are in the table below. In short:

  • q matches every word in the name or its search terms.
  • category narrows the list, and the reply lists the categories with counts.
  • limit and offset page through long lists.

Arguments

NameTypeRequiredDescription
qstringoptionalSearch words, such as bell or arrow up.
categorystringoptionalA category key from the reply, such as communication or weather.
limitintegeroptionalNames per page, up to 1,000. Default 100.
offsetintegeroptionalHow many names to skip. Default 0.
Example callJSON
{"q": "bell", "limit": 2}
Example resultJSON
{
  "total": 14,
  "offset": 0,
  "limit": 2,
  "symbols": [
    {"name": "bell", "categories": ["communication"]},
    {"name": "bell.fill", "categories": ["communication"]}
  ],
  "categories": [{"key": "communication", "title": "Communication", "icon": "bubble.left.and.bubble.right", "count": 10}]
}

HTTP route

GET /v1/symbols.

Side effects

None. Read-only.

rive_check#

Loads a rive component without a window and reports what it found: the artboards, the state machines and their inputs, and the view-model properties. Nothing is shown or stored. See Rive.

With simulate it first runs pointer steps in order and reports what they wrote to the inputs. The steps are:

StepPointer action
hoverInThe pointer moves onto the animation.
hoverOutThe pointer leaves it.
pressDownThe button goes down.
pressUpThe button comes up.

Arguments

NameTypeRequiredDescription
appstringrequiredThe app id. Assets resolve in its folder.
componentobjectrequiredThe rive component: asset or path, and optionally artboard, stateMachine.
fieldsobjectoptionalField values for the component's bindings.
simulatearray of stringoptionalPointer steps to run in order.
Example callJSON
{"app": "example.bidbot", "component": {"type": "rive", "path": "confetti.riv"}, "simulate": ["hoverIn", "pressDown"]}

Example result

The reply is trimmed here to its main fields.

JSON
{
  "loaded": true,
  "inputs": {"celebrate": "trigger"},
  "applied": {},
  "artboards": [
    {
      "name": "Main",
      "width": 320,
      "height": 120,
      "defaultMachine": "Machine",
      "machines": [{"name": "Machine", "inputs": [{"name": "celebrate", "kind": "trigger"}]}],
      "animations": []
    }
  ],
  "takesClicks": false,
  "pointerWrites": {}
}

When the animation does not load, loaded is false and error holds the reason the banner's placeholder shows.

HTTP route

POST /v1/rive/check.

Side effects

None. Opens no window and stores nothing.

Edit this page on GitHub

Esc
Getting started
Guides