History

These endpoints read and manage the record Herald keeps of every notification: list recent ones, search, show one again, delete one, clear an app's History, and export it. Use them to audit what an integration sent, or to build your own view of past notifications. The examples use the $HERALD and $TOKEN variables from Connect.

What History holds#

Every notification is stored when it is delivered, whether or not a banner was shown. The record stays after the banner is closed. It holds the notification as it was drawn, when it arrived, how it ended, and what the user replied.

Herald keeps a fixed number of notifications per app, 1000 unless the user changed it in Settings > General. When an app goes over the limit, its oldest records are removed.

The History window shows the same records these endpoints return. All Apps, at the top of the list, corresponds to a request with no app parameter.
Herald's History window with the app list on the left and notifications on the right

The History window shows the same records these endpoints return. All Apps, at the top of the list, corresponds to a request with no app parameter.

The history record#

Every endpoint that returns notifications returns them in this shape.

FieldTypeDescription
idstringThe notification id.
appstringThe app that sent it.
notificationobjectThe notification as drawn, after the template was applied. Same fields as POST /v1/notify.
deliveredAtstringWhen it arrived, as an ISO 8601 date.
dismissedAtstringWhen its banner closed. Absent while the banner is still showing or snoozed.
actionUsedstringHow it ended: a button's label, open for a click on the banner, or timeout.
actionNotestringA note about an action that could not do its job.
snoozedUntilstringWhen a snoozed banner returns.
imagePathstringThe local copy of the notification's picture.
fieldsobjectThe values the template's tokens resolved to at delivery.
speechobjectWhat was spoken: text, voice, audioPath, durationSeconds, suppressed.
followUpobjectThe follow-up that ran, or tried to. See below.
replystringWhat the user typed into the banner's reply field.
repliedAtstringWhen the user replied.
replyAudioPathstringThe recording of a voice reply.
replyTranscriptstringThe transcript of a voice reply, made on this Mac.

Fields that do not apply to a notification are absent from its record.

The followUp object appears when a follow-up fired for the notification.

FieldTypeDescription
ranAtstringWhen the follow-up fired, as an ISO 8601 date.
actionstringThe label of the action it ran.
actionIdstringThe id of that action.
kindstringshortcut, script, command or callback.
outcomestringran, failed or waitingForApproval.
detailstringWhy it failed. Absent when it ran.
unattendedSecondsnumberHow long the banner went unanswered before it fired.
JSON
{"ranAt": "2026-10-02T13:25:12Z", "action": "Forward", "actionId": "fwd", "kind": "shortcut",
 "outcome": "ran", "unattendedSeconds": 612}

waitingForApproval means the action needs the person's approval and nothing ran. The banner carries the question.

Endpoints#

EndpointPurpose
GET /v1/historyList the most recent notifications.
GET /v1/history/searchSearch by words.
POST /v1/history/reshowShow a past notification again.
DELETE /v1/history/itemDelete one notification.
DELETE /v1/historyClear an app's History, or all of it.
GET /v1/history/exportExport full records as JSON.

GET /v1/history#

Lists the most recent notifications, newest first.

Request

NameInTypeRequiredDescription
appquerystringoptionalReturn only this app's notifications. Default: every app.
limitqueryintegeroptionalThe most records to return. Default 50.
Example requestshell
curl -s "$HERALD/v1/history?app=example.bidbot&limit=1" -H "Authorization: Bearer $TOKEN"
Example responseJSON
{
  "items": [
    {
      "id": "bid-42",
      "app": "example.bidbot",
      "deliveredAt": "2026-10-02T13:00:04.237Z",
      "dismissedAt": "2026-10-02T13:00:19.462Z",
      "actionUsed": "Open",
      "notification": {
        "app": "example.bidbot",
        "id": "bid-42",
        "title": "Bid accepted",
        "subtitle": "Acme RFP",
        "body": "Your bid of $4,200 was accepted.",
        "url": "https://example.com/bids/42",
        "buttons": [{"label": "Open", "url": "https://example.com/bids/42"}],
        "sound": "Glass",
        "persistent": true
      },
      "fields": {
        "app": "example.bidbot",
        "appName": "BidBot",
        "id": "bid-42",
        "title": "Bid accepted",
        "subtitle": "Acme RFP",
        "body": "Your bid of $4,200 was accepted.",
        "deliveredAt": "2026-10-02T13:00:04.237Z",
        "sound": "Glass"
      }
    }
  ]
}

