Server-to-Server (S2S) Integration Guide

Note: Server-to-server integrations are only available to Enterprise customers.

 

As an alternative to the Singular SDK, which has to be embedded within your apps, Singular also provides a REST API that can be used to build a full integration that runs from your own server.

This guide explains how to build a basic S2S integration with Singular and how to implement various optional features.

For a full list of the S2S API endpoints, their parameters, and sample calls, see the S2S Endpoint Reference.

Basic Integration

The most basic integration with Singular involves letting Singular know when there is a new session - in other words, when the app is opened.

To notify Singular about a user session, call the Session Notification Endpoint.

Session notifications allow Singular to do several things:

  • If it's the first session for the app on the specific device, Singular recognizes a new install and triggers the install attribution process.
  • If the session qualifies as a re-engagement session, Singular triggers the re-engagement attribution process (learn more in the Re-engagement FAQ).
  • Otherwise, Singular just marks it as a session, which is used to track user activity and retention.

The Session Notification endpoint has some required parameters. For help obtaining all the necessary data to report a session, see Reference: Retrieving the iOS Install Receipt and Reference: Retrieving Device Identifiers and Session Data.

Tips:

  • When you collect the data to report a session, be sure to wait for asynchronous functions to return and handle various edge-cases. These are common issues in S2S integrations that can cause missing data and partial attribution!

Optional: Sending the User ID

When you notify Singular about a new session, you can add the user ID. This can be a username, email address, randomly generated string, or whichever identifier your app uses as a user ID. Singular will use the user ID in user-level data exports as well as internal BI postbacks (if you configure any).

To send the user ID, add the "custom_user_id" parameter when you call the Session Notification Endpoint.

For example:

Python HTTP cURL
import requests
import json

API_KEY = 'api_key_from_sdk_page'
LAUNCH_URL = 'https://s2s.singular.net/api/v1/launch'

params = {
  'a': API_KEY,
  'p': 'Android',
  'i': 'com.singular.app',
  'ip': '10.1.2.3',
  've': '9.2',
  'ma': 'samsung',
  'mo': 'SM-G935F',
  'lc': 'en_US',
  'aifa': '8ecd7512-2864-440c-93f3-a3cabe62525b',
  'andi': 'fc8d449516de0dfb',
  'utime': 1483228800,
  'dnt': 0,
  'install':'true',
  'n': 'MyCoolApp',
  'c': 'wifi',
  'cn': 'Comcast',
  'bd': 'Build/13D15',
  'fcm':'bk3RNwTe3H0CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1',
  'app_v':'1.2.3',
  'openuri':'myapp%3A%2F%2Fhome%2Fpage%3Fqueryparam1%3Dvalue1',
  'ddl_enabled':'false',
  'install_source': 'com.android.vending',
  'install_time': 1510040127,
  'update_time': 1510090877,
  'custom_user_id': 'player_id_1234'
}

result = requests.get(LAUNCH_URL, params=params)
print result.json()

Sending Identifiers and Attribution Data

Sending the Google Play Install Referrer (Android)

The install referrer contains information about who sent a user to the Google Play Store. When the install referrer is available to Singular, it provides the most accurate way to attribute installs. We highly recommend retrieving this value and passing it along to Singular right after the first session notification call.

Google Play collects referrer information when a user arrives at the store. If the user later installs the app they were directed to, Google Play makes the information available to the app. For more information, see Google's developer documentation.

To share the install referrer with Singular:

  1. When the app is opened for the first time, retrieve the install referrer by using the Play Install Referrer API.
  2. Report a session to Singular the usual way, using the Session Notification endpoint.
  3. Then, call the Event Notification endpoint and report an event with the reserved event name __InstallReferrer. In the e parameter (used for adding custom event attributes), add the following attributes:
    • referrer: The referrer value as retrieved from the Play Install Referrer API. This is a JSON object so make sure to encode it as a string.
    • referrer_source: The source of the referrer. Specify "service" for this.
    • clickTimestampSeconds: The click timestamp as received from the Play Install Referrer API (e.g., "1550420123").
    • installBeginTimestampSeconds: The time that the install started, as received from the Play Install Referrer API.
    • current_device_time: The time in the current device, in milliseconds (e.g., "1550420454906").

