Stacks

These endpoints show which banners are stacked, open or close a stack, and set how Herald groups banners into stacks. Use them to inspect what is on screen or to control stacking from a script. The examples use the $HERALD and $TOKEN variables from Connect.

What a stack is#

When several notifications that belong together are on screen, Herald folds them into one stack: a single card with a count, so that ten emails do not cover the screen. Clicking a stack opens it into a list.

What "belong together" means is the stacking level.

LevelNotifications that stack together
byAppThose of one product, even when it sends under several app ids.
byIssuerThose of one app id.
bySenderThose of one app id with the same group value.
neverNone. Every notification is its own banner.

There is one global level, and each app can override it. The concept is explained in full, with examples, in the stacking reference.

A closed stack. The card on top is the newest notification and the badge counts the notifications in the stack.
A closed stack of banners with a count badge reading 3

A closed stack. The card on top is the newest notification and the badge counts the notifications in the stack.

Endpoints#

EndpointPurpose
GET /v1/stacksList the stacks on screen.
POST /v1/stacks/expandOpen or close a stack.
GET /v1/settings/stackingRead the global stacking level.
PUT /v1/settings/stackingSet the global stacking level.

GET /v1/stacks#

Lists the stacks that are on screen now, with the notifications in each.

Request

NameInTypeRequiredDescription
appquerystringoptionalReturn only this app's stacks. Default: every app.
Example requestshell
curl -s "$HERALD/v1/stacks?app=example.bidbot" -H "Authorization: Bearer $TOKEN"
Example responseJSON
{
  "stacks": [
    {
      "level": "bySender",
      "app": "example.bidbot",
      "group": "acme",
      "count": 2,
      "expanded": false,
      "members": [
        {
          "app": "example.bidbot",
          "id": "bid-43",
          "title": "Counter-offer from Acme",
          "group": "acme",
          "deliveredAt": "2026-10-02T13:02:11.000Z"
        },
        {
          "app": "example.bidbot",
          "id": "bid-42",
          "title": "Bid accepted",
          "group": "acme",
          "deliveredAt": "2026-10-02T13:00:04.237Z"
        }
      ],
      "frame": {"x": 1508, "y": 44, "width": 400, "height": 96}
    }
  ]
}

Response fields

FieldTypeDescription
stacks[].levelstringThe level that formed the stack: byApp, byIssuer or bySender.
stacks[].appstringThe app id, or the product family for byApp.
stacks[].groupstringThe group that keys the stack. Without a group, it is the app id.
stacks[].countintegerHow many notifications are in the stack.
stacks[].expandedbooleantrue when the stack is open as a list.
stacks[].membersarrayThe notifications, newest first. The first is the card on top.
stacks[].frameobjectWhere the stack is on screen, in points. Absent when it has no panel.

Notes

  • When nothing is stacked the reply is an empty list.

POST /v1/stacks/expand#

Opens a stack into a list of its banners, or closes it back into one card. This is what a click on the stack does.

Request

NameInTypeRequiredDescription
appbodystringrequiredThe app whose stack to change.
groupbodystringoptionalThe stack's group. Default: the app id.
expandedbodybooleanoptionaltrue opens the stack, false closes it. Default true.
Example requestshell
curl -s -X POST "$HERALD/v1/stacks/expand" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"app": "example.bidbot", "group": "acme", "expanded": true}'
Example responseJSON
{"ok": true}

Errors

StatusWhen
400app is missing.
404There is no stack of two or more notifications for this app and group.

GET /v1/settings/stacking#

Returns the global stacking level and the levels you can choose from.

Request

No parameters.

Example requestshell
curl -s "$HERALD/v1/settings/stacking" -H "Authorization: Bearer $TOKEN"
Example responseJSON
{"level": "bySender", "levels": ["byApp", "byIssuer", "bySender", "never"]}

PUT /v1/settings/stacking#

Sets the global stacking level. Apps with their own level keep it.

Request

NameInTypeRequiredDescription
levelbodystringrequiredbyApp, byIssuer, bySender or never.
Example requestshell
curl -s -X PUT "$HERALD/v1/settings/stacking" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"level": "byIssuer"}'
Example responseJSON
{"level": "byIssuer", "levels": ["byApp", "byIssuer", "bySender", "never"]}

Errors

StatusWhen
400level is missing or not one of the four values.

Notes

Edit this page on GitHub

Esc
Getting started
Guides