Notifications API

Notifications track system events, task completions, errors, and approval requests. The pending-approvals stream provides real-time SSE updates so the UI can surface human-in-the-loop decisions without polling.

Quick Start

List pending notifications, approve a tool request, and stream approvals in real time:

// 1. Check for unread notifications
const res1: Response = await fetch('/api/notifications?unread=true');
const notifications: Array<{ id: string; type: string; title: string }> = await res1.json();
console.log(`${notifications.length} unread notifications`);

// 2. List pending approval requests (agents waiting for permission)
const res2: Response = await fetch('/api/notifications/pending-approvals');
const approvals: Array<{ id: string; taskId: string; toolName: string }> = await res2.json();

if (approvals.length > 0) {
console.log(`${approvals.length} approvals waiting`);

// 3. Approve the first pending request (via Tasks API respond endpoint)
const approval = approvals[0];
await fetch(`/api/tasks/${approval.taskId}/respond`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    notificationId: approval.id,
    behavior: 'allow',
  }),
});
}

// 4. Stream approvals in real time so the UI can react instantly
const source: EventSource = new EventSource('/api/notifications/pending-approvals/stream');
source.onmessage = (event: MessageEvent) => {
const pending: Array<{ id: string; taskId: string }> = JSON.parse(event.data);
console.log(`${pending.length} approval(s) pending`);
};

// 5. Mark everything as read when done
await fetch('/api/notifications/mark-all-read', { method: 'PATCH' });

Base URL

/api/notifications

Endpoints

List Notifications

GET /api/notifications

Retrieve notifications with optional filtering. Returns up to 100 results, newest first.

Query Parameters

Param Type Req Description
unread string Filter to unread only when set to "true"
type string Filter by notification type
countOnly string When "true", return only the unread count

Response Body (Array)

FieldTypeReqDescription
idstring (UUID)*Notification identifier
typeenum*Notification type (task_complete, error, approval, etc.)
titlestring*Notification title
messagestringNotification body text
readboolean*Whether the notification has been read
createdAtISO 8601*Creation timestamp

Response Body (countOnly)

FieldTypeReqDescription
countnumber*Count of unread notifications

Fetch unread notifications to power a notification badge or dropdown in the UI:

// Fetch unread notifications for the UI badge
const res: Response = await fetch('http://localhost:3000/api/notifications?unread=true');
const notifications: Array<{ type: string; title: string; message: string }> = await res.json();

notifications.forEach((n) => {
const icon: string = n.type === 'error' ? '!' : n.type === 'approval' ? '?' : 'i';
console.log(`[${icon}] ${n.title} — ${n.message}`);
});

// Or just get the count for a badge
const countRes: Response = await fetch('http://localhost:3000/api/notifications?countOnly=true');
const { count }: { count: number } = await countRes.json();
console.log(`${count} unread`);

Example response:

[
  {
    "id": "notif-8a3b-2c1d",
    "type": "task_complete",
    "title": "Task completed",
    "message": "Analyze Q4 revenue trends finished successfully",
    "read": false,
    "createdAt": "2026-04-03T10:32:44.000Z"
  },
  {
    "id": "notif-7b4c-3d2e",
    "type": "approval",
    "title": "Permission required",
    "message": "Agent wants to write to /reports/q4-analysis.md",
    "read": false,
    "createdAt": "2026-04-03T10:31:20.000Z"
  }
]

Mark Notification Read

PATCH /api/notifications/{id}

Update the read status of a single notification.

Request Body

FieldTypeReqDescription
readbooleanRead status (defaults to true)

Response 200 — Updated notification object

Mark a notification as read after the user has seen it:

// Mark as read when the user clicks on it
await fetch('http://localhost:3000/api/notifications/notif-8a3b-2c1d', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ read: true }),
});

Errors: 404 — Notification not found

Delete Notification

DELETE /api/notifications/{id}

Permanently delete a single notification.

Response 200{ "success": true }

// Permanently delete a notification
await fetch('http://localhost:3000/api/notifications/notif-8a3b-2c1d', { method: 'DELETE' });

Mark All Read

PATCH /api/notifications/mark-all-read

Mark all unread notifications as read in a single operation.

Response 200{ "success": true }

Clear the notification badge by marking everything as read:

// Clear the notification badge
await fetch('http://localhost:3000/api/notifications/mark-all-read', { method: 'PATCH' });

Pending Approvals

List Pending Approvals

GET /api/notifications/pending-approvals

List all pending approval payloads that require human-in-the-loop decisions.

Response 200 — Array of approval payload objects

Fetch pending approvals to show a queue of agent requests waiting for human decisions:

// List approval requests waiting for human decision
const res: Response = await fetch('http://localhost:3000/api/notifications/pending-approvals');
const approvals: Array<{ taskId: string; toolName: string; message: string }> = await res.json();

approvals.forEach((a) => {
console.log(`Task ${a.taskId}: ${a.toolName} — ${a.message}`);
});

Example response:

[
  {
    "id": "notif-7b4c-3d2e",
    "taskId": "task-9d4e-a1b2",
    "toolName": "Write",
    "toolInput": { "file_path": "/reports/q4-analysis.md" },
    "message": "Agent wants to write to /reports/q4-analysis.md",
    "createdAt": "2026-04-03T10:31:20.000Z"
  }
]

Stream Pending Approvals (SSE)

SSE /api/notifications/pending-approvals/stream

Server-Sent Events stream that pushes real-time updates whenever the pending approvals list changes. Polls the database every 750ms and only emits when the set changes. Sends keepalive comments every 15 seconds.

Event format: data: [<approval>, ...]

Keepalive: : keepalive

Connect to the SSE stream to get instant updates when agents request permissions — no polling needed:

// Real-time approval stream — UI can react instantly
const source: EventSource = new EventSource('http://localhost:3000/api/notifications/pending-approvals/stream');

source.onmessage = (event: MessageEvent) => {
const approvals: Array<{ taskId: string; toolName: string; message: string }> = JSON.parse(event.data);

if (approvals.length === 0) {
  console.log('No pending approvals');
  return;
}

// Show each pending approval in the UI
approvals.forEach((a) => {
  console.log(`[${a.taskId}] ${a.toolName}: ${a.message}`);
});
};

source.onerror = () => {
console.error('SSE connection lost — will auto-reconnect');
};

Example stream events:

data: [{"id":"notif-7b4c-3d2e","taskId":"task-9d4e-a1b2","toolName":"Write","message":"Agent wants to write to /reports/q4-analysis.md"}]

: keepalive

data: []

Notification Types

TypeDescription
task_completeA task finished execution successfully.
errorAn error occurred during task or workflow execution.
approvalA tool invocation requires human approval before proceeding.
systemSystem-level events (budget warnings, runtime changes).