Server-to-Server - Integration Guide
Implement Singular's REST API for complete server-side tracking as an alternative to SDK integration, enabling full control over data collection, transmission, and attribution workflows.
Overview
Server-to-Server Use Case
Server-to-server (S2S) integration provides REST API endpoints for building complete attribution and analytics solutions running from your backend infrastructure without embedding Singular SDK in client applications.
Integration Approaches:
- Pure S2S: 100% server-side implementation handling both session and event tracking
- Hybrid: Singular SDK manages sessions while server-side handles event tracking
Hybrid Integration Pattern
Hybrid integration combines Singular SDK for session management with server-side EVENT API for backend event tracking, balancing ease of implementation with server-side flexibility.
Hybrid Benefits:
- SDK handles complex session logic, deep linking, and device data collection automatically
- Server sends events for transactions processed in backend systems
- Reduced client-side implementation complexity
- SESSION endpoint not required—SDK manages session lifecycle
Device Data Retrieval Methods:
- Client-Managed Flow: Capture required data points on client and forward to server via internal API for use with Singular EVENT endpoint
- Internal BI Postback: Configure Singular Internal BI postback to receive real-time JSON payload with device identifiers after install, re-engagement, or events ( Setup Guide )
Device Graph Maintenance: Both methods require server-side logic to maintain device graph. When SDK detects device identifier changes, update server accordingly to ensure accurate tracking.
Implementation Resources:
- EVENT Endpoint Required Parameters
- Retrieving Device Data Guide (iOS/Android code samples)
Key Integration Principles
| Principle | Description |
|---|---|
| Flexibility | Full control over data collection and transmission timing |
| Feature Parity | Supports all SDK functionality when proper data provided |
| Integration Path | Client → Your Server → Singular API |
| Real-time Processing | One request at a time—no batch processing support |
| Sequential Flow | Events must be processed chronologically |
| No Deduplication | Singular does not deduplicate—implement server-side deduplication |
| Data Permanence | Device-level data cannot be deleted after ingestion—validate before sending |
Integration Requirements
Pure S2S integration requires comprehensive data pipeline implementation for session and event tracking.
- Data Collection: Collect required data points from client applications
- Device Graph: Forward data to server and maintain device identifier storage
- Session Requests: Send session notifications via SESSION API
- Response Handling: Process and relay Singular responses back to client app
- Event Requests: Forward events via EVENT API
REST API Endpoints
Singular provides two primary REST API endpoints for server-to-server session and event tracking.
Session Endpoint
Session Tracking API
SESSION endpoint notifies Singular of app open events to initialize user sessions for attribution and retention tracking.
GET https://s2s.singular.net/api/v1/launch
Complete reference: SESSION Endpoint API Reference
Event Endpoint
Event Tracking API
EVENT endpoint tracks in-app events and revenue for attribution analysis and campaign optimization.
GET https://s2s.singular.net/api/v1/evt
Complete reference: EVENT Endpoint API Reference
Implementation Phases
Successful S2S integration requires four key implementation phases executed sequentially for optimal data quality and attribution accuracy.
Phase 1: Data Collection
Required Data Points
Establish robust data collection strategy capturing all required parameters for Singular platform functionality.
All Required Parameters Mandatory: Omitting required parameters results in data discrepancies and attribution errors. No parameters are optional.
Async Function Handling: When collecting client-side data for server transmission, wait for asynchronous functions to complete and handle edge cases. Common issue causing missing data and partial attribution.
Implementation Resources:
- SESSION Endpoint Required Parameters
- EVENT Endpoint Required Parameters
- Retrieving Device Data Guide (iOS/Android code samples)
- SKAdNetwork 4 Implementation Guide (iOS specific data points)
Phase 2: Real-Time Streaming
Critical Timing Requirements
Real-time data streaming maintains attribution accuracy and enables time-sensitive features like SKAdNetwork conversion value updates.
Attribution Impact:
- Delayed Sessions: Severely impact attribution accuracy—system requires precise temporal data for campaign association
- SKAdNetwork Timer: Strict on-device timer window for conversion values makes real-time streaming critical. Delays cause missed conversion value updates and incomplete campaign data
Best Practices:
- Implement server-side event listeners for app session starts
- Forward session data immediately with all required parameters
- Implement server-side event listeners for in-app events
- Forward event data immediately with all required parameters
- Use webhook architecture for reliable data transmission
- Implement retry mechanisms for failed requests
- Monitor data flow for quality assurance
Phase 3: Response Handling
Bidirectional Communication
Response handling bridges server-side API interactions with client-side functionality, enabling deferred deep linking and conversion value updates.
Key Response Types:
- Deferred Deep Links: API response contains pending deep link data requiring immediate relay to app for user routing and personalization
- Conversion Values: iOS SKAdNetwork conversion values must be promptly forwarded to app for accurate campaign measurement
Best Practices:
- Implement response handling on server infrastructure
- Parse and validate Singular API responses
- Forward relevant response data to client app (essential for iOS SKAdNetwork)
- Implement client-side response processing
- Handle errors gracefully with proper HTTP status codes
- Log failed responses for retry mechanisms
Phase 4: Testing & Validation
Data Flow Verification
Testing phase validates complete data pipeline functionality and attribution accuracy before production deployment.
Session Attribution Processes:
- First Session (New Install): Singular recognizes new install and triggers install attribution process
- Re-engagement Qualified: Singular triggers re-engagement attribution process ( Re-engagement FAQ )
- Standard Session: Singular records session for user activity and retention metrics
Critical Timing Requirements:
- Session Before Events: Single SESSION must be received before any events. SDK triggers session on app open, then sends in-app events. After 1+ minute background, session times out. New session sent when app returns to foreground. Use app lifecycle events and timers for session management
- Real-Time Events: Events occurring in app must be sent real-time after their respective session
Validation Checklist:
- Test session data flow—validate first and subsequent sessions have correct data points and values
- Confirm events received only after session reported to Singular (events before session create organic attribution)
- Confirm session response handled and passed to client app (critical for deferred deep links)
Integration Complete:
- ✓ Data collection and storage validated
- ✓ Real-time streaming to Singular validated
- ✓ Response handling and logging validated
- ✓ All testing data flows validated
Testing guide: S2S Integration Testing Guide
Advanced Features
Enhance S2S integration with advanced attribution handling, deep linking, cross-device tracking, and platform-specific capabilities.
Apple Search Ads Attribution
iOS Search Ads Integration
Apple Search Ads is a Self-Attributing Network (SAN) requiring platform-specific attribution implementation via Apple frameworks.
Framework Support:
- iOS 14.2 and below: iAd framework
- iOS 14.3 and above: AdServices framework (recommended)
Dual Implementation: Implement both iAd and AdServices frameworks until iAd deprecated. Singular prioritizes AdServices over iAd signals for attribution and reporting.
Complete guide: Apple Search Ads Integration Documentation
iAd Framework Implementation
Retrieve and send Apple Search Ads attribution data via iAd framework for iOS 14.2 and below.
Step 1: Retrieve Attribution Data
#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
}
}];
}
}
Latency Handling: Users clicking Search Ads may download and open app immediately. Prevent attribution timing issues by:
- Setting few-second delay before retrieving attribution data
- Implementing retry logic if response False or error code (0, 2, 3). Retry 2 seconds later
Step 2: Send to Singular
Report event with reserved name
__iAd_Attribution__
via
EVENT endpoint
,
passing attribution JSON as
e
parameter.
Timing Requirements:
-
iOS 13+: Send
__iAd_Attribution__event immediately after first session post-install/reinstall. Otherwise Apple Search Ads data not considered for attribution -
iOS 14+: Attribution responses only available
under certain conditions
and unavailable if ATT status is
ATTrackingManager.AuthorizationStatus.denied
AdServices Framework Implementation
Retrieve and send attribution token via AdServices framework for iOS 14.3 and above.
Step 1: Retrieve Attribution Token
#import <AdServices/AdServices.h>
NSError *error = nil;
Class AAAttributionClass = NSClassFromString(@"AAAttribution");
if (AAAttributionClass) {
NSString *attributionToken = [AAAttributionClass attributionTokenWithError:&error];
if (!error && attributionToken) {
// Handle attributionToken
}
}
Token Characteristics:
- Generated on device
-
Cached for 5 minutes—new token generated after expiry if
attributionToken()called - Valid for 24 hours
Step 2: Send to Singular
URL-encode token and append to
SESSION endpoint
as
attribution_token
parameter on first session after every
install and reinstall.
Google Play Install Referrer
Android Install Attribution
Google Play Install Referrer provides most accurate Android install attribution, containing information about user origin before Play Store arrival.
Required For:
- Facebook data in User-Level Exports
- Sharing with Data Destinations
- Sending postbacks
More info: Google Install Referrer Documentation
Implementation Steps:
- Retrieve install referrer on first app open using Play Install Referrer API
-
Report session via
SESSION endpoint
including
install_refJSON parameter with required attributes
install_ref Attributes:
| Attribute | Description |
|---|---|
referrer
|
Referrer value from Play Install Referrer API (JSON object—encode as string) |
referrer_source
|
Specify "service" |
clickTimestampSeconds
|
Click timestamp from API (e.g., "1550420123") |
installBeginTimestampSeconds
|
Install start time from API |
current_device_time
|
Current device time in milliseconds (e.g., "1550420454906") |
Meta Install Referrer
Facebook Attribution Enhancement
As of June 18, 2025: Meta's Advanced Mobile Measurement (AMM) removes need for Meta Install Referrer implementation. Not recommended if AMM enabled.
Meta Referrer is Android-specific measurement solution providing granular user-level attribution data for app installs, combining Google Play Install Referrer and Meta Install Referrer technologies.
Learn more: Meta Referrer FAQ
Implementation Steps:
- Retrieve Meta install referrer on first app open per Meta's documentation
-
Report session via
SESSION endpoint
including
meta_refJSON parameter with required attributes
meta_ref Attributes:
| Attribute | Description |
|---|---|
is_ct
|
is_ct from Meta Install Referrer (0 or 1) |
install_referrer
|
install_referrer from Meta Install Referrer |
actual_timestamp
|
actual_timestamp from Meta Install Referrer (e.g., 1693978124) |
Singular Links & Deep Linking
Implement deep linking and deferred deep linking for Singular tracking links so users move seamlessly from a campaign to a specific screen in your app.
A deep link is a clickable link that opens specific content inside an app. There are two scenarios to support:
- Deep link (direct): the user already has the app installed. They tap a Singular Link and the app opens directly on the right content.
- Deferred deep link (DDL): the user does not have the app yet. They tap the link, install from the store, and on first open the app still routes them to the intended content.
With an SDK integration, Singular handles most of this automatically. In a server-to-server (S2S) integration there is no SDK on the device, so your team is responsible for two extra jobs: passing the deep link your app received up to Singular, and reading the deferred deep link Singular returns and routing the user. Both flow through the SESSION endpoint:
GET https://s2s.singular.net/api/v1/launch
Resources: Deep Linking FAQ | Singular Links FAQ
The three configurations at a glance
| Step | What it does | Where it happens |
|---|---|---|
| 1. App level | Makes Singular Links open your app (iOS Universal Links / Android App Links) and defines the deep link destinations. | Apple/Google config + Singular dashboard (Apps & Manage Links) |
| 2. SESSION (inbound) | Singular receives the deep link result. Your app passes the opened link up via the openuri parameter. |
SESSION request → Singular |
| 3. SESSION (outbound) | The app receives the DDL result. Singular returns the deferred deep link in the response, and your server relays it to the app. | SESSION response → your server → app |
Step 1: App-level configuration
Before any deep link can work, Singular Links must be recognized by the OS as iOS Universal Links and Android App Links so that tapping a link opens your app instead of a browser. Traditional URI app schemes are supported as a fallback.
Follow the canonical setup guides for the detailed platform steps, then confirm each item below is complete. The detailed steps live in Singular Links Prerequisites and How to Configure Deep Links. The checklist here gives you the required order and the points teams most often miss.
Configuration checklist:
-
Add a link sub-domain in Attribution > Manage Links > Manage Link Domains (for example
yourbrand.sng.link). This is the host your Singular Links use. -
iOS — Universal Links: enable the Associated Domains capability for
applinks:yourbrand.sng.link, then copy your App Prefix / Team ID from the Apple Developer Portal into Settings > Apps > [iOS app] > Show Advanced Settings. Optionally register an app-scheme fallback as a URL Type in Xcode. -
Android — App Links: generate your SHA-256 signing fingerprints (Play Console for production,
keytoolfor debug), add anautoVerifyintent filter for your Singular domain toAndroidManifest.xml, then paste the fingerprint into Settings > Apps > [Android app] > Show Advanced Settings > App Links SHA256 fingerprints. - Define the deep links in Attribution > Manage Links > Create Link (see the field mapping below).
Important: On iOS, if the Team ID step is skipped, every Singular Link redirects to the App Store and deep links will not work. This is the most common reason a correctly built link fails to open the app.
Mapping the Manage Links redirect fields
When you create the link, three redirect fields map directly to the three deep link concepts used later in the API:
| Field in Manage Links | Meaning |
|---|---|
| If the app is not installed, go to | Fallback redirect, usually your App Store / Play Store page. |
| If the app is already installed, go to | The deep link, the in-app destination for existing users (Step 2). |
| After installation, go directly to | The deferred deep link, the destination for new users after install (Step 3). Usually the same value as the deep link. |
Developer handoff: engineering should provide the deep link scheme(s) per platform and the list of destination URLs (for example myapp://autumnfashion).
Step 2: SESSION (inbound) — send the deep link to Singular
When the app opens from a Singular Link, capture the full opening URL and send it to Singular on the SESSION request in the openuri parameter (URL-encoded). This is how Singular attributes the session to the link and reports the deep link result. Required when using Singular Links.
Always send SESSION on a deep-link open. Whenever the app is opened via a deep link, Universal Link, or App Link, send a SESSION request with openuri populated, even if the session would normally be considered still active (that is, regardless of the timeout window).
Singular Link parameters carried on the URL
| Parameter | Meaning |
|---|---|
_dl |
Deep link value (same destination for iOS and Android). |
_ios_dl / _android_dl
|
Platform-specific deep link values, when they differ per platform. |
_p |
Passthrough parameter, a URL-encoded JSON or plain string for custom routing. |
Capture the opening URL in the app
The app needs handler code that receives the opening URL and forwards it to your server for inclusion on the SESSION request.
iOS (Swift):
// Custom scheme / direct opens
func scene(_ scene: UIScene,
openURLContexts URLContexts: Set<UIOpenURLContext>) {
guard let url = URLContexts.first?.url else { return }
SessionReporter.report(openURL: url.absoluteString)
}
// Universal Links arrive via:
func scene(_ scene: UIScene,
continue userActivity: NSUserActivity) {
if let url = userActivity.webpageURL {
SessionReporter.report(openURL: url.absoluteString)
}
}
Android (Kotlin):
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
intent?.data?.let { uri ->
SessionReporter.report(uri.toString())
}
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
intent?.data?.let { SessionReporter.report(it.toString()) }
}
Send the SESSION request with openuri
From your server, include the captured URL (URL-encoded) as openuri:
curl -G https://s2s.singular.net/api/v1/launch \
--data-urlencode "a=<SDK key>" \
--data-urlencode "p=Android" \
--data-urlencode "i=com.yourcompany.app" \
--data-urlencode "aifa=<GAID>" \
--data-urlencode "n=YourAppName" \
--data-urlencode "openuri=myapp://home/page?queryparam1=value1"
Short link resolution
If the user opened a shortened Singular Link, also send singular_link_resolve_required=true. Singular returns the expanded long link so you can parse the deep link and passthrough values.
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"
}
Dynamic passthrough parameters
If your organization configured dynamic passthrough for a link, the deep link URL includes the _p parameter with a URL-encoded JSON string or unstructured string for content routing. See Dynamic Passthrough Parameters.
Reference: SESSION Endpoint API Reference (Deep Linking Parameters).
Step 3: SESSION (outbound) — relay the deferred deep link to the app
For a user who installs the app after tapping the link, the deferred deep link is returned by Singular in the SESSION response on the first session after install. Your server must relay that value back to the app so it can route the user.
Request the DDL on the first session
On the first SESSION request after an install, add these two parameters:
| Parameter | Purpose |
|---|---|
install=true |
Marks this as the first session after a fresh install. |
ddl_enabled=true |
Tells Singular the app expects a deferred deep link in the response. |
Sample SESSION request (install + DDL enabled):
curl -G https://s2s.singular.net/api/v1/launch \
--data-urlencode "a=<SDK key>" \
--data-urlencode "p=Android" \
--data-urlencode "i=com.yourcompany.app" \
--data-urlencode "aifa=<GAID>" \
--data-urlencode "n=YourAppName" \
--data-urlencode "install=true" \
--data-urlencode "ddl_enabled=true"
Read the response and relay it to the app
If the install came from a tracking link carrying a deferred deep link, Singular returns:
| Response field | What to do with it |
|---|---|
deferred_deeplink |
The deep link address. Relay to the app and route the user to the matching content. |
deferred_passthrough |
Any passthrough parameters attached to the link. Use for personalization. |
Sample SESSION response:
{
"deferred_deeplink": "myapp://deferred-deeplink",
"status": "ok",
"deferred_passthrough": "passthroughvalue"
}
Response handling is required. In a pure S2S setup Singular cannot talk to the device directly. Your backend must parse the SESSION response and forward deferred_deeplink (and deferred_passthrough) back to the client app, where your routing code sends the user to the right screen. If the response is not relayed, deferred deep linking will silently fail.
Testing & validation
- Deferred deep link: on a device without the app, tap the link and confirm you land on the store. Install and open, and the app should show the intended content.
- Direct deep link: with the app installed, tap the link and confirm the app opens directly on the intended content.
- SESSION ordering: confirm a single SESSION is received before any events, and that the SESSION response is parsed and passed to the client (critical for deferred deep links).
-
openuri populated: verify deep-link opens send a SESSION with
openuriset, regardless of the timeout window.
Testing guides: Test Your Integration | Test a Tracking Link
Additional Features
Implement cross-device tracking, revenue tracking, uninstall monitoring, and data privacy compliance for comprehensive analytics.
Cross-Device Tracking
Custom User ID Implementation
Leverage
custom_user_id
parameter to associate users with
device-level sessions for cross-device reporting and user-level analytics.
Privacy Compliance:
Adhere to data privacy policies
by avoiding personally identifiable information (PII) in
custom_user_id
. Use hashed username, email, or randomly
generated string as unique user identifier.
Enables comprehensive cross-device reporting, user-level data exports, and Internal BI postbacks while maintaining user privacy.
More info: Custom User ID Parameter
Revenue Tracking
In-App Purchase Reporting
Track revenue from in-app purchases for ROI analysis, campaign performance measurement, and export/postback enrichment.
Use EVENT endpoint with revenue parameters :
-
is_revenue_event: Marks event as revenue event (skip if event name is__iap__or amount > 0) -
purchase_receipt: Android/iOS In-App Purchase object—highly recommended for transaction details and report enrichment -
receipt_signature(Android): Highly recommended for transaction validation and fraud prevention -
amt: Revenue amount as Double (e.g., "amt=1.99") -
cur: ISO 4217 currency code (e.g., "cur=USD")
Implementation guide: Subscription State Management
Uninstall Tracking
Silent Push Notification Setup
Track uninstalls using device silent push notifications by sending push token with every session notification.
Setup Requirements:
- Follow platform-specific uninstall tracking setup:
-
Append platform-specific token to SESSION request:
-
iOS:
apns_token(APNs device token) -
Android:
fcm(FCM device token)
-
iOS:
iOS Install Receipt
Receipt Validation
Pass iOS install receipt in
install_receipt
parameter when reporting iOS session.
import Foundation
import StoreKit
class ReceiptManager {
static func getInstallReceipt() - String? {
if #available(iOS 18.0, *) {
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 {
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
}
}
}
}
Data Privacy Compliance
User Consent Handling
Notify Singular of end-user consent for data sharing to comply with GDPR, CCPA, and other privacy regulations.
Use
data_sharing_options
parameter to communicate user choice:
-
{"limit_data_sharing":false}: User consented (opted-in) to share information -
{"limit_data_sharing":true}: User refused to share information
Singular uses
limit_data_sharing
in
User Privacy Postbacks
and passes information to partners requiring compliance.
Optional but Recommended: Parameter optional, but some attribution information only shared by partners when user explicitly opted-in.
More info: User Privacy and Limit Data Sharing