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 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
--sharedtosp notify. - curl: add a
Shared: trueheader. TheX-Notification-Idresponse header is thegrpntf_group id by default, or thentf_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.
sp notify --title "Reminder" --content "Standup in 5 minutes"from simplepush import Client
client = Client(api_token="YOUR_API_TOKEN", passwords="personal-secret")
client.send_notification(title="Reminder", content="Standup in 5 minutes")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" });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.
# 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.pngfrom 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")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.
client.send_notification(topic="oncall", title="Database down",
content="Primary is unreachable", critical=True)await client.sendNotification({
topic: "oncall", title: "Database down",
content: "Primary is unreachable", critical: true,
});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.
# --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 collectfrom 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)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);
}# 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:
{"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.