API reference

Notifications

Push a notification into a user's IntelligenceBox desktop app — a title, a description, an icon, and a deep link that opens the right screen when clicked. Notifications are stored before they are delivered, so one sent while the app was closed reappears in the bell on the next launch instead of vanishing.

Delivery model

Every notification is written to the user’s feed first, then emitted over WebSocket to any connected app. If nobody is connected, the row is still there — this is what makes the API usable for build failures, nightly jobs and anything else that fires outside working hours.

Send a notification#

POST/api/public/notificationsAPI key

Creates and delivers a notification. Returns the stored notification, including its id.

curl -X POST BOX_URL/api/public/notifications \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Deploy finished",
    "description": "api-gateway v2.4.1 is live in production.",
    "type": "success",
    "category": "backgroundTasks",
    "icon": "Rocket",
    "name": "CI pipeline",
    "deepLink": "/inter/chat/deploy-log-2f8a"
  }'

Request body

titlestringrequired
The headline. Shown as the toast title and the row title in the bell. Max 200 characters.
descriptionstring
The body text, shown under the title. body is accepted as an alias. Max 2000 characters.
type"info" | "success" | "error"
Drives the accent colour and icon tone. Errors also hold the toast on screen longer.Default: "info".
categorystring
Groups the notification and picks its default glyph. See categories.Default: "system".
deepLinkstring
In-app route opened when the notification is clicked, e.g. /calendar. Must start with / — see deep links.
iconstring
A Lucide icon name in PascalCase, e.g. Rocket, AlertTriangle, Package.
namestring
Who the notification is from, e.g. "CI pipeline". Rendered next to the icon.
senderobject
Full control over the leading visual — see sender. Takes precedence over icon and name.
previewstring
Short secondary line. Preferred over description when both are set.
readboolean
Create it already read. It lands in history without raising the unread badge — useful for audit-trail entries.Default: false.
dedupeKeystring
Collapses repeats: a second send with the same key refreshes the existing row instead of stacking a new one.
resurfaceboolean
Only meaningful with dedupeKey. Set false for a persistent state (“sync is paused”) so it is announced once and stays quiet on every later identical send.Default: true.
silentToastboolean
Keep the OS popup and the bell row, skip the in-app toast.Default: false.
silentboolean
Persist only. The row appears in the bell with no popup and no toast.Default: false.
chatIdstring
Opens this conversation on click and adds an "Open chat" action to the toast.
userIdstring
Deliver to another user instead of the key owner. Requires an organization context — see sending to someone else.
metadataobject
Arbitrary JSON echoed back when the notification is read. Max 8 KB serialised.

Response

JSON
{
  "delivered": true,
  "persisted": true,
  "notification": {
    "id": "5c1e0f2a-9d84-4a1b-8f3c-2b7e0a6d1f45",
    "title": "Deploy finished",
    "description": "api-gateway v2.4.1 is live in production.",
    "type": "success",
    "category": "backgroundTasks",
    "deepLink": "/inter/chat/deploy-log-2f8a",
    "chatId": null,
    "icon": "Rocket",
    "sender": { "kind": "system", "name": "CI pipeline", "icon": "Rocket" },
    "read": false,
    "seen": false,
    "createdAt": "2026-08-03T09:14:22.401Z"
  }
}
  • 201Created and delivered.
  • 202Delivered live but not stored (persisted: false). The user sees it now; it will not be in the bell later.
  • 400Validation failed — check details for the offending field.
  • 403Recipient is outside the organization workspace.

Icon, name and avatar#

The leading visual is picked from the sender object. Four kinds, each rendering differently.

kind"system" | "user" | "assistant" | "email"
system renders icon as a glyph; user and assistant render imageUrl as an avatar; email renders sender initials.Default: "system".
namestring
Display name shown beside the notification.
imageUrlstring
Absolute URL to an avatar image. Used by the user and assistant kinds.
iconstring
Lucide icon name, used when there is no image.
colorstring
Hex tint for the glyph background, e.g. #3B82F6.
emailstring
Sender address — drives the initials fallback for the email kind.
JSON
{
  "title": "Marco Rossi mentioned you",
  "description": "\"can you review the Q3 numbers?\"",
  "category": "messaging",
  "deepLink": "/messaggi",
  "sender": {
    "kind": "user",
    "name": "Marco Rossi",
    "imageUrl": "https://example.com/avatars/marco.jpg"
  }
}

Shorthand

For the common system case you can skip the object entirely and send icon and name at the top level. They are folded into a system sender for you.

Where clicking the notification takes the user. Any in-app route works; these are the ones you will reach for.

RouteOpens
/inter/chat/:chatIdA specific conversation
/dc/vector/:vectorIdA folder and its documents
/assistantsThe assistant list
/calendarThe calendar
/emailThe unified inbox
/messaggiMessaging
/messaggi?channel=:idOne messaging channel
/contactsThe address book
/workWork management
/recordingsAudio recordings
/workboxWorkbox automations
/settingsSettings

In-app routes only

