Skip to documentation
Notify Docs

Delivery and reliability

Understand what a send proves, how retries behave, and how device availability affects an update. Use these rules when turning the guides into unattended workflows.

Acceptance is not a receipt

json
{
  "ok": true,
  "eventId": "evt_example_pickup",
  "delivered": 1
}

Save eventId if you will inspect an answer or request withdrawal. delivered counts push requests accepted across the configured providers: Expo for iOS and FCM for native Android. It is not a delivery receipt, proof of display, read state, or a human answer. Notification detail responses now include pushReceipts for the reader’s iOS devices: pending, accepted by Apple, error with a safe provider code, or unknown when no receipt arrives within 24 hours. Checks begin after one minute and retry every five minutes; diagnostics are retained for 30 days. Older sends have no receipts. A receipt still cannot prove on-screen display. DeviceNotRegistered retires only the matching Expo token; a newer registration and Live Activity credentials are preserved.

Interactive service questions persist their original push targets with the question before sending. While retries remain, HTTP 202 includes queued: N alongside the persisted delivered count. Repeat the identical payload and idempotency key to read that same event’s current counts. Retries preserve the interaction and response credential, recheck authorization and expiry, and never add newly registered devices. Exhausted delivery with no provider acceptance returns 502. A provider request can repeat after an ambiguous network result; deduplicate by interaction ID.

With no eligible devices, the event can still be created with delivered: 0 and an explanatory message. Decide explicitly whether your workflow requires an available device. For questions, proceed only after an approved or otherwise appropriate terminal answer.

Retry without duplicate sends

Give each logical send a stable Idempotency-Key when a network retry is possible. The header accepts 1–200 characters, and its namespace belongs to the service. Reuse a key only for an identical payload.

  • An already completed matching request returns its original event and idempotent: true.
  • A matching request still being processed returns 202 Accepted; wait before checking again.
  • A key associated with a different payload returns 409 Conflict.
  • An empty key or a key longer than 200 characters returns 400.

Choose recipients

Omit targeting to use the account’s active supported iOS and native Android devices, ordered by most recent activity. Personal accounts support unlimited devices.

Set deviceIds to 1–50 stable IDs from the dashboard when only selected devices should receive the update. A foreign or unknown selection returns 400 Invalid device selection. Owned devices that are inactive or unsupported are skipped. Workplace group targeting uses a separate permission check.

json
{
  "body": "Package PKG-42 needs a pickup decision.",
  "deviceIds": ["dev_your_iphone_id"]
}

Targeting has no paid personal-plan gate. Availability still depends on the device’s registration, platform capabilities, and operating-system notification settings.

Remove an obsolete alert

If a package alert becomes obsolete, use its saved eventId to request removal from the service owner’s active supported devices. Withdrawal also cancels an associated pending question. To stop answers without requesting alert removal, use question cancellation.

bash
curl -X POST \
  'https://notify.belweave.ai/hooks/whk_your_token/events/evt_example_pickup/withdraw'
json
{
  "ok": true,
  "eventId": "evt_example_pickup",
  "status": "withdrawn",
  "accepted": 1
}
accepted counts provider acceptance of the removal command, not confirmed device removal. Background work may run late or never run, including after an iOS force-quit. Use the webhook belonging to the original event. Withdrawal commands target the owner’s devices, not every original family/group recipient; those recipients may retain their alert. A settled repeat returns the status with accepted: 0 and idempotent: true, without another command.

Rate limits and usage

Plan limits
LimitPersonal · freeFamily
Requests per minute, per service300300
Requests per minute, per account1,5001,500
Notifications per monthUnlimitedUnlimited
Active devicesUnlimitedUnlimited

Hosted defaults are shown above. Notifications, questions, and activity operations share rolling 60-second counters. A per-minute rejection returns 429, Retry-After: 60, and retryAfterSeconds. There is no monthly notification cap.

Personal and Family include unlimited notifications and devices for free. Per-minute rate limits still apply to notifications, questions, and activity operations. Teams is TBD and is not currently purchasable.

Expiry and stale content

Set expiresInSeconds to the longest useful lifetime for the task: 60–28,800 seconds, with 28,800 as the default. After the deadline, state becomes expired and further updates are refused.

staleAfterSeconds expresses how long an update should be trusted: 0–28,800 seconds, default 14,400. On iOS, stale content remains visible and updateable but is marked as potentially outdated. Each update moves the stale deadline forward without exceeding expiry; omitting this field retains the previous stale window.

Activity slots and replacement

Notify allows up to five concurrent activities per target device, including interactive activities. Distinct keys can coexist. Starting the same active key for the same service or token without replacement returns this 409 response:

json
{
  "ok": false,
  "error": "A Live Activity with this key is still active",
  "code": "ACTIVE_ACTIVITY_CONFLICT",
  "activityId": "act_example_pickup"
}

Use code to identify a key conflict and update the existing task. Device capacity instead returns 422, code: ACTIVITY_CAPACITY_EXCEEDED, and limit: 5; end one of your running tasks before starting another. Interactive Live Activity asks use the same rejection: if any target is full, the whole mixed-target request rolls back without a queued receipt or push. Retry the same payload and idempotency key after a slot is released. Five is Notify’s admission policy, not a hardware specification.

A deliberate replacement uses the same key with replace: true. It ends only the matching activity owned by this service or token before the new start. replaced reports how many were displaced, or zero if no replacement was needed. Device removal remains subject to provider and OS behavior.

Replacement preserves other keys and other requesters’ activities. The matching activity is ended everywhere, including outside the new target set, so its alias can be reused. A stable key plus replacement suits repeated runs of the same task.

Activity delivery behavior

On iOS, starts include an alert; update/end pushes are silent. Routine progress updates use APNs priority 5. Starts, endings, and interactive state use priority 10. Priority does not grant sound, haptics, or display. Android updates modify the same ongoing progress notification; OS settings still control presentation.

Send updates at meaningful transitions. Activity operations share notification rate limits. There is no monthly allowance to exhaust.

Read activity.readiness to distinguish ready devices, queued starts and accepted starts awaiting an update token. After 90 seconds without registration, update_token_delayed is a diagnostic warning, not proof of Apple throttling; the activity still accepts late registration until its deadline. Open Notify and check Live Activity settings if it persists. Reuse one activity for each continuing task and send meaningful progress changes. Unlimited monthly usage does not mean unlimited delivery frequency.