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
messagestring—Notification 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
readboolean—Read 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).