Push notifications

Groups

Subscribe subscribers or devices to a keyed group, read its members, and send to everyone in it with one call.

Subscribe

A member is a subscriber id, which reaches every device registered under it now and later, or a device key, one registered deviceId, for anonymous devices such as browsers. An unknown key creates the group. Subscribing again is a no-op (joined: false), so you can subscribe on every launch.

Not Classic topic groups

Groups are separate from the topic groups of Expo Push (Classic): a different audience and send pipeline, and they don't interact.

POST https://app.nativenotify.com/api/universal/groups/subscribe
{
  "appId": 123,
  "appToken": "yourAppToken",
  "key": "weekly-news",
  "name": "Weekly News",
  "subscriberId": "user_8241"
}
FieldRequiredNotes
appId, appTokenyesYour app's pair.
keyyes1–64 characters: lowercase letters, digits, dashes and underscores, starting with a letter or digit. Aliases: groupKey, group_key, group.
subscriberIdone memberOne subscriber id (max 256 chars). Aliases: subscriber_id, subId, sub_id.
deviceIdone memberOne registered device key (max 200 chars). Alias: device_id.
subscribersone memberBulk subscriber ids, up to 500 per call. Aliases: subscriberIds, subscriber_ids.
devicesone memberBulk device keys, up to 500 per call. Aliases: deviceIds, device_ids.
name, descriptionnoUsed only when this call creates the group (max 120 and 500 chars). name defaults to the key.
createnoDefault true. false answers 404 group_not_found for an unknown key.

Send at least one member; the four member fields can be mixed. More than 500 ids of one kind is 400 too_many_members: the batch is rejected, never truncated. The response is 201:

{
  "ok": true,
  "group": { "id": 17, "key": "weekly-news", "name": "Weekly News", "created": true },
  "results": { "subscribers": [{ "subscriberId": "user_8241", "joined": true }], "devices": [] },
  "counts": { "subscribers": 1, "devices": 0, "resolvableDevices": 2, "prunableDevices": 0 }
}

Unsubscribe

POST https://app.nativenotify.com/api/universal/groups/unsubscribe

Same body as subscribe. It answers 200 with removed per member (false if it wasn't in the group) and the new counts. An unknown key is 404 group_not_found.

Read groups and members

GET https://app.nativenotify.com/api/universal/groups/:appId/:appToken
GET https://app.nativenotify.com/api/universal/groups/:appId/:appToken/:key
GET https://app.nativenotify.com/api/universal/groups/:appId/:appToken/:key/members?take=100&skip=0

The list returns every group, oldest first, each with id, key, name, description, kind (manual or auto), rule, createdBy, createdAt, updatedAt and counts. One group is looked up by key, or by numeric id when no group has that key.

Members returns a page of { memberId, subscriberId, deviceId, source, joinedAt, resolvedDevices } rows with total (the full membership count), take and skip. take defaults to 100 and must be 1 to 200 (400 invalid_take). Page with skip until you've read every row.

CountMeaning
subscribers, devicesMembership rows of each kind.
resolvableDevicesDistinct registered devices with a live token that the memberships reach now, across all environments. A send reaches only those in its own environment.
prunableDevicesDevice memberships whose device isn't registered. Never removed automatically; the dashboard's Prune action removes exactly those rows. Subscriber memberships are never prunable.

Send to a group

A group is one audience of the ordinary send:

{
  "appId": 123,
  "appToken": "yourAppToken",
  "title": "This week's newsletter",
  "message": "New recipes from the bakery and the library's reading list.",
  "audience": { "type": "group", "key": "weekly-news" }
}

Memberships resolve to live tokens in the send's environment (production when omitted). A device reached by both a subscriber and a device membership gets the push once. An unknown or empty group reports devices: 0, so check it.

On Premium, sending to a group answers 403 plan_upgrade_required and nothing is sent. Subscribing, unsubscribing and reading groups work on every plan.

Always-on groups

Every app has three, with nothing to create or turn on.

KeyMembersrule
lapsedNo app open in the last 30 dayslapsed_30d
activeOpened the app in the last 7 daysactive_7d
newFirst app open in the last 7 daysnew_7d

Members are subscriber ids computed live from app opens, so a device registered without a subscriberId is never in one. Activity is a device's last registration, so register on every launch. Every member holds a live token, so the counts are what a send reaches.

They're read-only: you can't subscribe to, edit or delete them, and their keys are reserved. Send to them like any group, with { "type": "group", "key": "lapsed" }, or schedule against them. In counts, subscribers and resolvableDevices hold the member count and devices is 0. Their members page shows source: "auto" and memberId: null, ordered by subscriber id.

Errors

StatusCodeMeaning
400invalid_body, missing_field, invalid_fieldBad body, no key, a bad appId, or a name or description that's too long.
400invalid_group_keyThe key breaks the rules above.
400invalid_subscriber_id, invalid_device_idAn empty or too-long member id, or a member list that isn't an array.
400no_members, too_many_membersNo member, or more than 500 of one kind.
400auto_groupA subscribe or unsubscribe named an always-on group.
400invalid_take, invalid_skipBad member-page parameters.
400bad_app_id, bad_group_idDashboard and MCP calls: a malformed id.
404app_not_found, group_not_foundThe app isn't accessible to this caller, or the key doesn't exist.
409key_taken, key_reserved, system_groupDashboard and MCP calls: the key is used or reserved, or an edit or delete targeted an always-on group.
502subscribe_failed, unsubscribe_failed, write_failedThe change couldn't be saved. Retry.

The dashboard's Groups tab lists every group, always-on ones first, and creates, edits and prunes manual groups. The MCP server has the same tools: list_universal_groups, subscribe_to_universal_group, send_notification_to_universal_group and more.