Server-to-Server - Web S2S Implementation Guide

Overview

Web Server-to-Server (Web S2S) sends web conversion and engagement events to Singular directly from your server, using the same EVENT endpoint (V2) as the mobile integration. The web path is differentiated by the platform parameter p=Web .

Web S2S is a browser-assisted architecture . Several attribution-relevant values are only available in the browser — location.href , document.referrer , and the browser userAgent cannot be reconstructed on the backend. A thin client-side snippet must collect these values and either call the endpoint directly or forward them to your backend, which then makes the S2S call.

There is no web SESSION endpoint . The first __PAGE_VISIT__ conversion event replaces the mobile launch call and establishes the attribution baseline for the visit.

Web S2S shares authentication, encoding, and common-parameter conventions with the rest of the S2S suite. See the EVENT endpoint reference for authentication and common parameters, and the S2S Fundamentals hub for core S2S concepts.


Endpoint

Web S2S uses the same host, path, and POST method as the mobile V2 EVENT endpoint. Set p=Web to route the request through the web attribution path.

POST https://s2s.singular.net/api/v2/evt

V2 required as of July 15, 2026. Accounts created on or after July 15, 2026 must use Event Endpoint V2 (SDID-based); V1 is not available for new accounts. Existing customers already integrated on V1 are unaffected. Contact your Singular Customer Success Manager if you want to migrate to V2.

Required header:

Content-Type: application/x-www-form-urlencoded

SDID Generation and Persistence

The Singular device ID ( sdid ) ties all events from one browser to a single web device. On first page load, if no global_singular_id exists in localStorage , generate a UUID v4 and store it. Reuse the same value as sdid on every event from that browser until localStorage is cleared.

If the Web SDK is present on the page, reuse the value it already set in global_singular_id rather than generating a new one.

function getOrCreateSDID() {
  var KEY = 'global_singular_id';
  var sdid = localStorage.getItem(KEY);
  if (!sdid) {
    sdid = crypto.randomUUID(); // UUID v4
    localStorage.setItem(KEY, sdid);
  }
  return sdid;
}

The __PAGE_VISIT__ Pattern

The first event of any new visit MUST be __PAGE_VISIT__ with conversion_event=true . This establishes the attribution baseline that later events inherit.

Send a new __PAGE_VISIT__ with conversion_event=true whenever the same browser returns from a different campaign (different UTM or partner). Reuse the same sdid , and send the __PAGE_VISIT__ before any other events for that visit.

Non-conversion events (for example sng_registration_complete or sng_purchase ) use conversion_event=false and omit attribution_data . They inherit attribution from the most recent __PAGE_VISIT__ .

A non-conversion event that arrives before its conversion event is deferred one minute while the backend waits for a match.

custom_user_id rules

  • Include custom_user_id after registration or login, and on all subsequent events.
  • Persist it in localStorage or a first-party cookie.
  • Drop it on logout.
  • Never include PII. Use a hashed or internal ID.

Parameters

The Web SDK sends roughly 40 parameters on __PAGE_VISIT__ , but most are SDK-internal telemetry that S2S developers should not replicate. The tables below cover the required and recommended sets only.

Authentication ( a ) and other shared conventions are documented in the EVENT endpoint reference rather than re-documented here.

Required parameters

Parameter Details
a

Type: String
Singular API key from your dashboard.
Example: myapikey_123

p

Type: String
Platform. Always Web for Web S2S. This value routes the request through the web attribution path.
Example: Web

i

Type: String
Bundle ID of the web app/site in Singular. Must match the Web SDK Product ID. Find it under Settings > Apps > expand the web app > Bundle ID.
Example: com.example.site

n

Type: String
Event name. The first event of a visit must be __PAGE_VISIT__ .
Example: __PAGE_VISIT__

sdid

Type: UUIDv4
Singular device ID. Use the same value set by the Web SDK ( global_singular_id ). If setting it yourself, generate a UUID v4 and reuse it for all events from the browser.
Example: 3730aecb-47ba-4d13-bd17-52b4b700956f

conversion_event

Type: Boolean
Set true only for an attribution event ( __PAGE_VISIT__ ). A non-conversion event arriving before the conversion event is deferred one minute while the backend waits for a match.
Example: true

attribution_data

Type: JSON
Click-through data from ad networks, URL-encoded JSON. Required on __PAGE_VISIT__ ; omit on non-conversion events. See attribution_data .
Example: %7B%22partner_name%22%3A%22Snapchat%22%2C%22is_attributed%22%3A%22true%22%7D

