SDK development guide


Do you want to develop a new SDK for Countly? Then this guide is for you. Before starting, bear in mind that there are a lot of SDKs already developed for Countly, so please check whether the SDK you are going to develop is not already available.


To start making requests to server SDK needs 3 things: URL of the server where to make requests, app_key of the app for which to report and current device_id to uniquely identify this device.

Server URL

SDK needs to have an ability for user to specify URL for the server where their Countly instance is installed..

App Key

App key should be provided by SDK user. App should be created on Countly server. After app creation server will provide app key to the user. Same app key is used for the same app on different platforms

Device ID

Device ID is required to uniquely identify device or user. So if you have some unique user ID which you can retrieve, you can use that. If not, you can provide platform specific device identification (as Advertising identifier in Google Play Services on Android) or use existing implementations (as OpenUDID);

Other parameters

Of course you can have other platform specific parameters, like adding debug parameter, or providing different ways to generate device Ids, or allowing set any other variables or settings upon initialization.

But there are some cross platform parameters that you need to allow to set to SDK users: * country_code - (optional) ISO Country code for the user's country * city - (optional) Name of the user's city * location - (optional) comma separate lat and lng values

These parameters, if provided, should be added to all begin_session requests

More information here.

Countly Code Generator

If you want to understand how SDKs work by generating mobile or web code for custom events, user profiles, crash reporting and all other features that comes with Countly in general, we suggest that you use Countly Code Generator, which is a point and click service that builds necessary code for you.

Making Requests

Countly server is a simple HTTP based REST API server and all SDK requests should be made to /i endpoint with two required parameters: app_key and device_id.

Other optional parameters needs to be provide based on what this request should do. You can check list of all parameters that Countly Server can accept in /i endpoint Server API reference.

Since in some cases devices can be offline, etc, and requests should be queued. It is highly recommended to add timestamp to each request, when it was created.

Encoding URI components

Due to possible symbols as & and ? in encoded JSON strings, SDKs should encode uri components before adding them to the request and sending it to server.

Using GET or POST

By default preferred way is to make GET requests to Countly servers. But there can be some length limitation for GET requests based on specific platform or server settings. So the best practice is to make POST request when the data reaches over 2000 characters for single request.

So before making each request you need to check if data that you are going to send is less than 2000 characters then use GET, if more then use POST.

Additionally SDK should be able to switch to post completely, if user specifies so in SDK configuration/settings.

Parameter tampering

This is one of the preventive measures of Countly. if someone in the middle intercepts the request, it would be possible to change the data in the request and make another request with other data to the server, or simply make random requests to the server through retrieved app_key.

To prevent that SDK should provide an option so send a checksum along the request data. For this, it should be possible for developer to provide some random string as SALT to SDK as parameters or configuration options.

If this SALT is provided, right before making the request, SDK should take all the payload it about to send (all the data after ? in GET requests, including app_key and device_id or query string encoded body of POST requests) and make a sha256 hash of this data plus provided SALT and append it as checksum256={hash}

  data += "&checksum256=" + sha256Hash(data + salt);

If SALT is not provided, SDK should make ordinary requests without any checksums.

Request queue

In some cases user might be offline, thus can not make requests to the server. In other cases server may be down or on maintenance and can't accept requests. In both cases SDK should handle queuing and persistently storing requests made to Countly server and waiting for successful response from server before removing request from queue.

Note that requests should be made in historical order, so you must also preserve the order of your queue.

Simple flow on how request should behave looks like this.

  1. Initiating request - either new event reported, or session call, etc
  2. Creating payload - take all the parameters (including current timestamp) and values needed for request and generate payload which will be included in HTTP request
  3. This payload is inserted into queue (First In, First Out).
  4. All updates to queue should be persistently stored. Based on environment you can use storage for queue directly
  5. On some other thread there should be a request processor which takes first request in queue, applies checksum if needed, determines request type (GET or POST) based on length and makes HTTP request
    • if request is successfull, then it should be removed from queue and next request will be processed on next iteration
    • if request failed, request processor should have a cool down period, for a minute or so (configurable value) and then try same request again, till it is completed

