Skip to content

Communication Style

Set icon_url to render the notification with iOS Communication Notification styling — a round avatar on the side, like an iMessage. Combine with thread_id for grouping in Notification Center.

Notification rendered in iOS Communication style with a source avatar
PushWard app detail view of the Communication-style notification with sender avatar

Source identity

FieldTypeDescription
sourcestringStable internal id — e.g. sonarr, github-actions, home-assistant. Used for filtering and analytics.
source_display_namestringThe pretty version users see — e.g. Sonarr, GitHub Actions. Shown above the title and used to group notifications under that source in iOS settings and the inbox.
icon_urlstringHTTPS avatar (max 2048 chars). Recommended ≤256×256 and ≤100 KB; the iOS extension rejects responses larger than 512 KB to protect its 24 MB memory budget. When set, iOS renders the notification in Communication Notification style — a round avatar floating on the side, like an iMessage.
Info

Communication Notifications were introduced in iOS 15 for messaging apps. PushWard uses the same APIs to give your push notifications a personality — the source avatar floats on the side instead of being embedded as a tiny attachment.

Example: Home Assistant doorbell

Communication-style notification
curl -X POST https://api.pushward.app/notifications \
  -H "Authorization: Bearer hlk_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Front Door",
    "subtitle": "Doorbell pressed · 13:45",
    "body": "Someone is at the front door",
    "level": "time-sensitive",
    "source": "home-assistant",
    "source_display_name": "Home Assistant",
    "thread_id": "ha-doorbell",
    "icon_url": "https://www.home-assistant.io/images/favicon-192x192.png",
    "push": true
  }'

Threading with thread_id

When several notifications belong to the same "conversation," give them all the same thread_id. iOS stacks them as one expandable group in Notification Center — the user sees a single row that opens to reveal the full list. They add up; nothing is overwritten.

Good real-world threads:

  • Every download for one show: thread_id: "sonarr-the-bear"
  • Every message in one chat: thread_id: "chat-42"
  • Every alert from one host: thread_id: "alerts-prod-db-01"
Two Sonarr downloads stack as one group
# First episode
curl -X POST https://api.pushward.app/notifications \
  -H "Authorization: Bearer hlk_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Sonarr",
    "body": "The Bear S03E01 downloaded",
    "thread_id": "sonarr-the-bear",
    "source": "sonarr",
    "source_display_name": "Sonarr",
    "push": true
  }'

# Second episode — appears stacked under the first
curl -X POST https://api.pushward.app/notifications \
  -H "Authorization: Bearer hlk_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Sonarr",
    "body": "The Bear S03E02 downloaded",
    "thread_id": "sonarr-the-bear",
    "source": "sonarr",
    "source_display_name": "Sonarr",
    "push": true
  }'

Replacement with collapse_id

When you only care about the latest value of something, send each update with the same collapse_id. APNs replaces the previous notification on-device — the user always sees one row, and it keeps changing.

Good real-world collapses:

  • CI build progress: collapse_id: "build-482" with body cycling through "running 12%" → "running 84%" → "passed".
  • Live score updates: collapse_id: "match-arsenal-chelsea".
  • Battery / charge percent on a 3D print: collapse_id: "printer-x1c".
Info

collapse_id is a pure APNs delivery hint — PushWard does not store it, and it's not returned in the response. Keep it under 64 characters.

Build status that updates in place
# Build kicks off
curl -X POST https://api.pushward.app/notifications \
  -H "Authorization: Bearer hlk_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Build #482",
    "body": "Status: running (12%)",
    "collapse_id": "build-482",
    "source": "github-actions",
    "source_display_name": "GitHub Actions",
    "push": true
  }'

# Final status — overwrites the previous notification on-device
curl -X POST https://api.pushward.app/notifications \
  -H "Authorization: Bearer hlk_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Build #482",
    "body": "Status: passed (100%)",
    "collapse_id": "build-482",
    "source": "github-actions",
    "source_display_name": "GitHub Actions",
    "push": true
  }'

Combine the two

Use thread_id and collapse_id together to get the best of both: every build groups under one stack in Notification Center, while each individual build only ever shows its latest status.

Group all builds, but keep one row per build
{
  "title": "Build #482",
  "body": "Status: passed (100%)",
  "thread_id": "ci-builds",
  "collapse_id": "build-482",
  "source": "github-actions",
  "source_display_name": "GitHub Actions",
  "push": true
}