pingd docs

Publishing messages

Send one JSON POST to a topic.

Endpoint

POST/topics/:name/messages

Example

For an open topic, publish with the fields you need.

curl -s http://localhost:7685/topics/alerts.disk/messages \
  -H 'Content-Type: application/json' \
  -d '{
    "priority": 3,
    "tags": ["prod", "storage"],
    "payload": {
      "title": "Disk full",
      "body": "Volume /data is 95% full"
    }
  }'
pingd-cli messages publish \
  --topic alerts.disk \
  --title "Disk full" \
  --body "Volume /data is 95% full" \
  --priority 3 \
  --tags prod,storage
import requests

requests.post(
    "http://localhost:7685/topics/alerts.disk/messages",
    json={
        "priority": 3,
        "tags": ["prod", "storage"],
        "payload": {
            "title": "Disk full",
            "body": "Volume /data is 95% full",
        },
    },
)

Request body

FieldTypeRequiredNotes
payload.bodystringyesMain message text
payload.titlestringnoShort headline
payload.subtitlestringnoSecondary line (used by APNS)
priority1–3noDefault 2. 1 low, 3 urgent
tagsstring[]noUp to 10 tags, 1–30 chars each, [a-zA-Z0-9_-]
ttlsecondsnoTime-to-live, max 30 days

Auth

Pick the one that matches the topic and caller:

ro is read-only. It works for listing, polling, SSE, and subscriptions, not publishing. See Permissions for the full access matrix.

Priority

Priority 3 maps to APNS interrupting notifications and Web Push urgency hints. Most clients, including the dashboard, sort by priority.

Tags

Tags are short labels for filtering and display. Up to 10 per message, each 1–30 characters, alphanumeric plus dash and underscore. They show up in the dashboard, in the SSE stream, and in the data payload sent to push clients.

TTL and expiry

If ttl is set, pingd records expiresAt = now + ttl. Expired messages are filtered lazily on read, and the dispatch worker marks deliveries that haven't been sent yet as expired instead of pushing them. Use this when a message should be ignored after a specific time.

Limit: max ttl is 30 days (2,592,000 seconds). Without a ttl, messages never expire.

Delivery semantics

Publishing stores the message and creates pending delivery rows for active subscriptions. A background worker picks those rows up, sends through APNS or Web Push, and marks each delivery delivered, failed, or expired.

SSE listeners receive messages live on /topics/:name/stream.

Real-time push dispatch is planned for upcoming versions.

Inspecting deliveries

Once a message exists, you can list its delivery rows:

GET/messages/:id/deliveries

Each row has status, retryCount, and timestamps. Useful for debugging "why didn't my phone get the push?"

Listing messages

GET/topics/:name/messages

Read access uses the same rules as GET /topics/:name. Expired messages are filtered out of the response.