REST API reference
Base URL: //api.pushnotificationservice.com/v1. All responses are JSON.
The API is versioned in the path. A machine-readable
OpenAPI 3.1 description of everything on this page
is also available.
Authentication
Create an API key in the dashboard under API keys. The key is shown once. Store it securely. Authenticate with a bearer token:
Authorization: Bearer pns_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Keys are pns_ followed by a 12-character prefix and a 40-character secret.
Keys are scoped to your account: you can only act on sites your account owns. Requests are
rate-limited to 120/min per key. Unauthenticated requests get 401.
Send a notification
POST /v1/sites/{siteId}/notifications
| Field | Type | Notes |
|---|---|---|
title | string | Required. Max 120 chars. |
body | string | Required. Max 500 chars. |
url | string | Click destination. Tracked via signed redirect. |
icon, image | string | Optional HTTPS image URLs. |
tags | string[] | Target subscribers with any of these tags (max 10). Mutually exclusive with subscribers. |
subscribers | string[] | Target specific subscriber IDs (max 1000). |
scheduled_for | ISO 8601 | Future time to send. Omit to send immediately. |
Omitting both tags and subscribers targets all active subscribers.
curl -X POST \
//api.pushnotificationservice.com/v1/sites/SITE_ID/notifications \
-H "Authorization: Bearer pns_live_..." \
-H "Content-Type: application/json" \
-d '{
"title": "Flash sale",
"body": "30% off for the next hour.",
"url": "https://example.com/sale",
"tags": ["offers"]
}'
{
"data": {
"id": "9b1f...",
"title": "Flash sale",
"status": "queued",
"counts": { "targeted": 0, "delivered": 0, "displayed": 0, "failed": 0, "clicked": 0 }
}
}
Delivery is asynchronous: the send returns queued (or scheduled)
and the counts fill in as workers deliver. Poll the campaign endpoint below for live counts.
delivered counts push-service acceptance; displayed counts
notifications the service worker actually showed on a device.
Other notification endpoints
| Method & path | Description |
|---|---|
GET /v1/sites/{siteId}/notifications | List campaigns (newest first, 25 per page, ?page=). |
GET /v1/sites/{siteId}/notifications/{id} | Fetch one campaign with live delivery counts. |
Subscribers
| Method & path | Description |
|---|---|
GET /v1/sites/{siteId}/subscribers | List subscribers. Filters: ?status=active, ?tag=news, ?per_page= (default 50, max 200). |
POST /v1/sites/{siteId}/subscribers/{id}/tags | Add/remove tags: {"add":["vip"],"remove":["trial"]} (max 20 each). |
DELETE /v1/sites/{siteId}/subscribers/{id} | Unsubscribe a subscriber. |
GET /v1/sites/{siteId}/tags | List tags with subscriber counts. |
Signed links in notifications
Every delivered notification carries signed URLs generated per campaign or subscriber. You never build these yourself, but you'll see them in traffic and logs:
| Method & path | Description |
|---|---|
GET /c/{campaignId} | Click-through redirect. Counts the click, then 302s to the campaign's url (or the site's domain). No cookies, no per-user history. |
GET /d/{campaignId} | Display receipt pinged by the service worker after it shows the notification. Campaign-scoped, carries no subscriber identity. |
GET /unsubscribe/{subscriberId} | One-click unsubscribe page. Works without JavaScript or CORS. |
All of them verify a URL signature and reject tampered links with 403. The
browser-facing subscription endpoints used by the SDK snippet
(/v1/sites/{siteId}/config, /v1/sites/{siteId}/subscriptions…)
are internal to the snippet and documented on the
snippet page.
Errors & limits
| Status | Meaning |
|---|---|
401 | Missing or invalid API key. |
403 | Account suspended. |
404 | Site or resource not found, or not owned by your account. |
422 | Validation error (see errors in the body). |
429 | Rate limit hit, or monthly push quota exceeded. |