Response fields

FieldTypeDescription
itemsarrayHistory records, newest first.

Errors

StatusWhen
400limit is negative or not a whole number.

GET /v1/history/search#

Finds notifications that contain every word of a query. The search looks in the title, subtitle, body, app id and app name. It ignores case and accents.

Request

NameInTypeRequiredDescription
qquerystringoptionalThe words to find. An empty query returns the most recent notifications.
appquerystringoptionalSearch only this app. Default: every app.
limitqueryintegeroptionalThe most records to return, up to 1000. Default 50.
Example requestshell
curl -s -G "$HERALD/v1/history/search" -H "Authorization: Bearer $TOKEN" \
  --data-urlencode "q=acme accepted" --data-urlencode "limit=5"
Example responseJSON
{
  "query": "acme accepted",
  "count": 1,
  "items": [
    {
      "id": "bid-42",
      "app": "example.bidbot",
      "deliveredAt": "2026-10-02T13:00:04.237Z",
      "dismissedAt": "2026-10-02T13:00:19.462Z",
      "actionUsed": "Open",
      "notification": {
        "app": "example.bidbot",
        "id": "bid-42",
        "title": "Bid accepted",
        "subtitle": "Acme RFP",
        "body": "Your bid of $4,200 was accepted."
      }
    }
  ]
}

Response fields

FieldTypeDescription
querystringThe query as Herald read it.
countintegerHow many records are in items.
itemsarrayHistory records, newest first.

Errors

StatusWhen
400limit is negative or not a whole number.

POST /v1/history/reshow#

Shows a stored notification again as a new banner, with its sound and a fresh delivery time. Use it to bring back something the user dismissed too early.

Request

NameInTypeRequiredDescription
appbodystringrequiredThe app that sent the notification.
idbodystringrequiredThe notification id.
Example requestshell
curl -s -X POST "$HERALD/v1/history/reshow" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"app": "example.bidbot", "id": "bid-42"}'
Example responseJSON
{"ok": true, "id": "bid-42"}

Errors

StatusWhen
400app or id is missing.
404History has no notification with this app and id.

DELETE /v1/history/item#

Deletes one notification from History. If its banner is still on screen, the banner is closed.

Request

NameInTypeRequiredDescription
appquerystringrequiredThe app that sent the notification.
idquerystringrequiredThe notification id.
Example requestshell
curl -s -X DELETE "$HERALD/v1/history/item?app=example.bidbot&id=bid-42" \
  -H "Authorization: Bearer $TOKEN"
Example responseJSON
{"ok": true}

Errors

StatusWhen
400app or id is missing.
404History has no notification with this app and id.

DELETE /v1/history#

Clears History: one app's, or everything.

Request

NameInTypeRequiredDescription
appquerystringoptionalClear only this app's History. Default: every app.
Example requestshell
curl -s -X DELETE "$HERALD/v1/history?app=example.bidbot" -H "Authorization: Bearer $TOKEN"
Example responseJSON
{"ok": true}

GET /v1/history/export#

Returns every stored record, of one app or of all apps, as JSON. Herald either returns the records in the reply or writes them to a file you name.

Request

NameInTypeRequiredDescription
appquerystringoptionalExport only this app. Default: every app.
pathquerystringoptionalWrite the records to this file. Must end in .json.
Example requestshell
curl -s -G "$HERALD/v1/history/export" -H "Authorization: Bearer $TOKEN" \
  --data-urlencode "app=example.bidbot" --data-urlencode "path=$HOME/Desktop/bidbot-history.json"
Example responseJSON
{"count": 128, "path": "/Users/you/Desktop/bidbot-history.json", "bytes": 214530}

Response fields

FieldTypeDescription
countintegerHow many records were exported.
pathstringWhere the file was written. Present when you sent path.
bytesintegerThe size of the written file. Present when you sent path.
itemsarrayThe history records. Present when you did not send path.

Errors

StatusWhen
400path does not end in .json, or its folder does not exist.

Edit this page on GitHub

Esc
Getting started
Guides