custom_user_id

Type: String
Your internal user ID. No PII. Include after registration/login and on subsequent events; drop on logout.
Example: pid123abc

ip

Type: String
Device IP at the time of the event.
Example: 104.220.23.123

web_url

Type: String
The conversion landing page URL, including its marketing parameters. This is the last URL that contained marketing params, stored like the SDID and reused. It is used by the backend to create the attribution touchpoint. Distinct from web_page_url .
Example: http://my.landing.page/?utm_campaign=test&utm_source=test

Parameter Details
device_user_agent

Type: String
The browser user agent ( navigator.userAgent ). Browser-only; collect it client-side.
Example: Mozilla/5.0 ...

web_page_referrer

Type: String
The referrer of the current page ( document.referrer ). Ingested and used for attribution (organic source/type classification). Browser-only; collect it client-side. Distinct from document_referrer , which is event-logging only.
Example: https://www.google.com/

timezone

Type: String
The device timezone.
Example: America/New_York

os

Type: String
The device operating system.
Example: iOS

screen_width

Type: Integer
Screen width in pixels.
Example: 390

screen_height

Type: Integer
Screen height in pixels.
Example: 844

utime

Type: Integer
Event time in UNIX time (seconds).
Example: 1751500800

URL and referrer parameters — do not conflate. web_url and web_page_referrer are attribution-relevant and are used by the pipeline. web_page_url (the current page, window.location.href ) and document_referrer are event-logging only and are silently dropped for attribution. Do not send web_page_url or document_referrer expecting them to affect attribution.

For device identity and other shared parameters not listed above, see the EVENT endpoint reference rather than re-documenting them here.


attribution_data

The attribution_data parameter carries click-through data from ad networks as URL-encoded JSON. Send it on __PAGE_VISIT__ conversion events only.

{
  "partner_name": "Snapchat",
  "is_attributed": "true",
  "touch_timestamp": "",
  "partner_campaign_id": "",
  "partner_campaign_name": "Snap_campaign_1234",
  "partner_creative_id": "",
  "partner_creative_name": "",
  "partner_keyword": "",
  "partner_site": "",
  "partner_site_id": "",
  "partner_site_name": "",
  "partner_sub_site": "",
  "partner_sub_site_name": "",
  "partner_subcampaign_id": "",
  "partner_subcampaign_name": "",
  "_p": ""
}

Required fields

Field Details
partner_name

Type: String
Mapped from the web param wpsrc or utm_source .
Example: Snapchat

is_attributed

Type: String
Set true for attributed events.
Example: true

partner_campaign_name

Type: String
Mapped from the web param wpcn or utm_campaign .
Example: Snap_campaign_1234

Optional fields

Field Details
touch_timestamp

Type: Integer
Touchpoint time in UNIX seconds.
Example: 1751500800

partner_campaign_id

Type: String
Mapped from the web param wpcid .

partner_creative_id

Type: String
Mapped from the web param wpcrid .

partner_creative_name

Type: String
Mapped from the web param wpcrn .

partner_keyword

Type: String
Mapped from the web param wpkwn .

partner_site_id

Type: String
Mapped from the web param wpsid .

partner_site_name

Type: String
Mapped from the web param wpsn .

partner_sub_site_name

Type: String
Mapped from the web param wpssn .

partner_subcampaign_id

Type: String
Mapped from the web param wpscid .

partner_subcampaign_name

Type: String
Mapped from the web param wpscn .

Web click IDs in the e param

Partner Conversion API support: To forward these events to ad network partners through Singular's Conversion API integrations, include the standard optional event attributes (hashed first-party data such as eventId and ehash ). See Standard Event Attributes for Conversion API Integrations .

Pass network click IDs in the event e payload using the JSON key each partner expects:

Partner Click ID key(s)
Snapchat ScCid
TikTok ttclid
Google gclid , gbraid , wbraid
DV360 dclid
Facebook fbclid
Reddit rdt_uuid

Marketing-param detection. Treat a landing URL as a touchpoint when it contains any of: utm_* ; Singular wp* ( wpsrc , wpcn , wpcid , and similar); Singular pc* / p* ( pcid , pcn , psrc , and similar); clid ; or kw / an / ud . On a match, store the full URL as web_url and fire a conversion event.


Web-to-App Attribution