There are multiple scenarios why request might fail, so to ensure that request was successfully delivered to server SDK need to make sure that:

  1. User has internet connection
  2. HTTP response code was successful (which is any 2xx code or code between 200 <= x < 300)

Additionally server replies with JSON object which has a property "result" with value success. There can be different scenarios, like when blocking specific device requests by server configuration, in which case "Success" may change to some other value, so do not completely rely on success value, but if there is no way to check HTTP response code, you can check if response contains JSON with result property.


If the response code is within the required interval and the response text is a json object which has only one key "result" (the value of that entry does not matter), then it means the request was successfully delivered to the server and can be removed from the queue.

Queue size limit

We need to limit queue size, so it would not overflow, and that syncing up would not take that much time in some specific server down for too long cases. This limit would be in amount of stored queries, and this limit should be available for end user to change as SDK settings.

In case of reaching this limit, SDK should remove older queries and insert new ones. The default limit can change from SDK needs, but suggested limit is 1000 queries.

Session flow

App Initialization

When app is initialized then SDK should send begin_session=1 request. This same request should also contain metrics parameters with maximum metrics described on /i page that can be collected from this SDK specific environment/language.

Session update

After that each minute the session should be extended by sending session_duration request with amount of seconds that passed from previous session request (begin_session or session_duration, which ever was last).

Ending session

When app exits SDK should send end_session=1 request including session_duration parameter with how many seconds passed from last session request (begin_session or session_duration, which ever was last).

Here are couple example request generated by different session lengths:

2 min 30 second session 30 second session

Session cooldown

In some cases it is hard to know for sure if session ended, like with web analytics, when user is leaving the page, and whether he will visit another one or not. That is why there is a small cooldown time of 15 seconds. If end_session request is sent and then begin_session request is sent within 15 seconds, it will be counted as the same session and session duration will extend this session, instead of applying to new one.

This makes it easier to call end_session on each page unload without worrying of starting new session if user visits another page.

If you don't need this behavior, simply pass ignore_cooldown=true parameter to all session requests and server will not extend session, but always count as new one.

15 seconds cool down is default value and can be configured on the server, so don't rely on it being 15 seconds.

Recording time of data

In order to properly report and process data (especially queued data), you should also provide a time when data was recorded. With each request, you need to provide 3 parameters:

  • timestamp: 13 digit UTC millisecond unique timestamp of the moment of action
  • hour: Current user local hour (0 - 23)
  • dow: Current user day of the week (0-sunday, 1 - monday, ... 6 - saturday)
  • tz: Current user time zone in minutes (120 for UTC+02, -300 for UTC-05)

As multiple events can be combined in single request, you should also provide these parameter automatically in every event object.

The suggested millisecond timestamp should be unique, meaning if if events were reported in the same timestamp, SDK should update millisecond timestamp in the order events were reported. The pseudo code to unique millisecond timestamp can look like this:

//variable to hold last used timestamp
lastMsTs = 0;

