Notifications API
Send a notification to a user's inbox, with optional APNs push delivery to their devices.
Create Notification
POST
/notificationsCreate an in-app notification and optionally push it to all user devices. Requires an active subscription.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Yes | Notification title |
body | string | Yes | Notification body text |
subtitle | string | No | Optional subtitle |
level | string | No | passive, active (default), or time-sensitive. Controls iOS interruption level. |
thread_id | string | No | Groups notifications in Notification Center |
collapse_id | string | No | APNs deduplication key (max 64 chars). Not stored or returned in responses. |
source | string | No | Source identifier (e.g. integration name) |
source_display_name | string | No | Human-readable source name shown in notification settings and inbox grouping |
url | string | No | Action URL (max 2048 chars, must start with http:// or https://) |
media | object | No | Rich media attachment. url (HTTPS, max 2048 chars) plus type (image, video, or audio). iOS renders inline. Apple size caps: image 10 MB, audio 5 MB, video 50 MB. |
actions | array | No | Up to 10 dynamic action buttons. Each: id (string, max 64), title (string, max 64), optional url, foreground (bool), destructive (bool), authentication_required (bool), icon (SF Symbol name). |
icon_url | string | No | Per-notification source avatar, shown as the Communication Notification avatar on iOS. Accepts http or https (max 2048 chars, recommended ≤256×256 and ≤100 KB; the iOS extension rejects responses larger than 512 KB). |
metadata | object | No | Key-value string pairs (max 20 keys, key max 64 chars, value max 4096 chars) |
activity_slug | string | No | Optional link to an existing activity. An unknown slug is rejected with 422 before the notification is persisted. |
push | boolean | No | If true (default), send APNs rich alert to all user devices. Set false to store in the inbox only. |
Info
By default, every notification is delivered as an APNs push to the user's devices. Set push: false to store the notification in the inbox without triggering an alert.
Example
curl -X POST https://api.pushward.app/notifications \
-H "Authorization: Bearer hlk_YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Deploy Complete",
"body": "Successfully deployed to production",
"source": "github-actions",
"source_display_name": "GitHub Actions",
"level": "active"
}'Response (201):
{
"id": 42,
"title": "Deploy Complete",
"subtitle": "",
"body": "Successfully deployed to production",
"thread_id": "",
"level": "active",
"source": "github-actions",
"source_display_name": "GitHub Actions",
"url": "",
"media_url": "https://example.com/img.png",
"media_type": "image",
"actions": [
{ "id": "rerun", "title": "Re-run", "foreground": true }
],
"icon_url": "",
"metadata": {},
"activity_slug": "",
"pushed": true,
"created_at": "2026-04-24T21:00:00Z",
"delivery": "all",
"reason": ""
}On POST /notifications only, the response also includes two read-only fields
describing the APNs fan-out outcome (omitted from GET responses):
| Field | Values | Meaning |
|---|---|---|
delivery | all, partial, none | Whether every, some, or no devices accepted the push. |
reason | no_apns_token, apns_rejected, push_disabled | Failure mode when delivery is not all. Empty on success; omitted entirely when push: false. |
Error Responses
Errors follow RFC 9457 Problem Details with Content-Type: application/problem+json. See the canonical Errors section for the body shape and known code values.
| Status | Meaning |
|---|---|
400 | Malformed JSON or wrong shape (semantic validation now uses 422) |
401 | Missing or invalid token |
403 | Insufficient permissions or active subscription required (code: subscription.required) |
422 | Unknown activity_slug, or other semantic validation failure |
429 | Rate limit exceeded — pairs with Retry-After and retry_after_ms |
500 | Internal server error |