The following is sample code for reporting the install referrer event:

Python HTTP
import requests
    import json

    API_KEY = 'api_key_from_sdk_page'
    EVENT_URL = 'https://s2s.singular.net/api/v1/evt'

    referrer_values = {
      "referrer": "tracking_id%3D123456789&utm_source%3Dmdotm%26utm_medium%3Dbanner%26utm_campaign%3Dcampaign",
      "referrer_source" : "service",
      "clickTimestampSeconds" : 1550420123,
      "installBeginTimestampSeconds" : 1550420123,
      "current_device_time" : 1550420454906
    }

    params = {
        'n': '__InstallReferrer',
        'e': json.dumps(referrer_values),
        'a': API_KEY,
        'p': 'Android',
        'i': 'com.singular.app',
        'ip': '10.1.2.3',
        've': '9.2',
        'ma': 'samsung',
        'mo': 'SM-G935F',
        'lc': 'en_US',
        'aifa': '8ecd7512-2864-440c-93f3-a3cabe62525b',
        'andi': 'fc8d449516de0dfb',
        'utime': 1483228800,
        'bd': 'Build/13D15'
    }

    result = requests.get(EVENT_URL, params=params)
    print result.json()

Tracking Attribution in Apple Search Ads Campaigns

Since Apple Search Ads is a Self-Attributing Network (SAN), you have to collect the attribution data for Apple Search Ads campaigns from the app itself. Then, you have to share it with Singular.

1. Retrieving the Attribution Data:

To retrieve the attribution data, use the Apple Search Ads Attribution API. Calling requestAttributionDetails(_:): returns a JSON object containing the attribution data.

For example:

Objective-C
#import <iAd/iAd.h>

  Class ADClientClass = NSClassFromString(@"ADClient");

  if (ADClientClass) {
      id sharedClient = [ADClientClass performSelector:@selector(sharedClient)];
      
      if ([sharedClient respondsToSelector:@selector(requestAttributionDetailsWithBlock:)]) {
          [sharedClient requestAttributionDetailsWithBlock:^(NSDictionary *attributionDetails, NSError *error) {
              if (attributionDetails && attributionDetails.count > 0) {
                  // REPORT attributionDetails FROM YOUR APP TO YOUR SERVER
              }
          }];
      }
  }
Warning: Often, users who click on a Search Ads ad download and open the app immediately. If there is any latency in communications, an MMP such as Singular may not be able to receive and process the ad click in time before the app is opened and calls the Search Ads Attribution API. To prevent this, Apple recommends:
  1. Setting a delay of a few seconds before retrieving attribution data.
  2. Implementing retry logic if the response is False or an error code (0, 2, 3). Call the Apple Attribution API again 2 seconds later.

2. Sending the Attribution Data to Singular:

To share the attribution data with Singular, use the Event Notification endpoint to report an event with the reserved event name __iAd_Attribution__. Pass the JSON object you retrieved in the previous step as the value of the parameter, as in the example below.

Python HTTP
import requests
import json

API_KEY = 'api_key_from_sdk_page'
EVENT_URL = 'https://s2s.singular.net/api/v1/evt'

# !!! REPLACE WITH COLLECTED VALUE FROM APP !!!
apple_attribution_data = {
  u'Version3.1': {
      u'iad-adgroup-id': u'1234567',
      u'iad-adgroup-name': u'Ad Group Name',
      u'iad-attribution': u'true',
      u'iad-campaign-id': u'1234567',
      u'iad-campaign-name': u'Search Campaign',
      u'iad-click-date': u'2016-05-21T12:19:31Z',
      u'iad-conversion-date': u'2016-05-21T12:19:41Z',
      u'iad-keyword': u'ballon',
      u'iad-lineitem-id': u'1234567',
      u'iad-lineitem-name': u'Line Item Name',
      u'iad-org-name': u'Cool Company',
      u'iad-purchase-date': u'2016-05-21T12:19:41Z'
  }
}