function getUniqueMsTimestamp(){
	//get current timestamp in miliseconds
	ts = getMsTimestamp();
  //if last used timestamp is equal or greater
  if(lastMsTs >= ts){
  	//increase last used
  	//store current timestamp as last used
  	lastMsTs = ts;
  //return timestamp
  return lastMsTs;

If it is not possible to use millisecond timestamp on specific platform, then you can also use 10 digit UTC seconds timestamp.


Depending on SDKs environment/language there could be different set of features supported. Some of them can be supported on any platforms, others are quite platform specific. For example, a desktop app type may not be providing telecom operator information.

Note that function and argument namings are just examples of what it could be. Try to follow your platform/environment/language best practices when creating and naming functions and variables.

Here is a list of things your SDK could support:

Core features

Core features are minimal set of features that SDK should support and these features are platform independent.


In official SDKs Countly is used as singleton object or basically object with shared instance. Still there are some parameters that need to be provided before SDK could work, so usually there is a init method which accepts URL, app key and device_id (or SDK generates it itself if not provided):

Countly.init(string url="", string app_key, string device_id, ...)

Session Flows

Most of official SDKs implement automatic session handling, meaning SDK users don't need to bother with session calls separately. However, it is a good practice to provide a way to disable automatic session handling and allow SDK users to make session calls themselves through methods like

  • Countly.begin_session()
  • Countly.session_duration(int seccond)
  • Countly.end_session(int seconds)

Here is a documentation that shows how you can report sessions through our API.

Device metrics

Metrics should be only reported together with begin_session=1 parameter on every session start. Collect as much metrics as possible, or allow providing some values by user upon initialization. Possible metrics are listed in API Reference.

One thing that we should agree on is identifying platforms with the same string over all SDKs, so here is the list of how we would suggest identifying platforms for server through _os metric.

  • Android - for Android
  • BeOS - for BeOS
  • BlackBerry - for BlackBerry
  • iOS - for iOS
  • Linux - for Linux
  • Open BSD - for Open BSD
  • os/2 - for OS/2
  • macOS - for Mac OS X
  • QNX - for QNX
  • Roku - for Roku
  • SearchBot - for SearchBots
  • Sun OS - for Sun OS
  • Symbian - for Symbian
  • Tizen - for Tizen
  • tvOS - for Apple TV
  • Unix - for Unix
  • Unknown - if operating system is unknown
  • watchOS - for Apple Watch
  • Windows - for Windows
  • Windows Phone for Windows Phone

SDK Meta Data

SDK should send following meta data with every begin_session request.

  • SDK name:

Query String Key: sdk_name Query String Value: [language]-[origin]-[platform] Example: &sdk_name=objc-native-ios

  • SDK version:

Query String Key: sdk_version Query String Value: SDK version as string Example: &sdk_version=16.10


Events (or custom events) are the basic Countly reporting tool that reports when something has happened in the app. Someone clicked a button, did specific action, etc, all of that could be recorded as an event.

Events should be provided by the SDK user, who knows what's important for the app to log. Also events can also be used to report some Countly internal events, starting with [CLY]_ prefix that vary per feature implementation on different platforms.

Event must contain key and count properties. If count is not provided it should default to 1. Optionally user can also provide sum property (for example of in app purchase events), dur property for recording some duration/period of time and segmentation as map with keys and values for segmentation.

More on event format can be found in API Reference.

Here are couple of example events:

User logged in the game * key = login * count = 1

User completed second a level in the game with score 500 * key = level_completed * count = 1 * segmentation = {level=2, score=500}

User purchased something in the app worth 2.99 in main screen * key = purchase * count = 1 * sum = 2.99 * dur = 30 * segmentation = {screen=main}

As you can imagine, your SDK should provide methods to cover these combinations, either by defaulting values or by function parameter overloading, etc:

  • Countly.event(string key)
  • Countly.event(string key, int count)
  • Countly.event(string key, double sum)
  • Countly.event(string key, double duration)
  • Countly.event(string key, int count, double sum)
  • Countly.event(string key, map segmentation)
  • Countly.event(string key, map segmentation, int count)
  • Countly.event(string key, map segmentation, int count, double sum)
  • Countly.event(string key, map segmentation, int count, double sum, double duration)

Note: count value defaults to 1 internally if not specified.

Timed Events

Basically you can report time with dur property in an event. It is a good practice to allow user to measure some periods internally using SDK API. For that purpose, SDK needs to provide methods below.

  • startEvent(string key) - which internally will save event key and current timestamp in the associative array/map

  • endEvent(string key, map segmentation, int count, double sum) - which will take event starting timestamp by event key from the map, get current timestamp and calculate the duration of the event and fill it up as dur property and report an event as you would report any regular event.

  • endEvent(string key) - which will simply end event with 1 as count, 0 as sum and nil as segmentation values.

If it is possible, SDK can provide a way to start multiple timed events with same key, like returning event instance in the method and then calling end method on that instance.

If not then in case already started events tried to be started again or already ended or not yet started events tried to be ended, these calls should be ignored or provide informative error.

User Details

Your SDK does not need to have a platform specific way to get user data, if it is not possible on your platform. But you need to provide a way for a developer to pass this information to SDK and send it to Countly server.

For that you can create a method to accept object with key/values about user that are described here, or provide a parameterized method to pass the information about user. Note that all fields are optional.

Additionally there could be custom key values added to user details, in this case you would need to provide a means to set them:

  • Countly.user_details(map details)
  • Countly.user_custom_details(map custom_details)

You can find more information on what data can be set for a user in this link.

Modifying custom data properties

You should also provide an option to modify custom user data, like increase value on server by 1, etc. Since there are many operations you can do with that data, it is recommended to implement a subclass for this API, which can be retrieved through Countly instance.

The standard methods SDK should provide are (provided as pseudo code and naming conventions may differ from platform to platform):

  • Countly.userData.set(string key, string value)
  • Countly.userData.setOnce(string key, string value)
  • Countly.userData.increment(string key)
  • Countly.userData.incrementBy(string key, double value)
  • Countly.userData.multiply(string key, double value)
  • Countly.userData.max(string key, double value)
  • Countly.userData.min(string key, double value)
  • Countly.userData.push(string key, string value)
  • Countly.userData.pushUnique(string key, string value)
  • Countly.userData.pull(string key, string value)
  • //send data to server

Note: make sure push, pushUnique and pull parameters when reporting to server, can provide multiple values for same property as array.

Here is more information on how to report this data to server

Custom Device ID / Changing Device ID

There are 3 main cases when SDK user might want to provide custom device ID to identify device/user.

1. Tracking same user across multiple devices

In this case app developers need to provide their own way to identify users on initialization. So, Countly SDK needs to provide a way to set custom device ID upon initialization and store it persistently for next session use. If there is an already stored device ID, Countly SDK should use stored one primarily, unless it is forced to do otherwise.

2. Tracking multiple users on same device

In addition to initialization, developers may need to change device ID while the app is running. For example when an end-user signs out and another end-user signs in. In this case Countly SDK needs to provide a way to change device ID at any point while the app is running. It should replace internally used device ID with the new one, and use it for all new requests, and persistently store it for next sessions. So, Countly SDK should follow these steps:

  • Add currently recorded but not queued events to request queue
  • End current session
  • Clear all started timed-events
  • Change device ID and store it persistently for next session use
  • Begin new session with new device ID

3. Tracking an unauthenticated user who becomes authenticated later

Developers may need to change device ID to their own internal user ID and merge server side data previously generated by user while he/she was unauthenticated. It is similar to Case 2 but Countly SDK needs to merge data on server too. In order to make a proper transition Countly SDK should follow these steps:

  • Temporarily keep current device ID
  • Change device ID and store it persistently for next session use
  • Use old_device_id API with temporarily kept old device ID to merge data on the server
  • No need to end and restart current session, or clear started timed-events

To summarize, Countly SDK should provide a proper way to change device IDs for all 3 cases.

Note: If new and current device ID are exactly same, then Countly SDK must ignore this change call.

Recording location

There are 4 location related parameters that can be set in a Countly SDK. It is "country code", "city", "location"(gps coordinates), "ip address. Those could be set with functions similar to these:

  • Countly.country_code(string country_code)
  • city)
  • Countly.location(double latitude, double longitude)
  • Countly.ip_address(string ip_address)

City should always be paired together with country, you can not set only one of them.

When empty "location" is sent, is interpreted as "disable location tracking for this device ID"

Empty country code, city and ip adress can not be sent.

Additional parameters

There are also optional, additional parameters. If you can get them on your platform, then you can append them to any/every request. But if not, it might be a good idea to allow SDK user to optionally provide such values.

If values are not provided, Countly server will try to determine them automatically based on all other provided data.

Here is more information on possible additional API parameters.

Reserverved Segmentation keys

Currently there are 9 segmentation keys that are reserved for Countly internal use. They should be ignored when the SDK user provides them as segmentation for any functionality. The list of key's is:

  • name
  • segment
  • visit
  • start
  • bounce
  • exit
  • view
  • domain
  • dur

Push Notifications

Push notifications are platform specific and not all platforms have them, but if your platform has, you would need to register your device to push notification server and send the token to Countly server. For more information, please click here for API calls.

From SDK API point of view, there could be one simple function to enable push notifications for Countly server:


Crash reporting

On some platforms it is possible to automatically detect errors and crashes. In this case, your SDK can report them to Countly server. Just like other similar functions, this is also optional so if a crash report is not sent, it won't be displayed on dashboard under Crashes section. Here is more information on Crash reporting parameters that you can use in your SDK.

For crashes, all information except app version and OS are optional, but you should collect as much information about device as possible to make sure that each crash can be more identifiable with additional data. You should also provide a way for user to log errors manually (for example logging handled exceptions, which are not fatal).

Basically in automatically captured errors, you should set _nonfatal property to false, while on user logged errors, _nonfatal property should be true. You should also provide a way to set custom key/values to be reported as segments with crash reports either by providing global default segments, or setting separately for automatically tracked errors and user logged errors.

Additionally there should be a way for SDK user to leave breadcrumbs that would be submitted together with crash reports. In order to collect breadcrumbs as logs, create an empty array on initialization and provide a method to add breadcrumbs as strings into that array as elements for log. And upon crash, concatenate array with new line symbols and submit under _logs property. There is no need to persistently save those logs on device, since we want to have a clean log on each app start.

The end API could look like this (but it should be totally based on specific platform error handling)

  • Countly.enable_auto_error_reporting(map segments)
  • Countly.log_handled_error(string title, string stack, map segments)
  • Countly.log_unhandled_error(string title, string stack, map segments)
  • Countly.add_breadcrumb(string log)


Reporting views would allow to analyze which views/screens/pages did the app user visit, and how long he spent on specific view. If it is possible to automatically determine when user visits specific view in your platform, then you should provide an option to automatically track views. Also, it is also important to provide a way to track views manually. Here is more information on view tracking API.

Let's start with manual view tracking, as it should be available on any platform. First, you need to have 2 internal private properties, as string lastView and int lastViewStartTime. Then, create internal private method reportViewDuration, which checks if lastView is null, and if not it should report duration for lastView, by calculating it from current timestamp and lastViewStartTime.

After those steps, provide a reportView method to set view name as string parameter, inside this method call reportViewDuration, to report duration of previous view if any, and then set provided view name as lastView and current timestamp as lastViewStartTime. And report view as event with visit property and segment as your platform name. Additionally if this is the first view user visits in this app session, then report start property as true too. You also need to call reportViewDuration on app exit event.

After manual view tracking is implemented, you can also implement automatic view tracking, if it is possible on your platform. In order to implement that, you need to catch your platform's specific event when view is changed can call your implemented reportView method with view name.

Additionally you need to implement enabling and disabling automatic view tracking, as well as checking status if automatic view tracking is currently enabled or not.

The pseudo code to implement view tracking could look like this:

class Countly {
    String lastView = null;
    int lastViewStartTime = 0;
    boolean autoViewTracking = false;
    private void reportViewDuration(){
        if(lastView != null){
             //create event with parameters and 
             //calculating dur as getCurrentTimestamp()-lastViewStartTime
    void onAppExit(){
   void onViewChanged(String view){
    public void reportView(String name){
        //report previous view duration
        lastView = name;
        lastViewStartTime = getCurrentTimestamp();
        //create event with parameters without duration
       // duration will be calculated on next view start or app exit
    public void setAutoViewTracking(boolean enable){
        autoViewTracking = enable;
    public boolean getAutoViewTracking(){
        return autoViewTracking;

Additionally if you platform supports actions on view, like clicks, you can report them too. Here is more information on reporting actions for views.


If possible, SDK should provide a simple 1-to-5 star-rating interface for getting user's feedback about the application. Interface will have a simple message explaining what it is for, a 1-to-5 star meter for getting users rating, and a dismiss button, incase user does not want to give a rating. This star-rating has nothing to do with App Store/Google Play Store ratings and reviews. It is just for getting a brief feedback from users, to be displayed on Countly dashboard.

After user gives a rating, a reserved event will be recored with [CLY]_star_rating as key and following as segmentation dictionary:

  • platform: on which application runs
  • app_version: application's version number
  • rating: user's 1-to-5 rating

If user dismisses star rating dialog without giving a rating event will not be recorded. Star-rating dialog's message and dismiss button title can be customized using properties on initial configuration object.

CountlyConfiguration.starRatingMessage = "Custom Message";
CountlyConfiguration.starRatingDismissButtonTitle = "Custom Dismiss Button Title";

If not set explicitly, message should be "How would you rate the app?" and dismiss button title will be "Dismiss" or corresponding localized versions depending on device language.

Star-rating dialog can be displayed in 2 ways:

1. Manually by developer

Star-rating dialog will be displayed when developers calls the specified method like askForStarRating. Optionally, there will be a callback method indicating user's 1-to-5 rating value for the developer, incase developer wants to user user's rating.


There is no limit on how many times star-rating dialog can be displayed manually.

2. Automatically depending on session count

Star-rating dialog will be displayed when application's session count reaches specified limit, once for each new version of the application. So, SDK should keep track of session count for each app version locally, and compare to specified count on each app launch. This session count limit can be specified on initial configuration.

CountlyConfiguration.starRatingSessionCount = 5;

Once star-rating dialog is displayed automatically, it will not be displayed again unless there is a new app version.

There should be an optional flag called starRatingDisableAskingForEachAppVersion on initial configuration, to force star-rating dialog to be displayed only once for app lifetime, instead of for each new version.

CountlyConfiguration.starRatingDisableAskingForEachAppVersion = false;

GDPR compatability

GDPR compatability is about dividing SDK functionality into different features, and allowing SDK users to ask for consent on using these features, and upon giving consent, only then SDK can send newly collected (after consent is given) data to the server.

Additionally user may change his/her mind during app run and opt out of some features, thus SDK should be able to disable these features on run time.

Mostly persistence of consent settings should be handled by client and not SDK, but if there is a requirement for SDK to handle it too, it may do so.

Initial Configuration

There should be a flag on initial configuration (e.g. requiresConsent), to inform SDK that it requires consent before doing anything.

If this configuration is set, SDK should not send any data to server. Even if specific SDK methods (reporting errors, or recording custom events etc.) are called manually, these calls should be ignored until consent is given.

Exposing Available Features for Consent

SDK should expose all the features it supports for consent, in form of a method or static properties, or constant strings. So, developer can check what features are available during development or creating a consent form UI.

Currently available features are:

* sessions - tracking when, how often and how long users use your app/website

* events - allow sending custom events to server

* location - allow sending location information, if consent not given, SDK should force send empty location on begin_session, to prevent server from determining location by IP address

* views - allow tracking which views/pages user visits

* scrolls - allow tracking user scrolls for scroll heatmap

* clicks - allow tracking user clicks for heatmap as well as link clicks

* forms - allow tracking user's form submissions

* crashes - allow tracking crashes, exceptions and errors

* attribution - allow tracking from which campaign did user come

* users - allow collecting/providing user information, including custom properties

* push - allow push notifications

* star-rating - allow to send their rating and feedback

* accessory-devices - allow to detect accessory or wearable devices like Apple Watch etc.

Note that available features may change depending on platform.

Feature Grouping (optional)

SDK can also provide features grouping, to allow putting existing features into groups and using these groups to give, cancel and check consent.

For example, client may put "sessions","events" and "views" into one group called "activity". And after that, giving consent to "activity", SDK should automatically give consent to all underlying features.


Giving Consent

SDK should have a method to give consent, and this method should have feature names or groups as parameters. It can accept single feature or group, as well as multiple features or groups, in form of array or variable arguments, depending on SDK language and environment.

At any time during app run, user may give consent to more features after starting the SDK.

Upon receiving consent, SDK should immediately start collecting data allowed by provided feature(s) and also send consent approval to the server in form of consent={"feature":true}. For the exact feature names refer to the list above. For example, if features are crashes and users, then request should contain consent={"crashes":true,"users":true}. This can be a separate request or attached to any other SDK request.

If an already given consent is tried to be given again, SDK should ignore it.

Checking Consent Status

There should also be a method to check current consent status for SDK, returning true if consent was given, and false, if there was no consent. Checking status for groups should return true only if all the underlying features return true.

Removing Consent

SDK also needs to provide a method to remove consents. It should support the same parameter options as giving consent method.

Upon receiving request to remove consent, SDK should immediately stop collecting data allowed by provided feature(s) and also send consent removal to the server in form of consent={"feature":false}. For example, if feature are crashes and users, then request should contain consent={"crashes":false,"users":false}

This can be a separate request or attached to any other request.

Depending on SDK structure, SDK may sync existing requests in queue, or ignore requests in queue and never send them, or remove them from queue.

Both giving consent and removing consent can be combined in single request too. If, for example, consent was given for crashes, and removed from users, then request should contain consent={"crashes":true,"users":false}

Common Flow with Required Consent

1) Developer sets requiresConsent on initial configuration: config.requiresConsent = true;

2) Developer starts the Countly SDK, but Countly SDK does nothing related to user tracking, no information is queued or sent to server.

3) Developer handles permissions, like showing popup to user and asking for consent, as well as persistently storing user choices.

4) Upon receiving consent from user or storage, Developer calls giveConsent method of the Countly SDK, feature names or groups, depending on the permissions they managed to get.

5) Countly SDK starts relevant features and also sends a request to Countly Server. ( consent={"feature":true} )

6) Countly SDK checks if FeatureNames has already been passed to giveConsent method before, and ignores all repetitive calls.

7) Countly SDK does not persistently store status of given consents, and expects developer to call giveConsent method on each app launch, just like starting the SDK.

8) If app user changes his/her mind about consents later, developer reflects this to Countly SDK using removeConsent method, passing feature names or groups.

9) Countly SDK stops the relevant features and also sends a request to Countly Server. ( consent={"feature":false} )

