Server-to-Server - Integration Guide

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

Hybrid S2S Data Flow

Device Data Retrieval Methods:

  1. Client-Managed Flow: Capture required data points on client and forward to server via internal API for use with Singular EVENT endpoint
  2. 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:


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.

  1. Data Collection: Collect required data points from client applications
  2. Device Graph: Forward data to server and maintain device identifier storage
  3. Session Requests: Send session notifications via SESSION API
  4. Response Handling: Process and relay Singular responses back to client app
  5. 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:


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:

  1. 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
  2. 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:

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

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
            }
        }];
    }
}

Latency Handling: Users clicking Search Ads may download and open app immediately. Prevent attribution timing issues by:

  1. Setting few-second delay before retrieving attribution data
  2. 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

Objective-C
#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:

More info: Google Install Referrer Documentation

Implementation Steps:

  1. Retrieve install referrer on first app open using Play Install Referrer API
  2. Report session via SESSION endpoint including install_ref JSON 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:

  1. Retrieve Meta install referrer on first app open per Meta's documentation
  2. Report session via SESSION endpoint including meta_ref JSON 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:

  1. 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.
  2. 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.
  3. Android — App Links: generate your SHA-256 signing fingerprints (Play Console for production, keytool for debug), add an autoVerify intent filter for your Singular domain to AndroidManifest.xml, then paste the fingerprint into Settings > Apps > [Android app] > Show Advanced Settings > App Links SHA256 fingerprints.
  4. 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 openuri set, 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:

  1. Follow platform-specific uninstall tracking setup:
  2. Append platform-specific token to SESSION request:

iOS Install Receipt

Receipt Validation

Pass iOS install receipt in install_receipt parameter when reporting iOS session.

Swift
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