Note: Server-to-server integrations are only available by request. Talk to your Singular Customer Service Manager or Singular Support.
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 implement various optional features.
For a full list of the S2S API endpoints, their parameters, and sample calls, see the S2S Endpoint Reference.
Reporting Sessions
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 an App session, call the Session Notification Endpoint.
GET | https://s2s.singular.net/api/v1/launch |
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 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 Retrieving App Data.
Tip: When you collect the data to report a session, be sure to wait for asynchronous functions to return and handle various edge cases. This is a common issue that can cause missing data and partial attribution.
Reporting 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.
GET | https://s2s.singular.net/api/v1/evt |
Notes:
- Singular recommends passing events using Singular's standard event and attribute naming convention. Using standard events streamlines mapping and compatibility with your partners standard events in integrations.
- 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.
Important:
- 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.
Reporting 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 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 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).
// 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];
}
}
}
Sample Custom Revenue Event
import requests
import json
SDK_KEY = '[sdk_key from Developer tools > SDK Integration > SDK keys]'
EVENT_URL = 'https://s2s.singular.net/api/v1/evt'
params = {
'a': SDK_KEY,
'p': 'Android',
'i': 'com.singular.app',
'ip': '10.1.2.3',
've': '9.2',
'ma': 'samsung',
'mo': 'SM-G935F',
'lc': 'en_US',
'bd': 'Build/13D15',
'aifa': '8ecd7512-2864-440c-93f3-a3cabe62525b',
'utime': 1483228800,
'n': 'RevenueEventName',
'amt': '2.50',
'cur': 'USD',
'is_revenue_event': 'true',
'purchase_receipt': {'orderId"':'GPA.1234',
'packageName':'com.example',
'productId':'com.example.product',
'purchaseTime':1417113074914,
'purchaseState':0,
'purchaseToken':'hakfcimbk... pM'},
'receipt_signature': 'TyVJfHg8OAoW7W4wuJt...5agEDMnNXvhfrw==',
'purchase_product_id': 'com.example.product',
'purchase_transaction_id': 'GPA.1234-1234-1234-12345'
}
result = requests.get(EVENT_URL, params=params)
print result.json()
https://s2s.singular.net/api/v1/evt?n=RevenueEventName&a=SDK_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&amt=2.50&cur=EUR&bd=Build%2F13D15&is_revenue_event=true&purchase_receipt=%7B%27orderId%27%3A%27GPA.1234%27%2C%27packageName%27%3A%27com.example%27%2C%27productId%27%3A%27com.example.product%27%2C%27purchaseTime%27%3A1417113074914%2C%27purchaseState%27%3A0%2C%27purchaseToken%27%3A%27hakfcimbk...%20pM%27%7D&receipt_signature=TyVJfHg8OAoW7W4wuJt5agEDMnNXvhfrw==&purchase_product_id=com.example.product&purchase_transaction_id=GPA.1234-1234-1234-12345https://s2s.singular.net/api/v1/evt?n=RevenueEventName&a=SDK_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&amt=2.50&cur=EUR&bd=Build%2F13D15
curl --request GET \ --url 'https://s2s.singular.net/api/v1/evt?n=RevenueEventName&a=SDK_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&amt=2.50&cur=EUR&bd=Build%2F13D15&is_revenue_event=true&purchase_receipt=%7B%27orderId%27%3A%27GPA.1234%27%2C%27packageName%27%3A%27com.example%27%2C%27productId%27%3A%27com.example.product%27%2C%27purchaseTime%27%3A1417113074914%2C%27purchaseState%27%3A0%2C%27purchaseToken%27%3A%27hakfcimbk...%20pM%27%7D&receipt_signature=TyVJfHg8OAoW7W4wuJt5agEDMnNXvhfrw==&purchase_product_id=com.example.product&purchase_transaction_id=GPA.1234-1234-1234-12345'
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.
Additional Attribution Handling
Attribution for Apple Search Ads Campaigns (iOS)
Apple Search Ads is considered a Self-Attributing Network (SAN). As of iOS 14.3, the Apple Search Ads integration is supported through two iOS frameworks:
- For iOS 14.2 and below, Apple Search Ads is supported via the iAd framework.
- For IOS 14.3 and above, Apple Search Ads is supported via the AdServices framework.
We recommend that both the iAd and AdServices frameworks are implemented until the iAd framework is deprecated at a future date. Since AdServices is still a new Apple service, Singular will utilize both services but prioritize AdServices over iAd signals for attribution and reporting.
For more information, see our Apple Search Ads integration documentation.
Implementing Apple Search Ads via iAd (iOS 14.2 and below)
1. Retrieving the Attribution Data:
To retrieve the attribution data, use the Apple Search Ads iAd API. Calling requestAttributionDetails(_:): returns a JSON object containing the attribution data.
For example:
#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
}
}];
}
}
- Setting a delay of a few seconds before retrieving attribution data.
- 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 e parameter, as in the example below.
import requests
import json
SDK_KEY = '[sdk_key from Developer tools > SDK Integration > SDK keys]'
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': SDK_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()
https://s2s.singular.net/api/v1/evt?n=__iAd_Attribution__&e=%7B%22Version3.1%22%3A%7B%22iad-purchase-date%22%3A%20%222016-10-25T22%3A24%3A35Z%22%2C%22iad-keyword%22%3A%20%22ballon%22%2C%22iad-adgroup-id%22%3A%20%221234567%22%2C%22iad-campaign-id%22%3A%20%221234567%22%2C%22iad-lineitem-id%22%3A%20%221234567%22%2C%22iad-org-id%22%3A%20%224070%22%2C%22iad-org-name%22%3A%20%22Cool%20Company%22%2C%22iad-campaign-name%22%3A%20%22Search%20Campaign%22%2C%22iad-conversion-date%22%3A%20%222016-05-21T12%3A19%3A41Z%22%2C%22iad-conversion-type%22%3A%20%22Redownload%22%2C%22iad-click-date%22%3A%20%222016-05-21T12%3A19%3A31Z%22%2C%22iad-attribution%22%3A%20%22true%22%2C%22iad-adgroup-name%22%3A%20%22Ad%20Group%20Name%22%2C%22iad-lineitem-name%22%3A%20%22Line%20Item%20Name%22%7D%7D&a=SDK_KEY&p=iOS&i=com.singular.app&ip=10.1.2.3&ve=9.2&ma=Apple&mo=iPhone8%2C1&lc=en_US&idfa=8ECD7512-2864-440C-93F3-A3CABE62525B&idfv=38548D9F-3F73-4D4B-8545-9A920CC89191&utime=1568948680
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.
Implementing Apple Search Ads via AdServices (iOS 14.3 and above)
1. Retrieving the attribution token:
Retrieve the attribution token using attributionToken() as soon the app initializes for the first time after an install or re-install.
For example:
#import <AdServices/AdServices.h>
NSError *error = nil;
Class AAAttributionClass = NSClassFromString(@"AAAttribution");
if (AAAttributionClass) {
NSString *attributionToken = [AAAttributionClass attributionTokenWithError:&error];
if (!error && attributionToken) {
// Handle attributionToken
}
}
Notes:
- The attribution token is generated on the device.
- After generating, the token is cached for 5 minutes on the device. After 5 minutes, a new token is generated if attributionToken() is called.
- The generated token is valid for 24 hours.
2. Send the attribution token to Singular:
URL encode the token and send the token to Singular via the session notification endpoint, appended to the &attribution_token= parameter. This token should be sent on the first session after every install and re-install to enable Singular to track Apple Search Ads downloads and re-downloads.
Important: 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. Retrieve this value and pass it along to Singular on the first session notification call. It is required for some important Singular features, such as receiving Facebook data in our User-Level Exports, sharing it with Data Destinations, and sending postbacks.
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:
- When the app is opened for the first time, retrieve the install referrer using the Play Install Referrer API.
-
Report a session to Singular using the Session Notification endpoint including the parameter install_ref. This parameter is JSON-encoded and has the following attributes:
Attribute Description 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 Specify "service". 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:
import requests
import json
SDK_KEY = '[sdk_key from Developer tools > SDK Integration > SDK keys]'
LAUNCH_URL = 'https://s2s.singular.net/api/v1/launch'
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
}
referrer_values = json.dumps(referrer_values, separators=(',',':'))
params = {
'a': SDK_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': '123456789abcd',
'install_ref' : referrer_values
}
result = requests.get(LAUNCH_URL, params=params)
print result.json()
https://s2s.singular.net/api/v1/launch?aifa=8ecd7512-2864-440c-93f3-a3cabe62525b&andi=fc8d449516de0dfb&p=Android&a=SDK_KEY&i=com.singular.app&ip=10.1.2.3&ve=9.2&dnt=0&n=MyCoolApp&dnt=0&c=wifi&cn=Comcast&lc=en_US&bd=Build%2FMMB29K&ma=samsung&mo=SM-G935F&custom_user_id=123456789abcd&install_ref=%7B%22installBeginTimestampSeconds%22%3A%221568939453%22%2C%22referrer%22%3A%22utm_source%3Dgoogle-play%26utm_medium%3Dorganic%22%2C%22clickTimestampSeconds%22%3A%220%22%2C%22referrer_source%22%3A%22service%22%2C%22current_device_time%22%3A%221568944524%22%7D%0A
Meta Install Referrer Attribution (Android)
"Meta Referrer" is an Android-specific measurement solution introduced by Facebook to allow advertisers access to granular user-level attribution data for Android app installs (see Facebook's data policies). It is comprised of implementing both "Google Play Install Referrer" (see "Passing Google Install Referrer") and "Meta Install Referrer" technologies for app install measurement. Read more about Meta Referrer in the FAQ on the topic.
To share Meta Install Referrer information with Singnular:
- When the app is opened for the first time, retrieve the Meta install referrer according to Meta's documentation.
-
Report a session to Singular using the Session Notification endpoint including the parameter meta_ref. This parameter is JSON-encoded and has the following attributes:
Attribute Description is_ct The "is_ct" as received from the Meta Install Referrer. (e.g. 0 or 1) install_referrer The "install_referrer" as received from the Meta Install Referrer actual_timestamp The "actual_timestamp" as received from the Meta Install Referrer (e.g., 1693978124).
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:
- Configure at least one sub-domain for your links in the Link Management page in Singular. For more information, see the Singular Links FAQ.
- Enable Universal Links per the SDK documentation.
- Implement a links handler in the app code (see the handler in the SDK documentation).
For Android:
- Configure Android deep links per the SDK documentation.
- Implement a links handler in the app code (see the handler in the SDK documentation).
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 looks to see if the app was installed through a tracking link that included a deferred deep link. If it was, the call returns the following values:
- deferred_deeplink - the deep link address. This is what you need to parse to show the users the right product or experience.
- deferred_passthrough - any passthrough parameters added to the deep link.
Sample Session Notification Call with Deferred Deep Link Support
import requests
import json
SDK_KEY = '[sdk_key from Developer tools > SDK Integration > SDK keys]'
LAUNCH_URL = 'https://s2s.singular.net/api/v1/launch'
params = {
'a': SDK_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"
}
Handling Singular Short Links
When the app is opened from a Singular short link, the following parameter should be sent on the launch request to notify the Singular endpoint that the url sent in openuri should be resolved to the Singular long link.
- singular_link_resolve_required=true
Singular returns the long link and the App Developer can parse the deep link and passthrough parameters in the link handler.
Sample Session Notification Call with Short Link resolution
import requests
import json
SDK_KEY = '[sdk_key from Developer tools > SDK Integration > SDK keys]'
LAUNCH_URL = 'https://s2s.singular.net/api/v1/launch'
params = {
'a': SDK_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/q5a2',
'singular_link_resolve_required':'true',
}
result = requests.get(LAUNCH_URL, params=params)
print result.json()
Sample Response
{
"status":"ok",
"resolved_singular_link":"https://myapp.sng.link/A59c0/nha7?_dl=myapp%3A%2F%2Fdeeplink&_ddl=myapp%3A%2F%2Fdeferred-deeplink&_p=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.
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:
import requests
import json
SDK_KEY = '[sdk_key from Developer tools > SDK Integration > SDK keys]'
LAUNCH_URL = 'https://s2s.singular.net/api/v1/launch'
params = {
'a': SDK_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()
https://s2s.singular.net/api/v1/launch?aifa=8ecd7512-2864-440c-93f3-a3cabe62525b&andi=fc8d449516de0dfb&p=Android&a=SDK_KEY&i=com.singular.app&ip=10.1.2.3&ve=9.2&n=MyCoolApp&dnt=0&c=wifi&cn=Comcast&lc=en_US&bd=Build%2FMMB29K&ma=samsung&mo=SM-G935F&custom_user_id=player_id_1234
curl --request GET \
--url 'https://s2s.singular.net/api/v1/launch?aifa=8ecd7512-2864-440c-93f3-a3cabe62525b&andi=fc8d449516de0dfb&p=Android&a=SDK_KEY&i=com.singular.app&ip=10.1.2.3&ve=9.2&n=MyCoolApp&dnt=0&c=wifi&cn=Comcast&lc=en_US&bd=Build%2FMMB29K&ma=samsung&mo=SM-G935F&custom_user_id=player_id_1234'
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:
import requests
import json
SDK_KEY = '[sdk_key from Developer tools > SDK Integration > SDK keys]'
LAUNCH_URL = 'https://s2s.singular.net/api/v1/launch'
params = {
'a': SDK_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()
https://s2s.singular.net/api/v1/launch?a=SDK_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%2F13D15&fcm=bk3RNwTe3H0CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1
curl --request GET \
--url 'https://s2s.singular.net/api/v1/launch?a=SDK_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%2F13D15&fcm=bk3RNwTe3H0CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1'
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, 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
import requests
import json
SDK_KEY = '[sdk_key from Developer tools > SDK Integration > SDK keys]'
LAUNCH_URL = 'https://s2s.singular.net/api/v1/launch'
params = {
'a': SDK_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()
https://s2s.singular.net/api/v1/launch?a=SDK_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%2F13D15&apns_token=b0adf7c9730763f88e1a048e28c68a9f806ed032fb522debff5bfba010a9b052
curl --request GET \
--url 'https://s2s.singular.net/api/v1/launch?a=SDK_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%2F13D15&apns_token=b0adf7c9730763f88e1a048e28c68a9f806ed032fb522debff5bfba010a9b052'
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":false to indicate that the user consented (opted in) to share their information.
- Pass "limit_data_sharing":true if the user refused.
Singular uses limit_data_sharing in "User Privacy Postbacks" as well as passing this information on to partners who require it in order to comply with relevant regulations. See "User Privacy and Limit Data Sharing" for more information.
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= |
Retrieving App Data
Required iOS Identifiers
- The IDFV is required on all S2S requests
- The IDFA should be provided if App Tracking Transparency Consent was provided by the user.
- The ATT Authorization Status is also required.
Retrieving the Identifier for Advertisers (IDFA)
The Identifier for Advertisers (IDFA) helps advertisers track and attribute user actions (e.g., ad clicks, app installs) to specific campaigns, enabling precise ad targeting and campaign optimization.
Starting with iOS 14.5, users must opt-in via the App Tracking Transparency (ATT) framework before apps can access their IDFA. If users do not opt-in, the IDFA is unavailable, limiting tracking capabilities.
Retrieving the Identifier for Vendors (IDFV)
The Identifier for Vendors (IDFV) is a unique identifier assigned by Apple to a device, which is specific to a particular vendor or developer. It remains consistent across all apps from the same vendor on a given device, allowing the vendor to track user behavior and interactions across their app ecosystem without identifying the user personally.
- Ensure the ATT prompt is shown and handled before attempting to access the IDFA.
- If you are using ATT, capture the IDFA and pass it back to your server for use on Singular API requests.
- Capture the IDFV and pass it back to your server for use on Singular API requests.
Example: Requesting ATT Authorization Status to Retrieve IDFA and IDFV with Console Logs
Here is the Objective-C and Swift code snippets to retrieve both the IDFA (Identifier for Advertisers) and IDFV (Identifier for Vendor) with console logs. This code includes handling App Tracking Transparency (ATT) permissions, which are required to access the IDFA starting from iOS 14.5.
#import <AdSupport/AdSupport.h>
#import <AppTrackingTransparency/AppTrackingTransparency.h>
#import <UIKit/UIKit.h>
- (void)retrieveIdentifiers {
// Request ATT authorization (iOS 14.5+)
[ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
dispatch_async(dispatch_get_main_queue(), ^{
switch (status) {
case ATTrackingManagerAuthorizationStatusAuthorized: {
// ATT authorized, retrieve IDFA
NSUUID *idfa = [[ASIdentifierManager sharedManager] advertisingIdentifier];
NSLog(@"IDFA: %@", [idfa UUIDString]);
break;
}
case ATTrackingManagerAuthorizationStatusDenied:
case ATTrackingManagerAuthorizationStatusRestricted:
case ATTrackingManagerAuthorizationStatusNotDetermined:
// ATT not authorized or not determined
NSLog(@"Tracking not authorized or not determined.");
break;
default:
NSLog(@"Unknown ATT status.");
break;
}
// Retrieve IDFV (always available)
NSUUID *idfv = [[UIDevice currentDevice] identifierForVendor];
if (idfv != nil) {
NSLog(@"IDFV: %@", [idfv UUIDString]);
} else {
NSLog(@"Unable to retrieve IDFV.");
}
});
}];
}
// Call the method to retrieve identifiers
[self retrieveIdentifiers];
import AdSupport
import AppTrackingTransparency
import UIKit
func retrieveIdentifiers() {
// Request ATT authorization (iOS 14.5+)
ATTrackingManager.requestTrackingAuthorization { status in
DispatchQueue.main.async {
switch status {
case .authorized:
// ATT authorized, retrieve IDFA
let idfa = ASIdentifierManager.shared().advertisingIdentifier.uuidString
print("IDFA: \(idfa)")
case .denied, .restricted, .notDetermined:
// ATT not authorized or not determined
print("Tracking not authorized or not determined.")
@unknown default:
print("Unknown ATT status.")
}
// Retrieve IDFV (always available)
if let idfv = UIDevice.current.identifierForVendor?.uuidString {
print("IDFV: \(idfv)")
} else {
print("Unable to retrieve IDFV.")
}
}
}
}
// Call the function to retrieve identifiers
retrieveIdentifiers()
- IDFA: Requires ATT authorization from iOS 14.5+. Without user consent, the IDFA will return all zeros.
- IDFV: Always available and should should always be provided on Singular API Requests.
Required Android Identifiers (Google Play Devices)
- The ASID is required on all S2S requests
- The AIFA (GAID) should be provided if available.
Retrieving the Google Advertising Identifier (GAID)
The Google Advertising Identifier (GAID), also known as AIFA in Singular or Android Advertising ID (AAID), is a unique, user-resettable identifier assigned to Android devices. It helps advertisers and app developers track and attribute user actions (e.g., ad clicks, app installs) across apps to specific campaigns, enabling precise ad targeting and campaign optimization, while maintaining user privacy.
Make sure you have added the required dependency for Google Play Services in your build.gradle file:
dependencies {
implementation 'com.google.android.gms:play-services-ads-identifier:17.0.0'
}
Usage
To use this method, simply call it from your activity or application class:
AdIdUtils.getGoogleAdId(getApplicationContext());
Java Code to Retrieve Google Advertising ID (GAID)
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import com.google.android.gms.ads.identifier.AdvertisingIdClient;
import com.google.android.gms.ads.identifier.AdvertisingIdClient.Info;
public class AdIdUtils {
// Method to retrieve the Google Advertising ID (GAID)
public static void getGoogleAdId(Context context) {
// Running the task in a background thread
AsyncTask.execute(new Runnable() {
@Override
public void run() {
try {
// Retrieve the Advertising ID info
Info adInfo = AdvertisingIdClient.getAdvertisingIdInfo(context);
// Get the GAID (Google Advertising ID)
String adId = adInfo.getId();
// Check if "Limit Ad Tracking" is enabled by the user
boolean isLimitAdTrackingEnabled = adInfo.isLimitAdTrackingEnabled();
// Log the results
Log.d("GoogleAdID", "Advertising ID: " + adId);
Log.d("GoogleAdID", "Limit Ad Tracking Enabled: " + isLimitAdTrackingEnabled);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
Make sure you have added the required dependency for Google Play Services in your build.gradle file:
dependencies {
implementation 'com.google.android.gms:play-services-ads-identifier:17.0.0'
}
Usage
To use this function, you need to call it from a coroutine scope, such as within a ViewModel or an activity using lifecycleScope. Here’s an example of how you might call it in an activity:
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Launch a coroutine to fetch the Google Ad ID
lifecycleScope.launch {
val googleAdId = AdIdUtils.getGoogleAdId(applicationContext)
Log.d("MainActivity", "Retrieved Google Ad ID: $googleAdId")
}
}
}
Java Code to Retrieve Google Advertising ID (GAID)
import android.content.Context
import android.util.Log
import com.google.android.gms.ads.identifier.AdvertisingIdClient
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
object AdIdUtils {
// Function to retrieve Google Advertising ID (GAID) using coroutines
suspend fun getGoogleAdId(context: Context): String? {
return withContext(Dispatchers.IO) {
try {
// Retrieve the Advertising ID info
val adInfo = AdvertisingIdClient.getAdvertisingIdInfo(context)
// Get the GAID (Google Advertising ID)
val adId = adInfo.id
// Check if "Limit Ad Tracking" is enabled by the user
val isLimitAdTrackingEnabled = adInfo.isLimitAdTrackingEnabled
// Log the results
Log.d("GoogleAdID", "Advertising ID: $adId")
Log.d("GoogleAdID", "Limit Ad Tracking Enabled: $isLimitAdTrackingEnabled")
adId
} catch (e: Exception) {
e.printStackTrace()
null
}
}
}
}
Retrieving the App Set ID (ASID)
The Android App Set ID provides a way for developers to track users across their own apps in a privacy-conscious manner. It is particularly useful for analytics and fraud prevention but cannot be used for advertising purposes such as personalized ads or measurement.
Make sure you have added the required dependency for Google Play Services in your build.gradle file:
dependencies {
implementation 'com.google.android.gms:play-services-appset:16.1.0'
}
Usage
To use this function, simply call it from your activity or application class:
AppSetIdUtils.getAppSetId(getApplicationContext());
Java Code to Retrieve App Set ID
import android.content.Context;
import android.util.Log;
import com.google.android.gms.appset.AppSet;
import com.google.android.gms.appset.AppSetIdClient;
import com.google.android.gms.appset.AppSetIdInfo;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
public class AppSetIdUtils {
// Method to retrieve the App Set ID
public static void getAppSetId(Context context) {
// Get the AppSetIdClient instance
AppSetIdClient client = AppSet.getClient(context);
// Retrieve the App Set ID information asynchronously
Task<AppSetIdInfo> task = client.getAppSetIdInfo();
task.addOnSuccessListener(new OnSuccessListener<AppSetIdInfo>() {
@Override
public void onSuccess(AppSetIdInfo info) {
// Get the App Set ID and its scope
String appSetId = info.getId();
int scope = info.getScope();
// Log the results
Log.d("AppSetID", "App Set ID: " + appSetId);
Log.d("AppSetID", "Scope: " + (scope == AppSetIdInfo.SCOPE_DEVELOPER ? "Developer" : "App"));
}
}).addOnFailureListener(e -> {
// Handle any errors that occur during retrieval
Log.e("AppSetID", "Failed to retrieve App Set ID", e);
});
}
}
Ensure you have added the necessary dependency for Google Play services in your build.gradle file:
dependencies {
implementation 'com.google.android.gms:play-services-appset:16.1.0'
}
Usage
To use this function, simply call it from your activity or application class:
AppSetIdUtils.getAppSetId(applicationContext)
Kotlin Code to Retrieve App Set ID
import android.content.Context
import android.util.Log
import com.google.android.gms.appset.AppSet
import com.google.android.gms.appset.AppSetIdClient
import com.google.android.gms.tasks.Task
object AppSetIdUtils {
// Function to retrieve the App Set ID
fun getAppSetId(context: Context) {
// Get the AppSetIdClient instance
val client: AppSetIdClient = AppSet.getClient(context)
// Retrieve the App Set ID information asynchronously
val task: Task<AppSetIdInfo> = client.appSetIdInfo
task.addOnSuccessListener { info ->
// Get the App Set ID and its scope
val appSetId: String = info.id
val scope: Int = info.scope
// Log the results
Log.d("AppSetID", "App Set ID: $appSetId")
Log.d("AppSetID", "Scope: ${if (scope == AppSetIdInfo.SCOPE_DEVELOPER) "Developer" else "App"}")
}.addOnFailureListener { exception ->
// Handle any errors that occur during retrieval
Log.e("AppSetID", "Failed to retrieve App Set ID", exception)
}
}
}
Required Android Identifiers (Non-Google Play Devices)
If no ASID or AIFA are available:
- The AMID (Amazon Identifier) should be provided. Amazon Devices Only.
Retrieving the Amazon ID (AMID)
To capture the Amazon Advertising Identifier in your Android app, you can use the Settings.Secure class to retrieve the identifier and check the user's ad tracking preferences. Here's a clean and simple code example based on the information provided by Amazon's documentation:
Make sure to handle scenarios where the Advertising ID might not be available (e.g., on non-Fire OS devices or older versions of Fire OS).
Always respect the user's Limit Ad Tracking preference when using this identifier for advertising or analytics purposes. This code should work on Amazon Fire devices running Fire OS 5.1 or later, as per Amazon's guidelines
You can call this method from your activity or service by passing in the ContentResolver:
// Example usage in an Activity
AdvertisingIdHelper.getAmazonAdvertisingId(getContentResolver());
Java Code to Retrieve the Amazon Advertising ID (AMID)
import android.content.ContentResolver;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.util.Log;
public class AdvertisingIdHelper {
public static void getAmazonAdvertisingId(ContentResolver contentResolver) {
String advertisingID = "";
boolean limitAdTracking = false;
try {
// Get user's ad tracking preference
limitAdTracking = (Settings.Secure.getInt(contentResolver, "limit_ad_tracking") == 0) ? false : true;
// Get the Amazon Advertising ID
advertisingID = Settings.Secure.getString(contentResolver, "advertising_id");
// Log the values for demonstration purposes
Log.d("AdvertisingID", "Amazon Advertising ID: " + advertisingID);
Log.d("LimitAdTracking", "Limit Ad Tracking: " + limitAdTracking);
} catch (SettingNotFoundException e) {
// Handle case where settings are not available (e.g., non-Fire OS devices)
Log.e("AdvertisingID", "Advertising ID not supported on this device", e);
}
}
}
You can call this method from an Activity or Service by passing in the ContentResolver:
// Example usage in an Activity
AdvertisingIdHelper.getAmazonAdvertisingId(contentResolver)
Kotlin Code to Retrieve the Amazon Advertising ID (AMID)
import android.content.ContentResolver
import android.provider.Settings
import android.util.Log
object AdvertisingIdHelper {
fun getAmazonAdvertisingId(contentResolver: ContentResolver) {
try {
// Get user's ad tracking preference
val limitAdTracking = Settings.Secure.getInt(contentResolver, "limit_ad_tracking") != 0
// Get the Amazon Advertising ID
val advertisingID = Settings.Secure.getString(contentResolver, "advertising_id")
// Log the values for demonstration purposes
Log.d("AdvertisingID", "Amazon Advertising ID: $advertisingID")
Log.d("LimitAdTracking", "Limit Ad Tracking: $limitAdTracking")
} catch (e: Settings.SettingNotFoundException) {
// Handle case where settings are not available (e.g., non-Fire OS devices)
Log.e("AdvertisingID", "Advertising ID not supported on this device", e)
}
}
}
- The OAID (Open Advertising Identifier) should be provided. China domestic devices.
Retrieving the Open Advertising ID (OAID)
The Open Advertising Identifier (OAID) is a unique, anonymous identifier used for advertising purposes on Android devices, particularly those manufactured in China. It was introduced by the Mobile Security Alliance (MSA) as an alternative to Google's Advertising ID (GAID) for devices where Google Play Services are unavailable or not supported, such as in the Chinese market.
The OAID is primarily used for advertising attribution and user tracking in environments where Google Play Services are restricted, allowing advertisers and developers to track user behavior while maintaining anonymity.
OAID is available on most Chinese-manufactured Android devices, including those from brands like Huawei, Xiaomi, and others. It can be accessed using the MSA SDK or Huawei Mobile Services (HMS).
Ensure your build.gradle file includes the necessary dependencies for the MSA SDK.
dependencies {
implementation 'com.bun.msa.sdk:msa:1.0.26' // Example version; check for the latest one
}
Java Code to Retrieve OAID
import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
import com.bun.msa.sdk.DeviceId;
import com.bun.msa.sdk.DeviceIdSupplier;
import com.bun.msa.sdk.IIdentifierListener;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "OAIDExample";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initialize and request OAID
getOAID();
}
private void getOAID() {
try {
// Create a DeviceId instance and request OAID asynchronously
DeviceId deviceId = new DeviceId(this);
deviceId.getDeviceIds(new IIdentifierListener() {
@Override
public void onSupport(boolean isSupport, DeviceIdSupplier supplier) {
if (isSupport && supplier != null) {
// Retrieve OAID from supplier
String oaid = supplier.getOAID();
Log.d(TAG, "OAID: " + oaid);
} else {
Log.e(TAG, "OAID not supported on this device");
}
}
});
} catch (Exception e) {
Log.e(TAG, "Error retrieving OAID", e);
}
}
}
Add the MSA SDK to your project: Similar to the Java version, you need to integrate the MSA SDK into your project. Ensure you have the correct dependency in your build.gradle file:
dependencies {
implementation 'com.bun.msa.sdk:msa:1.0.26' // Example version; check for the latest one
}
Kotlin Code to Retrieve OAID
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.bun.msa.sdk.DeviceId
import com.bun.msa.sdk.DeviceIdSupplier
import com.bun.msa.sdk.IIdentifierListener
class MainActivity : AppCompatActivity() {
companion object {
private const val TAG = "OAIDExample"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Initialize and request OAID
getOAID()
}
private fun getOAID() {
try {
val deviceId = DeviceId(this)
deviceId.getDeviceIds(object : IIdentifierListener {
override fun onSupport(isSupport: Boolean, supplier: DeviceIdSupplier?) {
if (isSupport && supplier != null) {
// Retrieve OAID from supplier
val oaid = supplier.oAID
Log.d(TAG, "OAID: $oaid")
} else {
Log.e(TAG, "OAID not supported on this device")
}
}
})
} catch (e: Exception) {
Log.e(TAG, "Error retrieving OAID", e)
}
}
}
- If no other device identifier exists, then you may provide the ANDI (Android ID).
Retrieving the Android ID (ANDI)
The Android ID is a unique 64-bit identifier generated by the Android operating system when a device is first set up. It is designed to be persistent across the lifetime of the device, but it can be reset under certain conditions such as a factory reset.
The Android ID is unique to each device and, starting from Android 8.0 (Oreo), it is scoped per app and per user. This means that different apps on the same device will receive different Android IDs unless they share the same signing key.
The Android ID remains constant unless the device undergoes a factory reset or if an app is uninstalled and reinstalled after an OTA (over-the-air) update.
To retrieve the Android ID in an Android app, you can use the following code snippet:
import android.provider.Settings;
import android.content.Context;
String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
You can call this method from an Activity or Service by passing in the ContentResolver:
import android.os.Bundle
import android.provider.Settings
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Find the TextView in the layout to display the Android ID
val textView: TextView = findViewById(R.id.textView)
// Retrieve the Android ID
val androidId: String = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
}
}
Required Web Identifiers
- The SDID is required on all S2S requests
Retrieving the Singular Web SDK Device ID (SDID)
Description
You must have the Singular Web SDK implemented.
UsageTo use this method, simply call it from your web site code after the Singular SDK has Initialized:
window.singularSdk.getSingularDeviceId()
Required Mobile Device Params
- The Locale, Make, Model, and Build are required on all S2S requests
Retrieving Locale, Device Model, and Build for Android
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <sys/sysctl.h>
// Retrieve the Locale
NSString *retrieveLocale() {
NSString *locale = [[NSLocale currentLocale] localeIdentifier];
NSLog(@"Locale: %@", locale);
return locale;
}
// Retrieve the Device Model
NSString *deviceModel() {
size_t bufferSize = 64;
char model[bufferSize];
int status = sysctlbyname("hw.machine", model, &bufferSize, NULL, 0);
if (status == 0) {
NSString *deviceModel = [NSString stringWithCString:model encoding:NSUTF8StringEncoding];
NSLog(@"Device Model: %@", deviceModel);
return deviceModel;
} else {
NSLog(@"Unable to retrieve device model.");
return nil;
}
}
// Retrieve the Build Version
NSString *buildVersion() {
size_t bufferSize = 64;
char build[bufferSize];
int status = sysctlbyname("kern.osversion", build, &bufferSize, NULL, 0);
if (status == 0) {
NSString *buildVersion = [NSString stringWithCString:build encoding:NSUTF8StringEncoding];
NSLog(@"Build Version: %@", buildVersion);
return buildVersion;
} else {
NSLog(@"Unable to retrieve build version.");
return nil;
}
}
// Example usage
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Retrieve Locale
NSString *locale = retrieveLocale();
// Retrieve Device Model
NSString *model = deviceModel();
// Retrieve Build Version
NSString *build = buildVersion();
// Log results
if (locale) {
NSLog(@"Locale: %@", locale);
}
if (model) {
NSLog(@"Device Model: %@", model);
}
if (build) {
NSLog(@"Build Version: %@", build);
}
}
return 0;
}
import Foundation
// Retrieve the Locale
func retrieveLocale() -> String {
let locale = Locale.current.identifier
print("Locale: \(locale)")
return locale
}
// Retrieve the Device Model
import UIKit
func deviceModel() -> String? {
var systemInfo = utsname()
uname(&systemInfo)
let machineMirror = Mirror(reflecting: systemInfo.machine)
let identifier = machineMirror.children.reduce("") { identifier, element in
guard let value = element.value as? Int8, value != 0 else { return identifier }
return identifier + String(UnicodeScalar(UInt8(value)))
}
print("Device Model: \(identifier)")
return identifier
}
// Retrieve the Build Version
func buildVersion() -> String? {
var size: Int = 0
sysctlbyname("kern.osversion", nil, &size, nil, 0)
var build = [CChar](repeating: 0, count: size)
sysctlbyname("kern.osversion", &build, &size, nil, 0)
let buildVersion = String(cString: build)
print("Build Version: \(buildVersion)")
return buildVersion
}
// Example usage:
let locale = retrieveLocale()
if let model = deviceModel() {
print("Device Model: \(model)")
}
if let build = buildVersion() {
print("Build Version: \(build)")
}
// Locale - lc= query parameter
String locale = Locale.getDefault().toString(); // Converts Locale object to string
// Model - mo= query parameter
String device = Build.MODEL; // Gets the device model
// Build - bd= query parameter
String build = "Build/" + Build.ID; // Gets the build ID and appends it to "Build/"
// Locale - lc= query parameter
val locale: String = Locale.getDefault().toString() // Converts Locale object to string
// Model - mo= query parameter
val device: String = Build.MODEL // Gets the device model
// Build - bd= query parameter
val build: String = "Build/" + Build.ID // Gets the build ID and appends it to "Build/"
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:
// ReceiptManager.h
@interface ReceiptManager : NSObject
+ (nullable NSString *)getInstallReceipt;
@end
// ReceiptManager.m
#import <StoreKit/StoreKit.h>
@implementation ReceiptManager
+ (nullable NSString *)getInstallReceipt {
if (@available(iOS 18.0, *)) {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block NSString *result = nil;
[SKPaymentQueue.defaultQueue addTransactionObserver:[[SKPaymentTransactionObserver alloc] init]];
if (@available(iOS 18.0, *)) {
[AppTransaction.shared fetchWithCompletionHandler:^(AppTransaction *transaction, NSError *error) {
if (error) {
NSLog(@"Failed to get app transaction: %@", error.localizedDescription);
} else {
result = transaction.jwsRepresentation;
}
dispatch_semaphore_signal(semaphore);
}];
}
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return result;
} else {
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
if (!receiptURL) {
NSLog(@"Receipt URL not found");
return nil;
}
NSError *error = nil;
NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL
options:NSDataReadingUncached
error:&error];
if (error) {
NSLog(@"Failed to read receipt: %@", error.localizedDescription);
return nil;
}
return [receiptData base64EncodedStringWithOptions:0];
}
}
@end
import Foundation
import StoreKit
class ReceiptManager {
static func getInstallReceipt() -> String? {
if #available(iOS 18.0, *) {
// Synchronous wrapper for async code
let semaphore = DispatchSemaphore(value: 0)
var result: String?
Task {
do {
let transaction = try await AppTransaction.shared
result = transaction.jwsRepresentation
semaphore.signal()
} catch {
debugPrint("Failed to get app transaction: \(error.localizedDescription)")
semaphore.signal()
}
}
semaphore.wait()
return result
} else {
// Legacy receipt fetching
guard let receiptURL = Bundle.main.appStoreReceiptURL else {
debugPrint("Receipt URL not found")
return nil
}
do {
let receiptData = try Data(contentsOf: receiptURL, options: .uncached)
return receiptData.base64EncodedString(options: [])
} catch {
debugPrint("Failed to read receipt: \(error.localizedDescription)")
return nil
}
}
}
}