10) Countly SDK checks if feature names or groups has already been passed to removeConsent method before, and ignores all repetitive calls. Or attempts to cancel consents never given before at all.

Remote Config

Automatic Fetch

Remote Config feature lets app developers change the behavior and appearance of their applications anytime, by creating or updating custom key-value pairs on Countly Server.

First of all, interaction with Countly Server for Remote Config feature please check Remote Config API documentation.

There should be flag on initial config to enable automatic fetching of remote config on SDK start. If this flag is set, SDK will automatically fetch remote config from server and store it locally. Locally stored remote config should reflect server response as is, overwriting any existing remote config. No merging or partial updating. Automatic fetching will be performed only on SDK start. Not with every begin session. There should also be a callback on initial config, to inform developer about result of automatic fetching of remote config.

e.g. config.enableRemoteConfig = true;

Manual Fetch

There should be a method/function to fetch remote config manually anytime developer wants to. Just like automatic fetch, this method will fetch remote config from server and store it locally. Locally stored remote config should reflect server response as is, overwriting any existing remote config. No merging or partial updating. This method should take a callback argument to inform developer about result of manual fetching of remote config. Callback on initial config should not be affected by manual fetchings, as it is for automatic fetchings only.

e.g. updateRemoteConfig(callback(){ })

Getting Values

