<!--
hoody-notifications Subskill (http)
Auto-generated by Hoody Skills Generator
Generated: 2026-05-06T18:27:09.182Z
Model: mimo-v2.5-pro
Mode: http


Tokens: 4724

DO NOT EDIT MANUALLY - Changes will be overwritten on next generation
-->

# hoody-notifications Subskill

## Overview

**hoody-notifications** delivers desktop and device notifications via an HTTP API. It wraps Linux `notify-send` behind a RESTful interface, enabling any authorized client to trigger native desktop notifications on remote displays. The service also supports real-time streaming via WebSocket, notification dismissal workflows, and icon serving.

### When to Use This Service

- Triggering desktop notifications from backend jobs, cron tasks, or event-driven workflows
- Streaming live notification updates to a frontend or monitoring dashboard
- Managing notification lifecycle (dismiss, clear dismissed, retrieve by display)
- Serving notification icon images for UI rendering

### How It Fits Into Hoody

hoody-notifications follows the Hoody Kit service model: it runs as a containerized service with automatic proxy routing, zero DNS configuration, and built-in authentication. All requests go through the Hoody Proxy using the standardized Kit URL pattern. The service exposes 8 endpoints covering notification CRUD, health, metrics, and icon delivery.

### Base URL

All examples use this pattern. Replace placeholders with your actual values:

```
https://{projectId}-{containerId}-n-{serviceId}.{node}.containers.hoody.icu
```

For brevity, examples below use `${BASE_URL}`. Set it before running:

```
export BASE_URL="https://myproject-abc123-n-1.us-east-1.containers.hoody.icu"
```

---

## Common Workflows

### Workflow 1: Trigger a Desktop Notification

Send a notification to a specific display.

**Step 1 — Send the notification:**

```
curl -s -X POST "${BASE_URL}/api/v1/notifications/notify" \
  -H "Content-Type: application/json" \
  -d '{
    "display": ":0",
    "summary": "Build Complete",
    "body": "Project alpha deployed successfully."
  }'
```

**Step 2 — Verify on the target display:**

The notification appears natively on the desktop. To confirm delivery programmatically, retrieve it:

```
curl -s "${BASE_URL}/api/v1/notifications/:0"
```

Expected response contains the notification object with an `id` field.

---

### Workflow 2: Retrieve Notifications for a Display

Fetch all active (non-dismissed) notifications for one or more displays.

**Single display:**

```
curl -s "${BASE_URL}/api/v1/notifications/:0"
```

**Multiple displays (comma-separated):**

```
curl -s "${BASE_URL}/api/v1/notifications/:0,:1"
```

**All displays:**

```
curl -s "${BASE_URL}/api/v1/notifications/all"
```

---

### Workflow 3: Dismiss and Restore Notifications

**Step 1 — Retrieve notifications to get IDs:**

```
curl -s "${BASE_URL}/api/v1/notifications/:0"
```

Extract the `id` values from the response array.

**Step 2 — Dismiss specific notifications:**

```
curl -s -X POST "${BASE_URL}/api/v1/notifications/dismiss" \
  -H "Content-Type: application/json" \
  -d '{
    "notificationIds": ["notif-abc-123", "notif-def-456"]
  }'
```

**Step 3 — Verify dismissal (dismissed notifications no longer appear):**

```
curl -s "${BASE_URL}/api/v1/notifications/:0"
```

The dismissed notifications are filtered out of the response.

**Step 4 — Restore dismissed notifications:**

```
curl -s -X DELETE "${BASE_URL}/api/v1/notifications/dismiss"
```

**Step 5 — Verify restoration:**

```
curl -s "${BASE_URL}/api/v1/notifications/:0"
```

Previously dismissed notifications are visible again.

---

### Workflow 4: Real-Time Notification Stream

Establish a WebSocket connection to receive live notification updates.

**Connect with query parameter specifying displays:**

```
# Using websocat (install separately)
websocat "${BASE_URL}/api/v1/notifications/stream?displays=:0,:1"
```

The server pushes notification events as JSON over the WebSocket. Each event contains the full notification object when a new notification is triggered on a subscribed display.

**Subscribe to all displays:**

```
websocat "${BASE_URL}/api/v1/notifications/stream?displays=all"
```

---

### Workflow 5: Serve Notification Icons

Retrieve an icon image by its ID. Icon IDs are derived from notification data (session, ID, timestamp, extension).

