Skip to main content
HQ has two distinct in-app surfaces, and they are easy to confuse:
  • Inbox — your personal feed of agent-delivered messages (for example, scheduled-task digests). It is scoped to you alone; admins never see another user’s inbox. Inbox messages carry rich content and can be deleted.
  • Notifications — the workspace notification center: async results a tenant should see (recommendations, profile-ready, setup nudges, billing). Items can be addressed to you or be workspace-wide (visible to any member), and they are dismissed rather than deleted.
Both are read with a Bearer token and live under /v1/api.
Every request authenticates with Authorization: Bearer hq_pat_.... All nine endpoints below accept either a personal access token (bearer_pat) or an oauth2 token.

What each item carries

The two list payloads are shaped differently because the surfaces solve different problems. An inbox message (List inbox) carries id, source_kind, schedule_id, schedule_name, title, body_markdown, ui_strings, delivered_at, and read_at. The body is Markdown, and when the message came from a schedule fire it includes the originating schedule’s id and name so the UI can group and link. Read state is a timestamp: read_at is null while unread. A notification (List notifications) carries id, kind, title, body (optional), cta (optional), severity, status, and created_at. The body is optional plain text, there is an optional call-to-action (cta), and read state is an explicit status string (unread, read, or dismissed).
Read state differs between surfaces. The inbox tracks a read_at timestamp (null = unread). Notifications track a status string. That is why the inbox supports re-marking a message unread, but notifications do not.

The inbox

1

List your inbox

GET /v1/api/inbox returns one page of your messages, newest delivery first.
curl https://api.hq.zone/v1/api/inbox \
  -H "Authorization: Bearer hq_pat_..."
# → { "messages": [ ... ], "unread_count": 3, "total": 42, "page": 1, "page_size": 25 }
The response is a ListResp: messages (the page), unread_count (unread across your whole inbox — drives the nav badge, independent of the current page/filter), total (rows matching the filter before paging), page, and page_size.Supported query params:
  • unread=true — only messages whose read_at is null.
  • q=<text> — case-insensitive substring search over the title, body, and (for schedule digests) the schedule name.
  • page=<n> — 1-based page index, clamped to >= 1.
  • page_size=<n> — rows per page, clamped to 1..=100, defaults to 25.
  • sort=recent (default, newest first) or sort=oldest.
# Unread only, search "revenue", second page of 50, oldest first
curl -G https://api.hq.zone/v1/api/inbox \
  -H "Authorization: Bearer hq_pat_..." \
  --data-urlencode "unread=true" \
  --data-urlencode "q=revenue" \
  --data-urlencode "page=2" \
  --data-urlencode "page_size=50" \
  --data-urlencode "sort=oldest"
A legacy limit=<n> param is still honored as the page_size fallback for older clients. Prefer page_size in new code.
2

Mark one message read or unread

Reading an inbox message just sets (or clears) its read_at timestamp.
# Mark read — preserves the original read_at if already read
curl -X POST https://api.hq.zone/v1/api/inbox/<MESSAGE_ID>/read \
  -H "Authorization: Bearer hq_pat_..."
# → { "ok": true }

# Mark unread — clears read_at back to null
curl -X POST https://api.hq.zone/v1/api/inbox/<MESSAGE_ID>/unread \
  -H "Authorization: Bearer hq_pat_..."
# → { "ok": true }
Both return { "ok": true } (mark read, mark unread). Because the lookup is scoped to your own inbox, a message that does not exist — or is not yours — returns 404.
3

Mark the whole inbox read

POST /v1/api/inbox/read_all marks all of your unread messages read. It is idempotent — it returns ok even when nothing was unread.
curl -X POST https://api.hq.zone/v1/api/inbox/read_all \
  -H "Authorization: Bearer hq_pat_..."
# → { "ok": true }
See Mark all inbox read.
4

Delete a message

The inbox is the only surface where items are permanently deleted.
curl -X DELETE https://api.hq.zone/v1/api/inbox/<MESSAGE_ID> \
  -H "Authorization: Bearer hq_pat_..."
# → { "ok": true }
Returns { "ok": true }, or 404 if the message does not exist or is not yours. See Delete inbox message.

The notification center

1

List notifications

GET /v1/api/notifications returns up to 100 non-dismissed notifications, newest first. There are no query params — no filters, no pagination.
curl https://api.hq.zone/v1/api/notifications \
  -H "Authorization: Bearer hq_pat_..."
# → { "items": [ ... ], "unread": 2 }
The response is a NotificationsResp: items and unread (how many of the returned-scope rows are still unread — drives the bell badge). The scope includes notifications addressed to you and workspace-wide ones visible to any member. Dismissed notifications are excluded. See List notifications.
2

Mark one notification read

POST /v1/api/notifications/{id}/read flips an unread notification to read and records the read time.
curl -X POST https://api.hq.zone/v1/api/notifications/<NOTIFICATION_ID>/read \
  -H "Authorization: Bearer hq_pat_..."
# → { "ok": true }
It only affects a notification currently in the unread state and always returns { "ok": true }. See Mark notification read.
3

Mark all notifications read

POST /v1/api/notifications/read-all marks all of your unread notifications read and returns the count.
curl -X POST https://api.hq.zone/v1/api/notifications/read-all \
  -H "Authorization: Bearer hq_pat_..."
# → { "updated": 2 }
Unlike the inbox’s read_all (which returns ok), this returns { "updated": <n> } — the number of rows updated. See Mark all notifications read.
4

Dismiss a notification

Notifications are not deleted; they are dismissed. POST /v1/api/notifications/{id}/dismiss sets status to dismissed, records the dismiss time, and removes it from future list responses.
curl -X POST https://api.hq.zone/v1/api/notifications/<NOTIFICATION_ID>/dismiss \
  -H "Authorization: Bearer hq_pat_..."
# → { "ok": true }
Always returns { "ok": true }. See Dismiss notification.
There is no “un-dismiss” and no inbox-style unread toggle for notifications. Dismissing is one-way: the item drops out of the list permanently.

Key differences at a glance

  • Scope. Inbox is per-user (owner_user_id); notifications are per-workspace and may be addressed to you or visible to every member.
  • Read state. Inbox uses a read_at timestamp and supports re-marking unread; notifications use a status string and do not.
  • Removal. Inbox messages are DELETEd; notifications are dismissed (and disappear from the list, but are not re-markable).
  • Listing. The inbox supports unread filtering, search, pagination, and sort; the notification list takes no params and caps at 100 newest non-dismissed items.
  • Badges. Inbox unread_count is whole-inbox; notification unread is over the returned scope.
  • Mark-all response. Inbox read_all returns { "ok": true }; notifications read-all returns { "updated": <n> }.