There should be a method to get remote config values for given key. It will return the value for given key. If key does not exists, or remote config is not fetched yet, this method should return nil or null or whatever the platform handles the absence of values. If server is not reachable, this method should return the last fetched and locally stored value if available.

e.g. remoteConfigValueForKey(key)

Keys and Omit Keys

For manual fetch, there should be 2 additional methods: - one for specifying which keys to be updated, - and one for specifiying which keys will be ignored.

These methods should take an array of keys as argument, in addition to callback, and send requests with keys= or omit_keys= query strings. For the result of these requests, only keys in response should be updated in local storage. Not a complete overwrite like automatic or standard manual fetch.

e.g. updateRemoteConfigForKeysOnly(keys, callback(){ }) e.g. updateRemoteConfigExceptKeys(keys, callback(){ })

Example case: Local storage reflecting server as is: (after an automatic or manual fetch)

  "a": "x",
  "b": "y",
  "c": "z",

Calling update with for specified keys only:

updateRemoteConfigForKeysOnly(["a"], callback(){ });


  "a": "xx",

Local storage:

  "a": "xx",
  "b": "y",
  "c": "z",


In case consentRequired flag is set on initial config, fetching remote config (automatic or manual) will be performed only when there is at least one consent given. Additionaly, if sessions consent is given, remote config requests will have metrics info, similar to begin session requests.