params = {
  'n': '__iAd_Attribution__',
  'e': json.dumps(apple_attribution_data),
  'a': API_KEY,
  'p': 'iOS',
  'i': 'com.singular.app',
  'ip': '10.1.2.3',
  've': '9.2',
  'mo': 'iPhone9%2C4',
  'lc': 'en_US',
  'idfa': '8ECD7512-2864-440C-93F3-A3CABE62525B',
  'idfv': '38548D9F-3F73-4D4B-8545-9A920CC89191',
  'utime': 1483228800
}

result = requests.get(EVENT_URL, params=params)
print result.json()

Notes:

  • For iOS 13+, you have to send the __iAd_Attribution__ event immediately after the first session after install or re-install. Otherwise, the Apple Search Ads data will not be considered for attribution.
  • In iOS 14, Apple Search Ads attribution responses are only available under certain conditions, and not available if AppTrackingTransparency status is ATTrackingManager.AuthorizationStatus.denied

    On September 3rd update: Apple announced an update delaying certain privacy features on iOS 14, specifically removing requirements to use AppTrackingTransparency to access the IDFA until "early next year".  With this change, we recommend NOT implementing the AppTrackingTransparency pop-up with the iOS 14 launch.  This also means the Apple Search Ads attribution responses will conitnue be available via cases where the AppTrackingTransparency status is ATTrackingManager.AuthorizationStatus.notDetermined.

Tracking Events

Singular can collect data about in-app events to help analyze the performance of your marketing campaigns. Events can include any user interaction from logins and registrations to leveling up in a gaming app.

Before you implement an SDK/S2S integration with Singular, you should have a list of the events your organization wants to track (see Defining In-App Events).

To notify Singular when an event occurs in your app, call the Event Notification endpoint. The event name you include in the call is how the event will be identified in Singular reports, exports, and postbacks.

Notes:

  • We highly recommend passing event names and other attributes in English for compatibility with any third-party partners and analytics solutions you may want to use.
  • Event names are limited to 32 ASCII characters. For non-ASCII characters, the limit is 32 bytes once converted to UTF-8.
  • Event attributes and values are limited to 500 ASCII characters.

Tracking Revenue

Singular can collect data about revenue gained through the app to help analyze the performance and ROI of your campaigns. Singular will make the data available to you in reports, log export, and postbacks.

To track revenue events, use the same Event Notification endpoint that you use for all events, but add the following information:

  • is_revenue_event=true: This marks the event as a revenue event. You can skip this parameter if the event name is __iap__ or the amount is larger than zero.
  • Purchase receipt: This is an object returned from Android's or iOS's In-App Purchase (IAP) process. We highly recommend passing it along to Singular in order to give Singular full details about the transaction and enrich your Singular reports with data.
  • Purchase signature (Android only): We highly recommend passing this bit of data along to Singular in order to validate the transaction and fight in-app fraud.
  • Revenue amount (e.g., "amt=1.99").
  • Currency (use the ISO 4217 currency code, e.g. "cur=USD").

Retrieving the Purchase Receipt

After a revenue event occurs, here's how to get the purchase receipt so you can send it to Singular:

  • In Android: Google Play provides an API to retrieve the purchase receipt ("In-App Purchase Data") and the purchase signature ("In-App Data Signature"). Use the getBuyIntent() method.
  • In iOS: Use Apple's In-App Purchases API as shown in the example below (for more information, see Apple In-App Purchases API).
