Skip to content

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}/notifications
Authorization: Bearer sk_...
Content-Type: application/json

Every 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.

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).

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.

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.

To tagged devices, with a button, tomorrow morning:

Terminal window
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:

Terminal window
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).

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.

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.

Terminal window
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.

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.

  • 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 ttl has run out. Then it counts as expired.
  • 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.

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 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.