To carry web campaign context into a mobile click, build a Singular Link from your landing page and append the captured landing query string. This forwards the marketing parameters to the mobile click so the app install can be attributed.

On landing, capture window.location.search (plus fragment) when it contains marketing params and persist it. When rendering the app-store or smart-banner link, build:

function buildWebToAppLink(baseLink, webUrl, deeplink, passthrough, deferredDeeplink) {
  var url = new URL(baseLink); // preserves existing base-link params
  if (webUrl) {
    var q = new URL(webUrl);
    var captured = q.search.slice(1) + (q.hash || ''); // landing query string (+fragment)
    url.searchParams.set('_web_params', captured); // URL-encoded on serialization
  }
  if (deeplink)         url.searchParams.set('_dl', deeplink);
  if (passthrough)      url.searchParams.set('_p', passthrough);
  if (deferredDeeplink) url.searchParams.set('_ddl', deferredDeeplink);
  return url.toString();
}
Appended param Details
_web_params

URL-encoded query string (plus fragment) of the captured landing web_url . Forwards UTM / wp* / pc* / clid campaign params to the mobile click.

_dl

Deep link argument. Optional.

_p

Passthrough argument. Optional.

_ddl

Deferred deep link argument. Optional.

Accuracy: the SDID is not appended to the link (no _dsid or _sdid ). This is campaign-parameter forwarding, not deterministic SDID device-stitching.


Clipboard Attribution (Deferred Deep Link)

Clipboard attribution provides a deterministic match for a deferred deep link. The web snippet writes a unique token to the clipboard; the freshly installed app reads it back and matches on token equality.

function openAppWithClipboardDdl(baseLink, deeplink, passthrough, deferredDeeplink) {
  // 1. Build the web-to-app link (see Web-to-App Attribution).
  var link = buildWebToAppLink(baseLink, capturedWebUrl, deeplink, passthrough, deferredDeeplink);

  // 2. Generate a UUID v4 and form the token.
  var uuid = crypto.randomUUID();
  var tokenUrl = location.protocol + '//' + location.hostname + '/__singular_ddl__/' + uuid;

  // 3. Write ONLY the token to the clipboard (execCommand fallback).
  copyToClipboard(tokenUrl);

  // 4. Append the token to the click as ecid.
  var url = new URL(link);
  url.searchParams.set('ecid', tokenUrl);

  // 5. Open the app / store.
  window.open(url.toString());
}

function copyToClipboard(text) {
  if (navigator.clipboard && navigator.clipboard.writeText) {
    navigator.clipboard.writeText(text);
    return;
  }
  var ta = document.createElement('textarea');
  ta.value = text;
  document.body.appendChild(ta);
  ta.select();
  document.execCommand('copy');
  document.body.removeChild(ta);
}

Accuracy: the clipboard holds only the token — not the SDID, not _web_params , and not the deep link. The match is token equality ( ecid on the click equals the clipboard token), so the freshly installed app matches deterministically.

The clipboard write must run inside a user-gesture handler (a click) or browsers block it. Note iOS Safari clipboard constraints.

Reading the clipboard on first launch is the app side's concern (its own mobile SDK/S2S integration), not this web snippet.


Event Sequence Examples

First visit and registration

  1. User arrives from Google Ads. Send the initial page visit:

    • n=__PAGE_VISIT__
    • conversion_event=true
    • sdid=3730aecb-47ba-4d13-bd17-52b4b700956f
    • attribution_data={ "partner_name": "googleadwords_int", "is_attributed": "true", "partner_campaign_name": "brand_us_en" }
  2. User registers. Send the registration event:

    • n=sng_registration_complete
    • conversion_event=false
    • sdid=3730aecb-47ba-4d13-bd17-52b4b700956f (same SDID)
    • custom_user_id=user_12345abc
    • No attribution_data

Returning user from a new campaign

  1. The same browser returns from a different campaign. Send a new page visit:

    • n=__PAGE_VISIT__
    • conversion_event=true
    • sdid=3730aecb-47ba-4d13-bd17-52b4b700956f (same SDID)
    • attribution_data={ "partner_name": "facebook", "is_attributed": "true", "partner_campaign_name": "retargeting_Q1" }
  2. User purchases. Send the purchase event:

    • n=sng_purchase
    • conversion_event=false
    • sdid=3730aecb-47ba-4d13-bd17-52b4b700956f (same SDID)
    • Persisted custom_user_id
    • No attribution_data