Objective-C Python HTTP cURL
// SKPaymentTransactionObserver
  + (void)paymentQueue:(id)queue updatedTransactions:(NSArray *)skTransactions {
     NSString *transactionReceipt = nil;

     if ([skTransaction respondsToSelector:@selector(transactionReceipt)]) {
        NSData *transactionReceiptRaw = [skTransaction performSelector:@selector(transactionReceipt)];
        if (transactionReceiptRaw) {
           transactionReceipt = [ApUtils base64Encode:transactionReceiptRaw];
        }
     }
  }

Supporting Deep Links

Deep links are links that lead into specific content inside an app. When a user clicks a deep link on a device that has the app installed, the app opens and shows a specific product or experience.

Singular tracking links can include deep linking as well as deferred deep linking (see our Deep Linking FAQ and the Singular Links FAQ for more information).

Deep Linking Prerequisites

For iOS:

For Android:

Supporting Deep Links

Whenever your app is opened through a deep link, add the URL when you report the session to Singular by using the openuri parameter. This is required when using Singular Links.

Enabling Deferred Deep Links

When the app is opened for the first time since the install, enable the deferred deep linking flow by adding the following parameters when you report the session to Singular:

  • install=true
  • ddl_enabled=true

Singular will look to see if the app was installed through a tracking link that included a deferred deep link. If it was, the call will return the following values:

  • deferred_deeplink - the deep link address. This is what you need to parse in order to show the users the right product or experience.
  • deferred_passthrough - any pass-through parameters added to the deep link.

Sample Session Notification Call with Deferred Deep Link Support

Python
import requests
    import json

    API_KEY = 'api_key_from_sdk_page'
    LAUNCH_URL = 'https://s2s.singular.net/api/v1/launch'

    params = {
        'a': API_KEY,
        'p': 'iOS',
        'i': '162738612',
        'ip': '10.1.2.3',
        've': '9.2',
        'mo': 'iPhone9%2C4',
        'lc': 'en_US',
        'idfa': '8ECD7512-2864-440C-93F3-A3CABE62525B',
        'idfv': '38548D9F-3F73-4D4B-8545-9A920CC89191',
        'utime': 1483228800,
        'dnt': 0,
        'n': 'MyCoolApp',
        'c': 'wifi',
        'cn': 'Comcast',
        'bd': 'Build/13D15',
        'openuri':'https://myapp.sng.link/A59c0/nha7?_dl=myapp%3A%2F%2Fdeeplink&_ddl=myapp%3A%2F%2Fdeferred-deeplink&_p=passthroughvalue',
        'install':'true',
        'ddl_enabled':'true'
    }

    result = requests.get(LAUNCH_URL, params=params)
    print result.json()

Sample Response

{
  "deferred_deeplink":"myapp://deferred-deeplink",
  "status":"ok",
  "deferred_passthrough":"passthroughvalue"
}

Supporting Dynamic Passthrough Parameters

Singular tracking links can include dynamic passthrough parameters (learn more). If your organization has set up dynamic passthrough parameters for a link, the deep link URL includes the _p parameter followed by a URL-encoded JSON value or an unstructured string which you can use to show the user the appropriate content or experience.

Advanced Options

The Singular S2S integration supports the following advanced features. They are implemented using the same API endpoints described above, but with special parameters and requirements.

Tracking Uninstalls

Singular can track uninstalls using the device's Silent Push Notifications. To enable it, you will need to send the device's push token to the Singular server along with every session notification.

Tracking Uninstalls in Android

To enable uninstall tracking in Android, first retrieve the FCM token from the app by calling FirebaseInstanceId.getInstance().getToken(). This method returns null if the token has not yet been generated. For more information, see the Google Firebase Documentation.

Then pass the device token in the fcm parameter when reporting the session to Singular, as in the following example:

Python HTTP cURL
import requests
import json

API_KEY = 'api_key_from_sdk_page'
LAUNCH_URL = 'https://s2s.singular.net/api/v1/launch'

