Notifications
In-app notifications (the "bell") are a per-user feed of events addressed to a specific cabinet user (for example, being assigned to a deal or to a deal task). This is the REST side of the same stream that arrives in real time as the notification.created event on the user's personal WebSocket channel: use the socket for the live push and badge, and these endpoints to backfill history on app load, read the unread count, and clear the badge.
Feed scope
The feed is scoped to the current user and their active company (current_company_id): the list, the counter, and "mark all" return only notifications for the active company; marking a single notification read resolves it by the owning user regardless of company.
Notification object
A notification object has these fields:
id— a UUID string.type— a snake_case string identifying the notification kind; the platform currently emitsdeal_assignedanddeal_task_assigned, and the set will grow.title— a short heading.body— a string ornull.data— an object whose fields depend ontype(e.g.deal_id/deal_titlefordeal_assigned).read_at— the ISO timestamp when read, ornullwhile unread.created_at— when it was created.
List response shape and query parameters
Important about the list response shape: GET /v1/notifications returns the standard Laravel paginator envelope — the array under the data key, with the pagination meta fields (current_page, per_page, total, last_page, next_page_url, etc.) sitting flat at the top level. This differs from most REST groups, where a list comes back as data:{ items, pagination }.
Query parameters: filter=unread (return only unread; any other value or omitting it returns all) and per_page (page size, default 20). Note the snake_case per_page (not perPage).
Platform notifications (`GET /platform-notifications`)
GET /platform-notifications (2026-08-24) — not to be confused with the personal "bell" feed above: this is a company-scoped (not per-user) flat log of technical notices from the AISAR platform itself (currently only "channel disconnected", from NotifyChannelDisconnectedJob), replacing the old service-conversation mechanic in chat. It requires the conversation.view permission (the same one the /conversations family uses), the feed window is the last 90 days, newest first, page-based pagination (page/limit, limit 1-50, default 20; a pagination block {page, perPage, total, lastPage}). Rate limit: 120 requests per minute.
A list item: id, kind (the notification type, e.g. channel_disconnected), reason (a human-readable cause, or null), created_at, channel ({id, name, type} or null — null once the channel has since been deleted; the event itself survives), conversation_id and message_id (kept for backward compatibility with notifications inherited from the old mechanic; null on new rows). The feed has no per-item "read" mark — instead, PATCH /me/ui-preferences with aisar_notifications.seen_until (an ISO datetime or null) advances the unseen-cursor consumed by the counters.aisar_notifications key in the Conversations group (GET /conversations/counters).
Endpoints
| Method | Path | Summary |
|---|---|---|
| GET | /v1/notificationsThe user's notification feed in the active company (Laravel paginator envelope). Params: `filter=unread`, `per_page` (default 20). | The user's notification feed in the active company (Laravel paginator envelope). Params: `filter=unread`, `per_page` (default 20). |
| GET | /v1/notifications/unread-countCount of the user's unread notifications in the active company. Response: `{ "count": N }`. | Count of the user's unread notifications in the active company. Response: `{ "count": N }`. |
| POST | /v1/notifications/{notification}/readMark a single notification read (by its UUID). Response: `{ "status": "ok" }`; 404 if the notification is missing or owned by another user. | Mark a single notification read (by its UUID). Response: `{ "status": "ok" }`; 404 if the notification is missing or owned by another user. |
| POST | /v1/notifications/read-allMark all of the user's unread notifications in the active company as read. Response: `{ "status": "ok" }`. | Mark all of the user's unread notifications in the active company as read. Response: `{ "status": "ok" }`. |
| GET | /v1/platform-notificationsCompany-scoped log of platform notifications (e.g. "channel disconnected") over the last 90 days. | Company-scoped log of platform notifications (e.g. "channel disconnected") over the last 90 days. |
Examples
List unread notifications
Request
curl -X GET "https://api.aisar.app/v1/notifications?filter=unread&per_page=20" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"Response
{
"current_page": 1,
"data": [
{
"id": "3f1e6a20-0c7b-4a9e-b2d1-8a5c1e000001",
"company_id": 1,
"user_id": 7,
"type": "deal_assigned",
"title": "You were assigned to deal \"Website redesign\"",
"body": "Assigned by Aigerim",
"data": {
"deal_id": 42,
"deal_title": "Website redesign",
"assigned_by": 3
},
"read_at": null,
"created_at": "2026-02-10T09:15:00.000000Z",
"updated_at": "2026-02-10T09:15:00.000000Z"
}
],
"first_page_url": "https://api.aisar.app/v1/notifications?page=1",
"from": 1,
"last_page": 1,
"last_page_url": "https://api.aisar.app/v1/notifications?page=1",
"next_page_url": null,
"path": "https://api.aisar.app/v1/notifications",
"per_page": 20,
"prev_page_url": null,
"to": 1,
"total": 1
}Unread count
Request
curl -X GET "https://api.aisar.app/v1/notifications/unread-count" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"Response
{
"count": 3
}Mark one notification read
Request
curl -X POST "https://api.aisar.app/v1/notifications/3f1e6a20-0c7b-4a9e-b2d1-8a5c1e000001/read" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"Response
{
"status": "ok"
}Mark all read
Request
curl -X POST "https://api.aisar.app/v1/notifications/read-all" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"Response
{
"status": "ok"
}Platform notification log
Request
curl -X GET "https://api.aisar.app/v1/platform-notifications?limit=20" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"Response
{
"data": {
"items": [
{
"id": 340,
"kind": "channel_disconnected",
"reason": "logged_out",
"created_at": "2026-08-24T09:12:00.000000Z",
"channel": { "id": 3, "name": "Основной WhatsApp", "type": "whatsapp" },
"conversation_id": null,
"message_id": null
}
],
"pagination": { "page": 1, "perPage": 20, "total": 1, "lastPage": 1 }
}
}