Skip to content

Notifications

A notification is a fire-and-forget message that pops up in the notification center of every receiving device. Nothing is saved on the phone: once dismissed, no record remains. For anything the recipient should be able to find later, or that carries more than one input, send a task instead.

A Simplepush notification banner on iOS, expanded to show Yes and No choice buttons

A notification carries a title, content, at most one media attachment (an image or an audio clip), and at most one input: a text input, a choice input, or a set of action buttons (for example Accept and Deny). iOS shows up to four buttons, Android three.

API endpoint

POST /v1/notifications, authenticated with API-Token (personal) or Api-Key (org). See the cURL guide for header-based sends; POST /v1/notifications/json is the JSON-body twin.

Delivery modes: independent (default) and shared

When a notification goes to more than one recipient, each recipient gets their own instance by default. Every instance has its own id and its own reply, tied together in a group: the send returns a grpntf_ group id plus one ntf_ id per recipient.

Shared mode is the alternative: one notification every recipient sees and answers together, where the first reply completes it for everyone. Use it when you want a single answer from whoever gets there first, rather than one per person.

  • SDKs: pass shared=True (Python) / shared: true (TypeScript). The default returns a group handle; shared returns a single notification handle.
  • CLI: add --shared to sp notify.
  • curl: add a Shared: true header. The X-Notification-Id response header is the grpntf_ group id by default, or the ntf_ id in shared mode.

Send to your own devices

Omit the topic (and any org target) and the notification goes to every device on your own account. It is encrypted automatically when your client has a personal password configured, and sent plaintext otherwise.

bash
sp notify --title "Reminder" --content "Standup in 5 minutes"
python
from simplepush import Client

client = Client(api_token="YOUR_API_TOKEN", passwords="personal-secret")
client.send_notification(title="Reminder", content="Standup in 5 minutes")
typescript
import { Client } from "@simplepush/sdk";

const client = new Client({ apiToken: "YOUR_API_TOKEN", passwords: "personal-secret" });
await client.sendNotification({ title: "Reminder", content: "Standup in 5 minutes" });
bash
curl -X POST https://api.simplepu.sh/v1/notifications \
  -H "API-Token: $SP_API_TOKEN" \
  -H "Title: Reminder" \
  -H "Content: Standup in 5 minutes"

Media: images and audio

A notification can show one media item in the native notification, alongside the text and any input.

  • Images (image/jpeg, image/png, image/gif) render on iOS and Android.
  • Audio (AIFF, WAV, MP3, M4A) plays inline on iOS only; Android ignores audio.

Provide media either as a URL (the recipient's device fetches it) or, in the SDKs, as a local file that is uploaded (and encrypted when the send is encrypted). Uploads follow the iOS attachment caps: images up to 10 MB, audio up to 5 MB. image and audio are mutually exclusive.

bash
# The CLI carries media as URLs; file uploads are SDK-only.
sp notify -t alerts --title "New chart" --content "Nightly report" \
  --image https://example.com/chart.png
python
from simplepush import Client

client = Client(api_token="YOUR_API_TOKEN")

# Image as a URL, fetched by the recipient's device
client.send_notification(topic="alerts", title="New chart", content="Nightly report",
                         image="https://example.com/chart.png")

# Image uploaded from disk
client.send_notification(topic="alerts", title="Signed", content="Contract attached",
                         image="./chart.png")
typescript
import { Client } from "@simplepush/sdk";

const client = new Client({ apiToken: "YOUR_API_TOKEN" });

await client.sendNotification({
  topic: "alerts", title: "New chart", content: "Nightly report",
  image: "https://example.com/chart.png",
});

// Uploaded file: { filename, data, contentType? }
await client.sendNotification({
  topic: "alerts", title: "Signed", content: "Contract attached",
  image: { filename: "chart.png", data: bytes },
});

The header-based curl endpoint does not carry media; use an SDK or sp notify.

Critical alerts (iOS)

A critical alert bypasses the silent switch and Do Not Disturb on iOS, so it plays a sound even on a muted phone. On Android the flag has no effect and the notification is delivered normally.

python
client.send_notification(topic="oncall", title="Database down",
                         content="Primary is unreachable", critical=True)
typescript
await client.sendNotification({
  topic: "oncall", title: "Database down",
  content: "Primary is unreachable", critical: true,
});
bash
curl -X POST https://api.simplepu.sh/v1/notifications \
  -H "API-Token: $SP_API_TOKEN" \
  -H "Topic: oncall" \
  -H "Title: Database down" \
  -H "Content: Primary is unreachable" \
  -H "Critical: true"

sp notify has no critical flag; send critical alerts with curl or an SDK.

Offline delivery on iOS

Notifications sent to an iOS device that is offline are not re-delivered when it comes back online. If the recipient must see the message, send a task: tasks persist in the app until handled.

Getting the reply

A notification's reply arrives like any other answer. Each way of sending has a matching way of collecting it.

bash
# --format json prints a `sent` line; sp collect turns it into the answer
sp notify -t deploys --title "Restart the gateway?" -c "Yes,No" --format json \
  | sp collect
python
from simplepush import Client, NotificationChoiceInput

client = Client(api_token="YOUR_API_TOKEN")

group = client.send_notification(topic="deploys", title="Restart the gateway?",
                                 input=NotificationChoiceInput(options=["Yes", "No"]))

async def main():
    async for g in group.inputs(timeout=300):
        print(g.recipient, g.item.reply)
typescript
import { Client } from "@simplepush/sdk";

const client = new Client({ apiToken: "YOUR_API_TOKEN" });

const group = await client.sendNotification({
  topic: "deploys", title: "Restart the gateway?",
  input: { type: "choice", options: ["Yes", "No"] },
});

for await (const { recipient, item } of group.inputs({ idleMs: 300_000 })) {
  console.log(recipient, item.reply);
}
bash
# Wait: true holds the request open until the first recipient answers
curl -s -X POST https://api.simplepu.sh/v1/notifications \
  -H "API-Token: $SP_API_TOKEN" \
  -H "Topic: deploys" \
  -H "Title: Restart the gateway?" \
  -H "Choice-Input: Yes,No" \
  -H "Wait: true" | tr -d '\n'

sp collect prints one JSON line per answer and a final end line, then exits once every recipient has answered:

json
{"type":"sent","groupId":"grpntf_...","members":[{"notificationId":"ntf_...","recipient":{"publicId":"usr_...","name":"Alice"}}]}
{"type":"completed","groupId":"grpntf_...","notificationId":"ntf_...","recipient":{"name":"Alice"},"reply":{"type":"choice","selectedIndex":0,"selectedValue":"Yes"}}
{"type":"end","reason":"complete","counts":{"completed":1},"members":{"total":1,"completed":1,"deleted":0,"pending":0}}

In the SDKs a notification handle has a single stream: inputs() yields one completion carrying the reply, then ends. Use group.sole.inputs(...) when there is exactly one recipient. To watch answers outside the sending process, use sp events or client.events(). See Receiving Data for the full picture.