params = {
  'a': API_KEY,
  'p': 'Android',
  'i': 'com.singular.app',
  'ip': '10.1.2.3',
  've': '9.2',
  'ma': 'samsung',
  'mo': 'SM-G935F',
  'lc': 'en_US',
  'aifa': '8ecd7512-2864-440c-93f3-a3cabe62525b',
  'andi': 'fc8d449516de0dfb',
  'utime': 1483228800,
  'dnt': 0,
  'n': 'MyCoolApp',
  'c': 'wifi',
  'cn': 'Comcast',
  'bd': 'Build/13D15',
  'fcm': 'bk3RNwTe3H0CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1'
}

result = requests.get(LAUNCH_URL, params=params)
print result.json()

Tracking Uninstalls in iOS

Singular can track uninstalls on iOS by using Apple's push notifications. If your app doesn't support push notifications yet, see Apple's guide. Once your app supports push notifications, just pass the device token returned from APNS when you report the session to Singular.

Notes:

  • We assume you already have a push notification implementation that you are retrieving a device token from.
  • The APNS token is usually binary data in the native form. Pass it to Singular as received from APNS. If your app is altering the token data type for another purpose, make sure to pass it as a hex-encoded string, e.g.: encodedb0adf7c9730763f88e1a048e28c68a9f806ed032fb522debff5bfba010a9b052
Python HTTP cURL
import requests
import json

API_KEY = 'api_key_from_sdk_page'
LAUNCH_URL = 'https://s2s.singular.net/api/v1/launch'

params = {
  'a': API_KEY,
  'p': 'iOS',
  'i': '162738612',
  'ip': '10.1.2.3',
  've': '9.2',
  'mo': 'iPhone9%2C4',
  'lc': 'en_US',
  'idfa': '8ECD7512-2864-440C-93F3-A3CABE62525B',
  'idfv': '38548D9F-3F73-4D4B-8545-9A920CC89191',
  'utime': 1483228800,
  'dnt': 0,
  'n': 'MyCoolApp',
  'c': 'wifi',
  'cn': 'Comcast',
  'bd': 'Build/13D15',
  'apns_token': 'b0adf7c9730763f88e1a048e28c68a9f806ed032fb522debff5bfba010a9b052'
}

result = requests.get(LAUNCH_URL, params=params)
print result.json()

Complying with Data Privacy Laws

Singular provides privacy-safeguarding functionality to help you cooperate with any partners who may be complying with consumer privacy laws such as GDPR and CCPA. These partners want to be notified if the end-user has consented to share their private information.

If you have implemented a way to ask users for consent to share their information, use the data_sharing_options parameters to notify Singular of the user's choice:

  • Pass "limit_data_sharing":true to indicate that the user consented (opted in) to share their information.
  • Pass "limit_data_sharing":false if the user refused.

Singular will pass this information on to partners who require it in order to comply with relevant regulations.

Note:

  • The use of the method is optional, but there may be attribution information that the partner will share with Singular only if specifically notified that the user has opted in.
  • Remember to URL-encode the JSON object if you pass it in a GET request.
Field Type Description Usage
limit_data_sharing boolean

Pass this optional value on every launch or evt request to indicate the end-user's preferences.

data_sharing_options=
%7B%22limit_data_sharing%22%3Atrue%7D

Reference: Retrieving the iOS Install Receipt

As you can see in the Session Notification endpoint reference, when reporting a session for an iOS app, you should pass the install receipt in the install_receipt parameter.

To retrieve this value, add the following code to your app:

Objective-C
+ (NSString*)installReceipt {
    // install receipts are iOS 7.0+
    if (NSFoundationVersionNumber < NSFoundationVersionNumber_iOS_7_0) {
        return nil;
    }
    
    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    
    if (receiptURL) {
        NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
        
        if (receipt) {
            return [receipt base64EncodedStringWithOptions:0];
        }
    }
    
    return nil;
}

Reference: Retrieving Device Identifiers and Session Data

This section details how you can retrieve certain values required in the REST API. These values are industry standard, and Apple and Google have documentation on them as well. We have provided a few reference implementation here for your convenience.

