In most cases, process of sending a message involves 2 steps:

  1. Preparation, that is calculation of audience and extracting their language preferences.
  2. Creation. That is taking a prepared message and creating schedule job .

When using API rather than Countly Dashboard, (1) and (2) can be done in background, so only request (2) is required. But in any case, (1) and (2) will be performed.

This request performs step (2). It creates message and sets it's status to "Created". It also creates a schedule a job for this message which is run right away for non-timezoned messages. If message is about to be sent in users' timezones, it's scheduled 24 hours prior to sending date to ensure users won't move between timezones.

Note that while most of arguments are cross-platform, there are still some differences between iOS & Android platforms which need to be addressed.

Returned JSON contains full message object. Note that Countly supports sending messages to multiple apps at once and even to multiple platforms at once.

Message status

Countly message status (result.status key) is a bitmap of following statuses:

0 (0)NotCreated
1 (1 << 0)Created - have been created, but haven't been scheduled yet
2 (1 << 1)Scheduled - put into sending queue, will be sent on scheduled date
4 (1 << 2)Sending - being sent right now
8 (1 << 3)Done - done with any processing
16 (1 << 4)Error - some error happened during sending
32 (1 << 5)Success - sending is completed with some success
1024 (1 << 10)Aborted - processing was aborted and won't be repeated due to non-recoverable error (usually credentials error)
2048 (1 << 11)Deleted - message was deleted

Note that statuses above are often combined. For example standard message waiting to be sent within an hour will have status 3 (1 << 0 | 1 << 1). Message which was aborted, but had some successful sends will have status 1081.

Usual way of status transitions is following:
NotCreated => Created => Created | Scheduled => Created | Scheduled | Sending => Created | Done | Success => Created | Done | Success | Deleted

Message result

Result is a JSON object containing results of message sending process:

  "status":	7,
  "total": 23812,
  "processed": 123,
  "sent": 120,
  "error": "Some error string if any",
  "resourceErrors": [{"date": 123912313123, "error": "error string"}],
  "aborts": [{"date": 123912313123, "error": "error string"}],
  • total number represents total number of recipients found for this message;
  • processed number shows number of notifications sent to APN or GCM service;
  • sent number represents number of notifications accepted by APN or GCM (tokens are valid);
  • error appears only when all retries spent or unrecoverable error happened;
  • resourseErrors appear as message is being sent, mostly connectivity issues are listed here;
  • aborts contains unrecoverable errors.

Message localisation

To localise your messages, you need to supply correct messagePerLocale property:

  "default": "Default message",
  "default|p": {
    "0": {
      "f": "our wonderful user",
      "c": true,
      "k": "name"
  "default|t": "Default message title", 
  "default|0|t": "Default button 0 title", 
  "default|0|l": "http://default.button0.link.com", 
  "default|1|t": "Default button 1 title", 
  "default|1|l": "http://default.button1.link.com", 
  "en": "Message in English",
  "en|p": {
    "0": {
      "f": "our wonderful English-speaking user",
      "c": true,
      "k": "name"
  "de": "... in Deutsch",
  "ru": "... на Русском", 
  "ru|t": "Заголовок сообщения на Русском", 
  "en|1|t": "Button 1 title in English"}

Correct messagePerLocale object must have "default" message and a message per each locale you want your message to be localized in.

Note complex keys in messagePerLocale:

  • LOCALE|p stands for message personalisation object.
  • LOCALE|t stands for message title.
  • LOCALE|tp stands for message title personalisation object.
  • LOCALE|0|t stands for button 0 title.
  • LOCALE|0|l stands for button 0 link.
  • LOCALE|1|t stands for button 1 title.
  • LOCALE|1|l stands for button 1 link.

Button attribute consists of locale name followed by separator (vertical line |, not L letter), then button index (0 or 1), separator again, then either t for title or l (this time lower L) for link.

No need to specify all properties for all locales. Just set default locale and override values for locale you have localisation for. For example set all button titles and links in default locale and only override button titles in languages you support.

Locales are 2-character codes from ISO 639-1 taken from corresponding iOS & Android APIs with exception of all Chinese-language locales (China, Taiwan, etc.) which have only two codes: "zh_hans" for Simplified Chinese and "zh_hant" for Traditional Chinese.

Note that locale parameter is just a helper for Countly Dashboard which contains percentages (from 0.0 to 1.0) for all message texts specified in messagePerLocale for this message (when the message was created). Example message above has locale property saying that 90% of message recipients have English language on their devices, while other 10% of users have other locales and Countly will send default message to them. This distribution is acquired from /i/pushes/prepare method.

Message personalisation

You can make messages dynamic by inserting personalised content into your messages. For example, user's first name or company name. Any property from a user profile can be used for message personalisation. Personalisation object is a key-value pair where key ("0" in example above) is a place in a corresponding string (index) of where to insert user profile property value, and value is an object with 3 mandatory keys: "k" is property key in user profile, "c" is a boolean defining whether to capitalise first letter of the property value, "f" is a fallback value to use when no such property exists in a user profile.

Message sounds

Behaviour of sound property is not cross-platform unless you pay attention to your app implementation:

  • On iOS, sound is what it should be - just a sound property in message JSON, see Apple documentation: Preparing Custom Alert Sounds.
  • On Android, sound must be a Uri pointing at corresponding sound file with an exception of "default" value which falls back to default sound of notification - Notification.DEFAULT_SOUND.

Message segmentation

userConditions & drillConditions limit audience of a particular message to users with some user properties or behaviour. For example, to send a message to a set of device ids, you can use:

{... "userConditions": {"did": {"$in": ["did1", "did2", ...]}} ...}

To send to users with some custom property set from SDKs:

{... "userConditions": {"custom.propName": {"$in": ["v1", "v2", ...]}} ...}

userConditions is a standard MongoDB query, while drillConditions is a specialised drill query. Easiest way to determine a query for a particular use case would be using Chrome Developer Tools to record a request made from Countly dashboard to Countly server.

Transactional messages

Introduced in 18.08 transactional messages allow reusing the same message again and again by just adding users to it with a separate API call. This message must be first created by running this API call (/create) with tx property set to true. See corresponding API method description for details.