Sending notifications
Send a notification with one request from your server, using the app’s secret key:
POST https://push-<region>.dockhive.io/v1/apps/{app_id}/notificationsAuthorization: Bearer sk_...Content-Type: application/jsonEvery server request uses the secret key as a bearer token, and the app id in the path must be that key’s app. Each app can make up to 50 requests a second.
Who gets it
Section titled “Who gets it”to takes exactly one of these:
to |
Who gets it |
|---|---|
{"users": ["u1", "u2"]} |
Every browser of these users (up to 1000 ids), plus an inbox item for each user |
{"devices": ["dev_..."]} |
These devices (up to 1000) |
{"tags": {"plan": "pro", "lang": "en"}} |
Devices that have all of these tags |
{"all": true} |
Every device of the app |
Users are your own user ids. A browser joins a user with push.setUser() and a user token; tags are set with push.setTags(). Inbox items are only made for users: the listed users, or the users the targeted devices belong to. Devices that have stopped accepting notifications are skipped (see Delivery).
What to send
Section titled “What to send”| Field | Type | Default | Notes |
|---|---|---|---|
to |
object | required | See Who gets it |
title |
string | required | Up to 120 characters |
body |
string | "" |
Up to 1000 characters |
url |
string | the app’s default URL | Opened on click: https://... or a path on your site (/orders/77) |
icon |
string | the app’s default icon | https://... or a path |
image |
string | A large image (https://... or a path), where the browser supports it |
|
badge |
string | A small monochrome icon (Android) | |
tag |
string | Up to 64 characters; a newer notification with the same tag replaces the one on screen | |
data |
object | Any JSON object, passed to the service worker and the inbox | |
actions |
array | Up to 2 buttons, each {"action": "track", "title": "Track", "url": "/orders/77/track"}; action and title up to 64 characters, url optional |
|
require_interaction |
boolean | false |
Keep it on screen until the user acts |
silent |
boolean | false |
No sound or vibration |
ttl |
integer | 86400 |
Seconds to keep trying while the device is offline, 0 to 2419200 (28 days) |
urgency |
string | normal |
very-low, low, normal or high; devices on battery may hold low-urgency notifications |
topic |
string | Up to 32 letters, digits, _ or -; replaces an earlier undelivered notification with the same topic |
|
send_at |
string | now | An RFC 3339 time up to 30 days ahead, e.g. 2026-11-13T09:00:00Z |
inbox |
boolean | the app’s inbox setting | Also create inbox items |
Set the app’s default URL and icon, and whether the inbox is on by default, in the app’s settings.
The 4 KB limit
Section titled “The 4 KB limit”Browsers accept notifications up to 4096 bytes, encrypted. That leaves about 3.9 KB for the title, body, links and data together. A larger notification is refused with 413 payload_too_large; shorten the title, body or data, or put a link in url instead of the content.
Examples
Section titled “Examples”To tagged devices, with a button, tomorrow morning:
curl https://push-global-1.dockhive.io/v1/apps/app_0123456789abcdef/notifications \ -H "Authorization: Bearer $HIVE_PUSH_SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{ "to": {"tags": {"plan": "pro"}}, "title": "Flash sale: 50% off", "body": "Today only.", "url": "/sale", "actions": [{"action": "shop", "title": "Shop now", "url": "/sale"}], "urgency": "high", "send_at": "2026-11-13T09:00:00Z" }'{"id": "ntf_0f1e2d3c4b5a6978", "targeted": 1840, "inbox": 1702, "scheduled_for": "2026-11-13T09:00:00Z"}Inbox only, to one user:
curl https://push-global-1.dockhive.io/v1/apps/app_0123456789abcdef/notifications \ -H "Authorization: Bearer $HIVE_PUSH_SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{"to": {"users": ["user-42"]}, "title": "Welcome aboard", "inbox": true}'A user with no subscribed browsers still gets the inbox item (targeted: 0, inbox: 1).
The response
Section titled “The response”A send answers 202 Accepted with the notification’s id, targeted (the number of browsers it goes to), inbox (the number of inbox items) and, for scheduled sends, scheduled_for. Delivery happens in the background.
Status and counts
Section titled “Status and counts”GET /v1/apps/{app_id}/notifications/{id} returns the notification with its status and counts:
{ "id": "ntf_5b0c9d2e7f1a3c4b", "status": "done", "created_at": "2026-11-12T09:00:00Z", "send_at": null, "completed_at": "2026-11-12T09:00:02Z", "to": {"users": ["user-42"]}, "title": "Your order shipped", "body": "It arrives on Thursday.", "url": "https://shop.example/orders/77", "ttl": 86400, "urgency": "normal", "inbox": true, "counts": {"targeted": 2, "sent": 2, "failed": 0, "expired": 0, "inbox": 1, "delivered": 2, "clicked": 1, "dismissed": 0, "pending": 0}}status |
Meaning |
|---|---|
queued |
Scheduled, or about to go out |
sending |
Being delivered |
done |
Every delivery has finished |
canceled |
Canceled. When it wasn’t you, error says why: quota_exceeded or app_deleted |
delivered, clicked and dismissed are reported by the service worker and count once per device. expired counts deliveries whose ttl ran out before the device came back online.
GET /v1/apps/{app_id}/notifications?limit=20 lists recent notifications, newest first, as {"notifications": [...]} (limit up to 100). Notifications are kept for 30 days.
Cancel a scheduled notification
Section titled “Cancel a scheduled notification”curl -X DELETE https://push-global-1.dockhive.io/v1/apps/app_0123456789abcdef/notifications/ntf_0f1e2d3c4b5a6978 \ -H "Authorization: Bearer $HIVE_PUSH_SECRET_KEY"Answers 204. Only notifications that are still queued can be canceled; others answer 409 not_cancelable.
List devices
Section titled “List devices”GET /v1/apps/{app_id}/devices?user_id=user-42&tag=plan:pro&limit=100&cursor=...
{ "devices": [ {"id": "dev_1a2b3c4d5e6f7a8b", "platform": "web", "user_id": "user-42", "tags": {"plan": "pro"}, "created_at": "2026-11-01T10:00:00Z", "last_seen_at": "2026-11-12T08:59:00Z", "disabled": false, "disabled_reason": ""} ], "next_cursor": "1841"}Filter by user_id and one tag (key:value). Pass next_cursor as cursor for the next page; it’s null on the last one. limit goes up to 100.
Delivery
Section titled “Delivery”- Encrypted end to end. Every message is encrypted for the receiving browser (the web push standard), so the browser’s push service can’t read it.
- Retries. When a browser’s push service is busy or unavailable, Hive Push tries again after 30 seconds, 2 minutes, 10 minutes and 1 hour (or later if the service asks), up to 5 attempts, and never after the notification’s
ttlhas run out. Then it counts asexpired. - Unsubscribed browsers. When a browser’s subscription no longer exists (the user unsubscribed, cleared site data or uninstalled the browser), the device is disabled and skipped from then on. If the same browser subscribes again, it’s turned back on.
- Rejected devices. A device whose deliveries are rejected 5 times in a row is disabled.
Counting sends
Section titled “Counting sends”A send is one browser delivery or one inbox item: a notification to 3 browsers of one user, with the inbox on, counts 4. Scheduled notifications count on the day they go out; if the daily limit has been reached by then, they’re canceled with quota_exceeded. See Limits and regions.
Errors
Section titled “Errors”Errors are JSON with a stable code and a readable error:
{"error": "This send would exceed the daily limit of 50000 sends on your Free plan ...", "code": "quota_exceeded"}The ones you’ll meet when sending:
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_target |
to must have exactly one of users, devices, tags, all; or ids or tags are malformed |
| 400 | invalid_title, invalid_body, invalid_url, invalid_icon, invalid_image, invalid_badge, invalid_tag, invalid_data, invalid_actions |
A content field is missing, too long or malformed |
| 400 | invalid_ttl, invalid_urgency, invalid_topic, invalid_send_at |
A delivery option is out of range |
| 401 | unauthorized / invalid_api_key |
No secret key sent, or the wrong one for this app |
| 403 | quota_exceeded |
The send would go over your daily send limit |
| 409 | not_cancelable |
The notification is already sending or finished |
| 413 | payload_too_large |
The notification doesn’t fit the 4096-byte web push limit |
| 429 | rate_limited |
Too many requests; wait for Retry-After seconds |
Every code is listed in Error codes.