Skip to main content

Notifications endpoints

In-app notifications for the logged-in user: assigned reviews, mentions, publications, etc. Available via classic REST + Server-Sent Events for real-time count.

Endpoint table

MethodPathAuthDescription
GET/notificationsSessionLists notifications and counts.
POST/notifications/mark-readSessionMarks notifications as read.
GET/notifications/streamSessionSSE — count updated in real time.

Scope: these endpoints require a user session (JWT/cookie). context.user_id and context.organization_id must be filled — pure API keys don't support notifications.

GET /notifications

Returns the user's notifications list for the active organization.

Query params:

  • limit — 1–200. Default 50.
  • unread_onlytrue returns only unread. Default false.

Response 200 (NotificationListResponse):

{
"items": [
{
"id": "ntf_...",
"user_id": "usr_...",
"organization_id": "org_...",
"kind": "review.created",
"title": "New review on Edge rules project",
"body": "Ana requested your review on 'Refactor sshd'",
"link": "/projects/prj_.../reviews/rev_...",
"read_at": null,
"created_at": "2026-04-24T14:00:00Z"
}
],
"total": 12,
"unread": 3
}
  • kind — notification type (review.created, review.approved, version.published, api.quota.warning, etc.).
  • read_atnull until read; ISO 8601 once marked.
  • total — total in the returned period.
  • unread — total unread.

POST /notifications/mark-read

Marks specific notifications or all as read.

Request body (NotificationMarkReadRequest):

{ "ids": ["ntf_aaa", "ntf_bbb"] }
  • ids — list of IDs. If empty/absent, marks all unread notifications for the user in the organization as read.

Response 200: updated NotificationListResponse (same shape as GET /notifications).

GET /notifications/stream (SSE)

Real-time stream that emits the unread count every time it changes.

Request headers:

Accept: text/event-stream
Authorization: Bearer <jwt>

(API keys don't work here — it's a session endpoint.)

Stream format:

event: unread
data: {"unread": 3}

: keep-alive

event: unread
data: {"unread": 4}

: keep-alive
  • The first event: unread arrives immediately on connect (seeds client state).
  • : keep-alive is an SSE comment to prevent proxies from closing the connection.
  • The next event: unread only appears when the count changes.

Limits

  • Concurrent connections per user are limited. If exceeded, 429 sse_connection_limit_exceeded with Retry-After. Reuse the open connection.
  • The server uses internal polling (5 s) to detect changes — maximum notification latency is ~5 s.

Example (browser)

const es = new EventSource("/api/v1/notifications/stream", { withCredentials: true })

es.addEventListener("unread", (ev) => {
const { unread } = JSON.parse(ev.data)
document.getElementById("badge").textContent = String(unread)
})

es.onerror = (err) => {
console.error("SSE failed", err)
// browser EventSource auto-reconnects; it respects Retry-After on 429.
}

Example (curl)

curl -N "$RF_BASE/notifications/stream" \
-H "Accept: text/event-stream" \
-H "Authorization: Bearer $JWT"

(-N disables curl buffering so we can see events arriving.)

Shared structures

NotificationRecord

{
"id": "string",
"user_id": "string",
"organization_id": "string",
"kind": "string",
"title": "string",
"body": "string",
"link": "string|null",
"read_at": "ISO 8601|null",
"created_at": "ISO 8601"
}

NotificationListResponse

{
"items": [ "NotificationRecord[]" ],
"total": 0,
"unread": 0
}
  • Webhooks — external notifications triggered by the same events.
  • Reviews — flow that generates notifications.