Notification API reference
The notification API runs on port 9876 and exposes a single endpoint for sending a notification to a specific member. The agent binds the port on all interfaces, and access is gated by a bearer-token API key.
Authentication
Section titled “Authentication”All requests require a bearer token in the Authorization header:
Authorization: Bearer <api-key>The API key is generated automatically on first startup, stored in the agent’s data volume, and rotatable from the dashboard. See the Notification API overview for details.
Send notification
Section titled “Send notification”POST /v1/notifySend a notification to a single member. The agent encrypts the payload to that member’s identity key (libsodium sealed-box) and relays it to atreoLINK for delivery to their phones and browsers.
Request body
Section titled “Request body”{ "userId": "550e8400-e29b-41d4-a716-446655440000", "userEmail": "alice@example.com", "title": "Backup complete", "body": "Daily backup finished. 4.2 GB synced.", "html": "<p>Daily backup finished. <strong>4.2 GB synced.</strong></p>", "plaintext": "Daily backup finished. 4.2 GB synced.\n\nDuration: 12m 47s.", "contentType": "text/html", "severity": "info", "emailFrom": "backup@home.example.com", "emailSubject": "Daily backup report"}| Field | Type | Required | Description |
|---|---|---|---|
userId | string | one of | Recipient’s atreoLINK user ID. Mutually exclusive with userEmail. |
userEmail | string | one of | Recipient’s atreoLINK account email. Mutually exclusive with userId. |
title | string | yes | Notification title. Shown on the lockscreen and in the inbox. |
body | string | yes | Plain body. The first 200 characters seed the lockscreen excerpt; the inbox falls back to this if no html or plaintext is sent. |
html | string | no | Optional rich HTML body. Rendered in a sandboxed dark-themed view. JavaScript blocked. |
plaintext | string | no | Optional full plaintext body. Rendered with monospace formatting in the inbox. |
contentType | string | no | text/html or text/plain. Tells the inbox which view to default to. Inferred from which fields are sent if omitted. |
severity | string | no | info, warning, or error. Defaults to info. |
emailFrom | string | no | Optional sender metadata, surfaced in the inbox under the title. |
emailSubject | string | no | Optional subject metadata. |
Exactly one of userId / userEmail is required.
Response
Section titled “Response”{ "userId": "550e8400-e29b-41d4-a716-446655440000", "sent": true}401 when the bearer token is missing or wrong, 400 when the request is malformed, 404 when no member matches the address, 413 when the request body exceeds 64 KiB, 500 on relay failure. See Rate limits for over-budget behavior.
The total request body is capped at 64 KiB. The bulk of that budget is typically the html field; trim or skip it if you’re getting 413 responses.
Rate limits
Section titled “Rate limits”Two caps apply to the notify path. A request must pass both, or it’s rejected.
| Scope | Limit | Keyed by |
|---|---|---|
| Per server (all senders on this agent) | 120 / minute | the agent’s server identity |
| Per recipient | 60 / minute | userId (resolved from userEmail if you sent that instead) |
The per-recipient bucket is shared across every server a recipient is linked to and every device they’ve paired — one member can’t receive more than 60 notifications per minute in total, no matter how many servers or apps are sending.
If you hit a limit, back off and retry; the agent doesn’t queue rejected requests.
Encryption
Section titled “Encryption”The agent encrypts the three content fields separately using libsodium sealed-box (crypto_box_seal, X25519 plus XChaCha20-Poly1305) to the recipient’s identity public key. atreoLINK never sees plaintext. See Encryption for the full story.
Retention
Section titled “Retention”atreoLINK’s archive keeps the 100 most recent notifications per recipient.
Examples
Section titled “Examples”curl (same host)
Section titled “curl (same host)”# Send to one member by emailcurl -X POST http://127.0.0.1:9876/v1/notify \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "userEmail": "alice@example.com", "title": "Download complete", "body": "your-file.zip is ready", "severity": "info" }'Docker app integration
Section titled “Docker app integration”Other Docker containers on the same host can reach the API via the host network. The agent runs with network_mode: host, so any other container can hit host.docker.internal:9876 or the host’s LAN IP.
curl -X POST http://host.docker.internal:9876/v1/notify \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"userEmail":"alice@example.com","title":"Disk usage","body":"/data is 92% full","severity":"warning"}'See also
Section titled “See also”- Notification API overview for where the bearer token lives and how to rotate it.
- Built-in SMTP server for an alternative integration that takes regular emails and turns them into notifications.
- Per-device delivery for how phones and browsers register with atreoLINK and why the agent never sees individual devices.
© 2026 atreoLABS. All rights reserved.
WireGuard is a registered trademark of Jason A. Donenfeld.