Retrieving the Google Advertising ID/Limit Ad Tracking (Android Identifiers)

Google Play Services SDK is required to obtain the Advertising ID in your app. Add the following tag as a child of your application element in AndroidManifest.xml:

XML
<meta-data android:name="com.google.android.gms.version"
           android:value="@integer/google_play_services_version" />

Sample code for getting the Google Advertising ID/Limit Ad Tracking parameters:

Java
import com.google.android.gms.ads.identifier.AdvertisingIdClient;
import com.google.android.gms.ads.identifier.AdvertisingIdClient.Info;
import com.google.android.gms.common.GooglePlayServicesAvailabilityException;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import java.io.IOException;

// Do not call this function from the main thread. Otherwise, 
// an IllegalStateException will be thrown.
public void getIdAndLAT() {
    Info adInfo = null;
    try {
        adInfo = AdvertisingIdClient.getAdvertisingIdInfo(mContext);
    } catch (IOException e) {
        // Unrecoverable error connecting to Google Play services (e.g.,
        // the old version of the service doesn't support getting AdvertisingId).
    } catch (GooglePlayServicesAvailabilityException e) {
        // Encountered a recoverable error connecting to Google Play services. 
    } catch (GooglePlayServicesNotAvailableException e) {
        // Google Play services is not available entirely.
    }

final String GAID = adInfo.getId(); final boolean limitAdTracking = adInfo.isLimitAdTrackingEnabled(); }

Retrieving Locale, Device, and Build for Android

Java
// Locale - lc= query parameter
String locale = Locale.getDefault();

// Model - mo= query parameter
String device = Build.MODEL;

// Build - bd= query parameter
String build = "Build/" + Build.ID;

Retrieving the IDFA/Limit Ad Tracking (iOS Identifiers)

Note: On September 3rd, Apple announced an update delaying certain privacy features on iOS 14, specifically removing the requirements to use AppTrackingTransparency to access the IDFA until "early next year".  With this change, we recommend NOT implementing the AppTrackingTransprency pop-up with the iOS 14 launch, as IDFA is still accessible as of iOS 14 beta 7.  Read more about iOS 14 beta 7 and the IDFA here

You can continue to retrieve the IDFA as documented below, until a future release of iOS 14 where AppTrackingTransparency will be required before the IDFA can be retrieved.

Import the AdSupport class at the top of your file:

Objective-C Swift
@import AdSupport;

Sample code for getting the IDFA/Limit Ad Tracking parameters:

Objective-C Swift
NSString* IDFA = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
NSString* IDFV = [[[UIDevice currentDevice] identifierForVendor] UUIDString];

BOOL isAdvertisingTrackingEnabled = [[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled];

The dnt (Do Not Track) parameter should be passed in Session requests.

Retrieving Locale, Device, and Build for iOS

Objective-C Swift
// Locale - lc= query parameter
NSString *locale = [[NSLocale currentLocale] localeIdentifier];

// Model - mo= query paramter
#import <sys/sysctl.h>
NSString *device() {
    size_t bufferSize = 64;
    NSMutableData * buffer = [[NSMutableData alloc] initWithLength:bufferSize];
    int status = sysctlbyname("hw.machine", buffer.mutableBytes, &bufferSize, NULL, 0);
    if (status != 0) {
        return nil; 
    }
    return [[NSString alloc] initWithData:buffer encoding:NSUTF8StringEncoding];
}

// Build - bd= query parameter
NSString * build () { 
    size_t bufferSize = 64;
    NSMutableData *buffer = [[NSMutableData alloc] initWithLength:bufferSize];
    int status = sysctlbyname("kern.osversion",buffer.mutableBytes, &bufferSize, NULL, 0);
    if (status != 0) {
        return nil;
    }
    return [[NSString alloc] initWithData:buffer encoding:NSUTF8StringEncoding];
}
Was this article helpful?