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 as the mobile V1 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 S2S Fundamentals hub and the EVENT endpoint reference.


Endpoint

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

GET https://s2s.singular.net/api/v1/evt

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 once in the hub. See Common Parameters in S2S Fundamentals.

Required parameters

Parameter Details
a

string

Singular API key from your dashboard. See Common Parameters.

Example: myapikey_123

p

string

Platform. Always Web for Web S2S. This value routes the request through the web attribution path.

Example: Web

i

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

string

Event name. The first event of a visit must be __PAGE_VISIT__.

Example: __PAGE_VISIT__

sdid

UUID

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

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

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

string

Your internal user ID. No PII. Include after registration/login and on subsequent events; drop on logout.

Example: pid123abc

ip

string

Device IP at the time of the event.

Example: 104.220.23.123

web_url

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

string

The browser user agent (navigator.userAgent). Browser-only; collect it client-side.

Example: Mozilla/5.0 ...

web_page_referrer

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

string

The device timezone.

Example: America/New_York

os

string

The device operating system.

Example: iOS

screen_width

integer

Screen width in pixels.

Example: 390

screen_height

integer

Screen height in pixels.

Example: 844

utime

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 Common Parameters in S2S Fundamentals 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

string

Mapped from the web param wpsrc or utm_source.

Example: Snapchat

is_attributed

string

Set true for attributed events.

Example: true

partner_campaign_name

string

Mapped from the web param wpcn or utm_campaign.

Example: Snap_campaign_1234

Optional fields

Field Details
touch_timestamp

integer

Touchpoint time in UNIX seconds.

Example: 1751500800

partner_campaign_id

string

Mapped from the web param wpcid.

partner_creative_id

string

Mapped from the web param wpcrid.

partner_creative_name

string

Mapped from the web param wpcrn.

partner_keyword

string

Mapped from the web param wpkwn.

partner_site_id

string

Mapped from the web param wpsid.

partner_site_name

string

Mapped from the web param wpsn.

partner_sub_site_name

string

Mapped from the web param wpssn.

partner_subcampaign_id

string

Mapped from the web param wpscid.

partner_subcampaign_name

string

Mapped from the web param wpscn.

Web click IDs in the e param

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