# Send push notifications from your server

**Summary:** Dispatch a push notification from your CMS or server with an M2M token, use topics as an audience filter, and read campaigns and delivery stats back from the Subrite API.

- Space: [Developers](https://www.subrite.no/developers)
- Source: https://www.subrite.no/developers/push-notifications-server
- Updated: 2026-09-23
- Markdown index: https://www.subrite.no/developers/llms.txt

To trigger dispatch of a push notification to all subscribers, some code needs to run when content authors publish a new article or other content item. This is typically implemented with a webhook, but will vary between CMSes and depend on the availability of development resources and access to server code.

<a id="step-1-dispatch-a-push-notification"></a>
## Step 1: Dispatch a push notification

To initiate a dispatch of a push notification, notify the Subrite API. The body below reaches every subscribed device for your tenant. To send to only a part of your audience, you can add a topic filter. Read the section "Topics are an audience filter" below before you do.

- **Method:** `POST`
- **Route:** `{baseUrl}/api/v1/app-push/pushcontent`
- **Authorization:** Bearer token (the M2M token from [Basic setup, step 5](https://www.subrite.no/developers/push-notifications-setup?md=true))
- **Body:** see the example below

<a id="example-body"></a>
### Example body

```javascript
{
  "title": "Subrite",
  "body": "Breaking! Subrite has won the Platform of the year Award",
  "data": {
    // insert the data you need to open the app in the preferred state here
    // it will be delivered as part of the payload
  }
}
```

<a id="topics-are-an-audience-filter"></a>
### Topics are an audience filter

Two optional fields let you send to a part of your audience instead of all of it:

- `topic`: a single string, for example `"news"`
- `topics`: an array of strings, for example `["news", "sport"]`

They do the same thing, and if you send both, `topic` wins and `topics` is ignored. While topic filtering is on (the default), the values are matched against the topics each device subscribed with in [App integration, step 3](https://www.subrite.no/developers/push-notifications-app?md=true), and only devices subscribed to at least one of them are notified.

They are an audience filter, **not** metadata attached to the message. Nothing you put here reaches the app. Anything the app needs (article ID, tags, categories, deep link) belongs in `data`.

- Omit the field, or send an empty array, to reach all subscribers.
- Only send values your app actually subscribes devices to. The field is not validated against your topic list: an unknown value is accepted, matches nobody, and the campaign is stored with status `skipped`. No notification is sent, and `POST /pushcontent` still answers `201`.
- A device that subscribed without topics, or with an empty `topics` array, matches no filter at all. It is reached only by a send that has no topic filter.
- Never forward article tags, categories or keywords from your CMS into this field. That is the most common way to end up with a campaign that silently reaches no one.

<a id="example-body-with-a-topic-filter"></a>
### Example body with a topic filter

```javascript
{
  "title": "Subrite",
  "body": "Breaking! Subrite has won the Platform of the year Award",
  "data": {
    // insert the data you need to open the app in the preferred state here
  },
  "topics": ["news"]
}
```

If you never want topic filtering, a tenant administrator can turn on the **Discard topics** setting in the admin dashboard, under **Account settings**, **Settings**, **Communications**, **Push notifications** (see [Basic setup](https://www.subrite.no/developers/push-notifications-setup?md=true)). Every send then goes to all subscribed devices, whatever `topic` or `topics` contains.

After dispatching, check the outcome with the endpoints in step 2. Read the campaign `status` with "Get a single campaign". A status of `skipped` means the send matched no devices at all, which for a filtered send usually means nothing is subscribed to those topics. Then use "Get campaign delivery stats" to see how many notifications were actually queued and sent.

<a id="step-2-read-campaigns-and-delivery-stats"></a>
## Step 2: Read campaigns and delivery stats

Once a campaign has been dispatched, you can read it back from the Subrite API. All three endpoints below accept the same M2M token as step 1, and require the **Push Content** section with **Read** selected (`push-content:read`) on the M2M client.

The endpoints only return campaigns belonging to the tenant the M2M token was issued for.

<a id="list-campaigns"></a>
### List campaigns

- **Method:** `GET`
- **Route:** `{baseUrl}/api/v1/app-push/pushcampaign`
- **Authorization:** `Bearer <M2M token>`
- **Query parameters:** `page` (default `1`), `take` (page size, default `10`, max `50`), `orderDirection` (`ASC` or `DESC` by creation date, default `DESC`)

<a id="example-response"></a>
#### Example response

```javascript
{
  "items": [
    {
      "id": 1234,
      "createdAt": "2026-08-12T09:15:00.000Z",
      "updatedAt": "2026-08-12T09:16:04.000Z",
      "title": "Breaking news",
      "body": "A new article has just been published",
      "data": {
        // the payload delivered to the app, as supplied when dispatching
      },
      "tenantId": 1,
      "status": "dispatched",
      "topics": ["news"],
      "segmentId": null,
      "scheduledAt": null
    }
  ],
  "meta": {
    "page": 1,
    "take": 10,
    "count": 1,
    "pageCount": 1,
    "hasPreviousPage": false,
    "hasNextPage": false
  }
}
```

<a id="get-a-single-campaign"></a>
### Get a single campaign

- **Method:** `GET`
- **Route:** `{baseUrl}/api/v1/app-push/pushcampaign/{id}`
- **Authorization:** `Bearer <M2M token>`

The response is a single campaign object, identical to one entry of the `items` array above.

<a id="get-campaign-delivery-stats"></a>
### Get campaign delivery stats

- **Method:** `GET`
- **Route:** `{baseUrl}/api/v1/app-push/pushcampaign/{id}/stats`
- **Authorization:** `Bearer <M2M token>`

<a id="example-response-2"></a>
#### Example response

```javascript
{
  "total": 1500,   // notifications queued for this campaign
  "sending": 0,    // still in flight
  "sent": 1476,    // delivered to the push provider
  "failed": 24,    // permanently failed, e.g. expired device tokens
  "opened": 412    // opened in the app, as reported by the open tracking call
}
```

And you're done. Good luck!
