Desktop Notifications Over HTTP.
Send Linux desktop notifications to any container display with a single HTTP POST. The Notification Server calls notify-send on your container's X11 display — trigger, fetch history, or stream live.
# Send a notification to display 0
curl -X POST ".../notify"
-d {"display":"0","summary":"Build Complete","urgency":"normal"}
# Response
{"message":"Notification sent successfully","success":true}
Three Steps. One HTTP Call Each.
Every notification follows the same lifecycle: trigger it, fetch the history or stream it live, then dismiss it when done.
Trigger
POST /notify sends a notify-send call to the target X11 display. Supply display, summary, and optionally urgency, body, expire_time, or icon.
# Fire a notification
POST /notify
{"display":"0",
"summary":"Build Complete",
"urgency":"normal"}
{"success":true}
Fetch or Stream
GET /{display} retrieves notification history with optional limit, since, username, and session filters. Connect via WebSocket to /stream?displays=0 for real-time push updates.
# Poll history
curl ".../0?limit=50"
# Or open a WebSocket
new WebSocket(
"wss://.../stream?displays=0"
)
Dismiss
POST /dismiss marks notification IDs as dismissed — they are filtered from subsequent GET responses. DELETE /dismiss clears the dismissed state, making them visible again.
# Dismiss by ID
POST /dismiss
{"displayId":"0",
"notificationIds":[10,11,12]}
# Clear dismissed state
DELETE /dismiss?displayId=0
low. normal. critical.
Three urgency levels map directly to notify-send behavior. Pick the right one and the notification daemon handles the rest.
Dismisses quickly. Set expire_time: 3000 for background tasks, telemetry, or informational pings that should not interrupt focus.
"urgency": "low",
"expire_time": 3000
Standard. Default when urgency is omitted. The notification daemon shows it at the configured timeout and dismisses automatically.
"urgency": "normal"
// default — omit to use this
Persistent — requires manual dismissal. Set expire_time: 0 for system failures, build errors, or anything that needs immediate action.
"urgency": "critical",
"expire_time": 0
Fetch History or Stream Live.
Two read channels serve different needs: REST for audit logs and one-shot queries, WebSocket for dashboards and real-time monitors.
Pull notification history for a display. Filter by limit, since timestamp, username, or session ID. Use for audit logs, one-shot queries, and batch processing.
# get last 50 for display 0
curl ".../0?limit=50"
# time-filtered
curl ".../0?since=1749025000000"
JSON array of notification objects with id, summary, urgency, timestamp, and icon_url.
Subscribe to one or more displays with displays=0,:1,2 or displays=all. Receives push messages as they arrive. Use for dashboards and real-time monitoring.
# open a WebSocket
const ws = new WebSocket(
"wss://.../stream?displays=0"
)
# on message
ws.onmessage = (e) => {
const msg = JSON.parse(e.data)
// msg.type === 'notification'
}
{ type: "notification", data: { id, summary, urgency, timestamp } } pushed on every new notification.
From Build Pipelines to ML Jobs.
Anywhere a long-running process needs to signal completion or failure, one HTTP POST is all it takes.
CI/CD Alerts
Long-running builds complete. HTTP POST triggers a notification on the container display — visible in any active display session.
System Monitoring
Server alerts route to a desktop notification on the container's X11 display, visible in any active display session.
Long-Running Tasks
Data exports, video rendering, ML model training, backup completion — notify when done without polling.
Automated Workflows
Cron jobs, scheduled tasks, and automation scripts notify the container display on completion or failure.
Notifications Are One HTTP POST Away.
Any script, service, or automation that can make an HTTP request can send a desktop notification to your container display.