Skip to content

Send a notification

POST
/v1/messages
curl --request POST \
--url https://api.pocketbell.dev/v1/messages \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'idempotency-key: backup-03-2026-09-27' \
--data '{ "message": "backup-03 failed with exit code 1", "title": "Backup failed", "markdown": true, "priority": "silent", "sound": "default", "url": "https://status.example.com", "url_title": "View Server", "thread": "backups-production", "tags": [ "production", "backup" ], "ttl": 3600, "replace": "deploy-4f2a91c", "actions": [ { "label": "Open Dashboard", "url": "https://status.example.com" } ], "deliver_at": "2026-09-28T02:00:00Z", "attachment": "https://example.com/graph.png", "callback": "https://example.com/pocketbell/callback", "retry": 300, "expire": 3600, "options": [ "Approve", "Deny" ], "progress": 0.6 }'

Queues a notification for every device the Source delivers to. Send an Idempotency-Key header to make retries safe: a repeat with the same key returns the first message instead of sending another.

idempotency-key

Any unique string, such as a UUID.

string
>= 1 characters <= 255 characters
Example
backup-03-2026-09-27
Media typeapplication/json
object
message
required

The notification’s text. Longer text is kept whole in the inbox and shortened in the push.

string
>= 1 characters <= 4096 characters
Example
backup-03 failed with exit code 1
title

Defaults to the Source’s name.

string
>= 1 characters <= 250 characters
Example
Backup failed
markdown

True: the message uses markdown. The apps show bold, italic, code, links and lists of lines starting with - or *; the notification itself shows the plain text. Off by default, so text like snake_case_names arrives as written.

boolean
Example
true
priority

Defaults to the Source’s default priority.

string
Allowed values: silent quiet normal important critical
Example
important
sound

The sound it plays: default is the device’s own, none plays nothing. Defaults to the Source’s sound. A Critical notification always plays the Critical sound, a desk bell, whatever this says.

string
Allowed values: default bell chime ping pulse rise alarm none
Example
chime
url

A link shown with the notification in the apps, titled url_title. Opening it asks first.

string format: uri
<= 2048 characters
Example
https://status.example.com
url_title
string
>= 1 characters <= 100 characters
Example
View Server
thread

Groups related notifications together on the Lock Screen and in Notification Center. Without one, a Source’s notifications are grouped together.

string
>= 1 characters <= 64 characters
Example
backups-production
tags
Array<string>
<= 10 items
Example
[
"production",
"backup"
]
ttl

Seconds, from when it goes out, that Apple keeps trying a device that’s offline before dropping the notification; 0 tries once. Without it, a day. It still reaches the inbox either way.

integer
<= 2419200
Example
3600
replace

A key for this event. A later notification from the same Source with the same key replaces this one on screen and becomes the event’s latest in the inbox; this one stays there as an earlier update, with its time.

string
/^[A-Za-z0-9._:-]{1,64}$/
Example
deploy-4f2a91c
actions

Up to three buttons, on the notification and with it in the apps. Each opens its link, after asking.

Array<object>
<= 3 items
object
label
required
string
>= 1 characters <= 40 characters
url
required
string format: uri
<= 2048 characters
Example
[
{
"label": "Open Dashboard",
"url": "https://status.example.com"
}
]
deliver_at

Send at this time instead of now, up to 30 days ahead.

string format: date-time
Example
2026-09-28T02:00:00Z
attachment

Pro: an image (PNG, JPEG, GIF or HEIC, up to 10 MB) shown with the notification. Pocketbell fetches it from this URL when a device shows it, and keeps nothing: your devices never contact this server, so it never sees their addresses.

string format: uri
<= 2048 characters
Example
https://example.com/graph.png
callback

Pro, for a Critical notification or a question: POSTed when it’s acknowledged or answered, or when it expires first. Signed to the Standard Webhooks spec with the Source’s signing secret.

string format: uri
<= 2048 characters
Example
https://example.com/pocketbell/callback
retry

Critical only: seconds between repeats until acknowledged. Defaults to 300.

integer
>= 30 <= 3600
Example
300
expire

Critical, or a question: seconds after which it stops repeating or waiting for an answer. 60 to 10800; defaults to 3600.

integer
>= 60 <= 10800
Example
3600
options

A question: one to three answers, shown as buttons on the notification and in the apps. The first answer, from any device, is kept, and the sender gets it from the notification’s status or a callback. Put the likely answer first: a banner may show only two.

Array<string>
>= 1 items <= 3 items
Example
[
"Approve",
"Deny"
]
progress

Pro, with replace: 0 to 1. The event shows as a Live Activity on your iPhones, on the Lock Screen and in the Dynamic Island, and each notification with the same replace key updates it. 1, or a notification with the key and no progress, ends it. Updates between the first and the last arrive quietly.

number
<= 1
Example
0.6

Accepted and queued.

Media typeapplication/json
object
id
required
string
status
required

Awaiting_answer for a question; awaiting_acknowledgment for a Critical notification, which repeats until acknowledged; scheduled when deliver_at is ahead.

string
Allowed values: queued scheduled awaiting_acknowledgment awaiting_answer
repeats

Critical only: false when the account already has 10 Critical notifications repeating. This one is delivered once, without repeats, and still waits for acknowledgment until it expires.

boolean
Example
{
"id": "msg_01jz9k6q8r3tfv0x2m4n5p7s8a",
"status": "queued"
}

The request was invalid.

Media typeapplication/json
object
error
required
object
code
required
string
message
required
string
details

Which fields were wrong, for invalid_request.

Array<object>
object
field
required
string
message
required
string
Example
{
"error": {
"code": "invalid_request"
}
}

Missing, malformed or revoked API key.

Media typeapplication/json
object
error
required
object
code
required
string
message
required
string
details

Which fields were wrong, for invalid_request.

Array<object>
object
field
required
string
message
required
string
Example
{
"error": {
"code": "invalid_request"
}
}

A Pro feature on a Free account.

Media typeapplication/json
object
error
required
object
code
required
string
message
required
string
details

Which fields were wrong, for invalid_request.

Array<object>
object
field
required
string
message
required
string
Example
{
"error": {
"code": "invalid_request"
}
}

Rate_limited: too many at once, retry after the Retry-After header’s seconds; monthly_limit_reached: the account’s notifications for the month are used up (a Critical one still goes out); or question_limit: the Free plan’s questions for the month are used up.

Media typeapplication/json
object
error
required
object
code
required
string
message
required
string
details

Which fields were wrong, for invalid_request.

Array<object>
object
field
required
string
message
required
string
Example
{
"error": {
"code": "invalid_request"
}
}