Device ID Change

After device ID change, locally stored remote config should be cleaned. And automatic fetch should be performed if enabled on initial config.


Remote config requests need to include checksum if enabled on initial config. Just like all other requests, only query string part will be used to calculate hash.

Application Performance Monitoring

Countly server support multiple metrics for performance monitoring. They are divided into 3 groups:

  • Custom traces
  • Network request traces
  • Device traces

Trace keys

In some of the exposed functionality, developers can provide the metric key for identifying the tracked thing. There are some requirements that the key must meet. Those requirements will be enforced on the API endpoint, and if the key will be deemed invalid, the trace will be dropped. Therefore it's important to catch invalid keys and warn about them on the SDK side.

In short, the keys have to be 32 or less characters long and have to correspond to the following regex:


Unwrapping those rules, we can create the following list:

  • The key has to be 32 or less characters long
  • The only characters that the key can contain are Latin letters (uppercase and lowercase), numbers and underscores
  • the key can start only with a letter

API Calls

Currently there is no batching functionality for APM requests. Every APM data point/trace should be put in the request queue immedietelly after it is acquired. So that would be after a network request is finished, after a custom trace has ended, every time the device goes to the background or foreground, etc.

APM data is combined into a single json object which is set to the "apm" param. So a basic request would look similar to:

&apm={ _apm_params }


Custom traces

These are used as a tool to measure the performance of some running task. At the basic level, this function is similar to timed events. The default metric that get's tracked is the "duration" of the event.  There should be a "startTrace" and "endTrace" call, which start and end the tracking. When ending the tracking, the developer has the option of providing additional metrics. Those metrics are String and Integer/Numeric pairs.

 Sample custom trace API request:

"apm_metrics":{"duration": 10, “memory”: 200},
"stz": 1584698900000,
"etz": 1584699900000}

Network traces


Sample network trace request:

"apm_metrics":{"response_time":1330,"response_payload_size":120, "response_code": 300, "request_payload_size": 70},
"stz": 1584698900000,
"etz": 1584699900000}



Device traces


Sample device trace request:

"apm_metrics":{"duration": 15000},
"stz": 1584698900,
"etz": 1584699900}







Was this article helpful?
0 out of 0 found this helpful

Looking for help?