Notifications and notification subscriptions
Read a Person's notification inbox, set which channels deliver notifications, and route an Account's notifications to specific people with the V3 API.
This page covers three things: the notification inbox of a Person, the channels that deliver notifications, and subscriptions that route an Account's notifications to the people who should see them.
A notification is created from a template. Each template has a handle (for example InvoicePaid) and a group. Wingspan creates the notification for each recipient and delivers it on the channels the recipient allows: InApp, Email or SMS. Your integration does not create notifications. The routes that emit and clear notifications are internal and are not part of the public API.
Prerequisites
- A Person login session or token for the inbox and channel-settings routes. These routes act on the signed-in Person only.
- For subscriptions, a credential with
notifications.notification:readto read andnotifications.notification:writeto create or delete, andX-Wingspan-Accountset to the Account whose notifications you route.
Endpoints
| Purpose | Method and path |
|---|---|
| List inbox | GET /v3/platform/persons/{personId}/notifications |
| Get a notification | GET /v3/platform/persons/{personId}/notifications/{notificationId} |
| Mark viewed | POST /v3/platform/persons/{personId}/notifications/{notificationId}/view |
| Dismiss | POST /v3/platform/persons/{personId}/notifications/{notificationId}/dismiss |
| Bulk mark viewed | POST /v3/platform/persons/{personId}/notifications/bulk-view |
| Bulk dismiss | POST /v3/platform/persons/{personId}/notifications/bulk-dismiss |
| Get channel settings | GET /v3/platform/persons/{personId}/notification-channel-settings |
| Update channel settings | PATCH /v3/platform/persons/{personId}/notification-channel-settings |
| List templates | GET /v3/platform/notification-templates |
| List subscriptions | GET /v3/platform/notification-subscriptions |
| Create a subscription | POST /v3/platform/notification-subscriptions |
| Get a subscription | GET /v3/platform/notification-subscriptions/{subscriptionId} |
| Delete a subscription | DELETE /v3/platform/notification-subscriptions/{subscriptionId} |
Person-scoped routes
The inbox, bulk and channel-settings routes belong to a Person, not an Account. Do not send X-Wingspan-Account on them. If you do, the API returns 400 with code: AccountScopeNotApplicable. If personId is not the Person you are signed in as, the API returns 404 ResourceNotFound.
Read the inbox
curl -g "https://api.wingspan.app/v3/platform/persons/Pq3mT8vLw2NzKc5xRb7HdA/notifications?filter[priority][eq]=High&page[size]=25" \
-H "Authorization: Bearer $WINGSPAN_TOKEN"Results are newest first. There is no sort parameter. When you omit filter[status][anyOf], the list returns Active and Viewed notifications. Only notifications whose InApp delivery reached Sent or Delivered appear.
| Filter | Values |
|---|---|
filter[status][anyOf][] | Active, Viewed, Dismissed |
filter[priority][eq] | Low, Normal, High |
filter[handle][eq], filter[group][eq] | Template handle or group. See List templates. |
filter[subjectAccountId][eq] | Notifications about one Account |
filter[isPersonal][eq] | true for notifications that belong to the Person and not to an Account |
filter[createdAt][gte], filter[createdAt][lte] | Date-time range |
To count unread notifications, request filter[status][anyOf][]=Active and read pagination.totalSize.
{
"url": "/v3/platform/persons/Pq3mT8vLw2NzKc5xRb7HdA/notifications?filter[priority][eq]=High",
"data": [
{
"id": "Hk9rW4tNx2LbQv6cYm8DeP",
"handle": "InvoicePaid",
"status": "Active",
"priority": "High",
"type": "Dismissable",
"subject": "Invoice paid",
"message": "Invoice 1042 was paid.",
"personId": "Pq3mT8vLw2NzKc5xRb7HdA",
"subjectAccountId": "Nw7kQ2pLx9RtVb3mHc5dZa",
"channels": ["InApp", "Email"],
"events": { "createdAt": "2026-11-02T14:05:11.000Z" }
}
],
"pagination": { "nextPageToken": "", "totalSize": 1 }
}The example values are illustrative. A notification also carries actionTitle, actionUrl, attachments, deliveries, referenceKey, expiresAt, context and metadata when they apply.
Mark viewed or dismiss
Status moves one way: Active, then Viewed, then Dismissed. Both calls take no body and return the updated notification.
curl -X POST "https://api.wingspan.app/v3/platform/persons/Pq3mT8vLw2NzKc5xRb7HdA/notifications/Hk9rW4tNx2LbQv6cYm8DeP/view" \
-H "Authorization: Bearer $WINGSPAN_TOKEN" \
-H "Idempotency-Key: $(uuidgen)"| Status | Cause |
|---|---|
404 | The notification does not exist, or belongs to another Person. |
409 InvalidStateTransition | You viewed a Dismissed notification, or you dismissed a NonDismissable one. |
409 IdempotencyKeyConflict | You replayed an Idempotency-Key with a different request. |
Bulk view and bulk dismiss
Send exactly one of filter or notificationIds. notificationIds takes 1 to 100 IDs. filter accepts subjectAccountId, isPersonal, status, handle, group, priority, createdAtGte and createdAtLte. If you omit limit in a filter, every match is selected. isPersonal: true together with subjectAccountId returns 400.
curl -X POST "https://api.wingspan.app/v3/platform/persons/Pq3mT8vLw2NzKc5xRb7HdA/notifications/bulk-dismiss" \
-H "Authorization: Bearer $WINGSPAN_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{ "filter": { "handle": "InvoicePaid" } }'The response is 202 with an operation. A small selection can come back already Completed. Otherwise status is Processing: poll the URL in the Location header with the same Person token and no account header until the status is terminal. See Async operations. result reports matched, succeeded, skipped and failed. Bulk dismiss skips NonDismissable notifications and counts them in skipped.
Choose delivery channels
Get channel settings always works. A Person who never changed anything has defaultChannels of isInAppEnabled: true, isEmailEnabled: true, isSmsEnabled: false, and no overrides.
Update channel settings changes only the flags you send. If you send overrides, the list replaces the whole existing list, and [] clears it. Each override sets exactly one of handle or group, plus any of isInAppEnabled, isEmailEnabled and isSmsEnabled. Flags you leave out inherit defaultChannels. Templates that force delivery ignore these settings.
curl -X PATCH "https://api.wingspan.app/v3/platform/persons/Pq3mT8vLw2NzKc5xRb7HdA/notification-channel-settings" \
-H "Authorization: Bearer $WINGSPAN_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"defaultChannels": { "isSmsEnabled": true },
"overrides": [ { "handle": "InvoicePaid", "isEmailEnabled": false } ]
}'An override with both or neither of handle and group returns 422.
Route an Account's notifications with subscriptions
A subscription is a routing rule: notifications on an Account that match a handle or a group go to a recipient Person's inbox. Use List templates to see the valid values. The list is the same for every Account, and it rejects X-Wingspan-Account.
curl -X POST https://api.wingspan.app/v3/platform/notification-subscriptions \
-H "Authorization: Bearer $WINGSPAN_TOKEN" \
-H "X-Wingspan-Account: Nw7kQ2pLx9RtVb3mHc5dZa" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"accountId": "Nw7kQ2pLx9RtVb3mHc5dZa",
"recipientPersonId": "Pq3mT8vLw2NzKc5xRb7HdA",
"handle": "InvoicePaid"
}'| Field | Required | Notes |
|---|---|---|
accountId | Yes | The Account whose notifications are routed. |
recipientPersonId | Yes | Must be a Stakeholder on that Account. |
handle or group | Exactly one | handle is an exact template handle. group is a coarser set. |
externalId, metadata | No | Your own reference and key-value data. |
Subscriptions cannot be edited. To change routing, delete the subscription and create a new one. Delete is a soft delete: the record gets events.deletedAt, and List subscriptions hides it unless you pass filter[includeDeleted][eq]=true. List also filters by recipientPersonId, handle and group. It does not filter by externalId.
| Status | Cause |
|---|---|
400 | A malformed request. |
409 ResourceConflict | A subscription with the same routing already exists. Create is not an upsert. |
422 | You sent both or neither of handle and group, or another field is invalid. |
404 | The subscription does not exist for this Account. |
Webhook events
These events are published: Notification.Created, Notification.Sent, Notification.Delivered, Notification.Failed, Notification.Viewed, Notification.Dismissed, NotificationSubscription.Created and NotificationSubscription.Deleted. See Event types.
Updated about 1 hour ago