deepLink must start with /. Absolute URLs are rejected with a 400: the app hands the value to its own router, so an https:// value would be a dead click at best.

When several targets are present the most specific one wins: chatId beats deepLink. A chatId also adds an explicit “Open chat” button to the toast.

Categories#

The category decides the default glyph, the label on the bell row, and which user preference can mute it.

CategoryUse it forMutable
systemAnything that does not fit elsewhereNo
backgroundTasksJobs, builds, imports, long-running workYes
chatAI runs and completionsYes
calendarReminders and eventsYes
emailMail activityYes
messagingMessages and mentionsNo
schedulerScheduled runsYes
contactsContact enrichment and syncYes
transcriptionAudio transcriptionYes
misconductSecurity and moderationYes
appUpdatesApp update availabilityYes

Mutable categories can be switched off by the user

Categories marked mutable map onto a toggle in Settings → Notifications. If the user has turned one off, your notification is accepted but not shown. For alerts that must always arrive, use system or messaging — neither is user-gated.

Avoiding notification spam#

Two knobs for jobs that run on a schedule and keep reporting the same thing.

Refresh one row instead of stacking

A build that reports progress every minute should not leave sixty rows in the bell. Give every send the same dedupeKey and each one updates the existing row in place, marking it unread again.

JSON
{
  "title": "Indexing 340/1200 documents",
  "category": "backgroundTasks",
  "dedupeKey": "index-run:2026-08-03"
}

Announce a persistent state once

A condition that is still true — a paused sync, an expired credential — should be announced once, not on every poll. Add resurface: false: the first send notifies, and every later identical send is silently absorbed.

JSON
{
  "title": "SharePoint sync paused",
  "description": "Credentials expired — reconnect in Settings.",
  "type": "error",
  "dedupeKey": "sync-paused:sharepoint",
  "resurface": false
}

Sending to someone else#

By default a notification goes to the user who owns the API key. To notify a colleague, send their userId together with an organization context — both accounts must belong to the same organization workspace.

Shell
curl -X POST BOX_URL/api/public/notifications \
  -H "x-api-key: YOUR_API_KEY" \
  -H "x-organization-id: YOUR_ORG_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Invoice awaiting approval",
    "description": "INV-2026-0412 from Acme Srl — €12,480.",
    "userId": "8f2c1d40-33ab-4e7e-9c15-6a0b2e91d774",
    "deepLink": "/work"
  }'
  • 403Either no organization header was sent, or the recipient is not part of that workspace.

List notifications#

GET/api/public/notificationsAPI key

Notifications for the key owner, newest first.

limitinteger
How many to return, 1–200.Default: 50.
offsetinteger
Rows to skip, for paging.Default: 0.
unreadOnlyboolean
Return only unread notifications.
categorystring
Filter to one category.
Shell
curl "BOX_URL/api/public/notifications?unreadOnly=true&limit=20" \
  -H "x-api-key: YOUR_API_KEY"
JSON
{
  "notifications": [ /* … */ ],
  "unreadCount": 3,
  "pagination": { "total": 41, "limit": 20, "offset": 0, "hasMore": true }
}

Unread count#

GET/api/public/notifications/unread-countAPI key

Just the number, for a badge somewhere else.

JSON
{ "count": 3 }

Get one notification#

GET/api/public/notifications/:idAPI key

The full stored notification, including its metadata.

Mark read or unread#

PATCH/api/public/notifications/:idAPI key
readboolean
Mark it read (which also marks it seen) or return it to unread.
seenboolean
Record that it was delivered without clearing the unread badge.
Shell
curl -X PATCH BOX_URL/api/public/notifications/5c1e0f2a-9d84-4a1b-8f3c-2b7e0a6d1f45 \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "read": true }'

Mark everything read#

POST/api/public/notifications/read-allAPI key
JSON
{ "updated": 12 }

Delete a notification#

DELETE/api/public/notifications/:idAPI key

Removes it from the feed permanently.

JSON
{ "success": true }

Recipes#

Alert on a failed job, with a link to the logs

Shell
curl -X POST BOX_URL/api/public/notifications \
  -H "x-api-key: $BOX_KEY" -H "Content-Type: application/json" \
  -d '{
    "title": "Nightly import failed",
    "description": "12 of 340 invoices could not be parsed.",
    "type": "error",
    "category": "backgroundTasks",
    "icon": "AlertTriangle",
    "name": "Invoice importer",
    "deepLink": "/dc/vector/'"$FOLDER_ID"'",
    "dedupeKey": "import-failure:invoices"
  }'

File a quiet audit entry

silent plus read: true writes to the history without a popup, a toast, or an unread badge — a log line the user can find if they go looking.

JSON
{
  "title": "Contract archived",
  "description": "MSA-Acme-2026.pdf moved to Legal / Archive.",
  "category": "system",
  "silent": true,
  "read": true,
  "metadata": { "documentId": "b71f…", "actor": "retention-bot" }
}

Related: run an assistant and notify the user when it finishes, or look up the recipient behind your API key.

Notifications | IntelligenceBox