**Step 1 — Get a notification to find its icon ID:**

```
curl -s "${BASE_URL}/api/v1/notifications/:0"
```

Extract the icon ID from the notification object.

**Step 2 — Fetch the icon:**

```
curl -s "${BASE_URL}/api/v1/notifications/icons/abc123-session-1700000000.png" \
  -o notification-icon.png
```

The response is an image file (PNG, JPEG, etc.) suitable for direct use in UIs.

---

## Advanced Operations

### Multi-Step: Notification Lifecycle Management

A complete notification lifecycle from creation to cleanup:

```
# 1. Trigger notification
curl -s -X POST "${BASE_URL}/api/v1/notifications/notify" \
  -H "Content-Type: application/json" \
  -d '{
    "display": ":0",
    "summary": "System Alert",
    "body": "Disk usage at 90%"
  }'

# 2. Verify delivery
curl -s "${BASE_URL}/api/v1/notifications/:0"

# 3. Dismiss after acknowledgment
curl -s -X POST "${BASE_URL}/api/v1/notifications/dismiss" \
  -H "Content-Type: application/json" \
  -d '{
    "notificationIds": ["extracted-id-here"]
  }'

# 4. Confirm dismissal
curl -s "${BASE_URL}/api/v1/notifications/:0"
```

### Monitoring Integration

**Health check (unauthenticated):**

```
curl -s "${BASE_URL}/api/v1/notifications/health"
```

Returns a standardized 9-field health response with HTTP 200 when the service is up.

**Prometheus metrics:**

```
curl -s "${BASE_URL}/api/v1/notifications/metrics"
```

Returns server metrics in Prometheus text exposition format. Scrape this endpoint with your Prometheus instance.

### Error Recovery Patterns

**If notification trigger fails:**
1. Check health: `GET /api/v1/notifications/health`
2. Verify the target display ID is valid (e.g., `:0`, `0`, or a specific display number)
3. Confirm the request body includes both `display` and `summary` (required fields)

**If dismiss returns empty or unexpected results:**
1. Retrieve current notifications first to confirm IDs exist
2. Ensure `notificationIds` is an array of strings, not a single string
3. Check that notifications haven't already been dismissed

**If stream disconnects:**
1. Reconnect to the WebSocket endpoint
2. The `displays` query parameter is required on every connection
3. Implement client-side reconnection logic with exponential backoff

### Performance Considerations

- The `stream` endpoint maintains a persistent WebSocket connection per client. Limit concurrent streams per display to avoid resource exhaustion.
- The `notify` endpoint triggers `notify-send` synchronously. High-frequency notification bursts may queue; batch where possible.
- The `metrics` endpoint is lightweight and safe for frequent polling (e.g., 15-second Prometheus scrape interval).
- Icon files are served directly; use HTTP caching headers on the client side to avoid redundant fetches.

---

## Quick Reference

### Endpoints

| Method | Path | Purpose |
|--------|------|---------|
| `POST` | `/api/v1/notifications/notify` | Trigger a desktop notification |
| `GET` | `/api/v1/notifications/{display}` | Get notifications for a display |
| `POST` | `/api/v1/notifications/dismiss` | Dismiss specific notifications |
| `DELETE` | `/api/v1/notifications/dismiss` | Clear all dismissed state |
| `GET` | `/api/v1/notifications/stream` | WebSocket real-time stream |
| `GET` | `/api/v1/notifications/icons/{iconId}` | Serve notification icon image |
| `GET` | `/api/v1/notifications/health` | Health check (unauthenticated) |
| `GET` | `/api/v1/notifications/metrics` | Prometheus metrics |

### Required Parameters

| Endpoint | Required Fields |
|----------|----------------|
| `POST /notify` | `display` (string), `summary` (string) |
| `POST /dismiss` | `notificationIds` (array of strings) |
| `GET /{display}` | `display` path param (string: ID, comma-list, or "all") |
| `GET /stream` | `displays` query param (string) |
| `GET /icons/{iconId}` | `iconId` path param (string) |

### Typical Response Formats

**Health check** returns a 9-field JSON object with HTTP 200.

**Metrics** returns Prometheus text exposition format (`text/plain`).

**Notifications** returns a JSON array of notification objects.

**Icons** returns a binary image file.

**Notify** returns a JSON confirmation object.

**Dismiss (POST)** returns a JSON confirmation object.

**Dismiss (DELETE)** returns a JSON confirmation object.