Singular
Forward Rovenue revenue and subscription-lifecycle events into Singular as Server-to-Server (S2S) events, so you can attribute revenue back to ad campaigns.
Singular
The Singular integration forwards Rovenue revenue events (purchases, trial conversions, renewals, cancellations) and subscription-lifecycle events (trials, grace period, billing issues, plan changes) to Singular's Server-to-Server EVENT API, so purchases and subscription-lifecycle changes made in your app show up as Singular events, attributable back to the ad campaign that acquired the user.
Singular is a mobile attribution / marketing-measurement platform, not an analytics warehouse: this integration sends events, not a full profile sync. It does not push user properties and does not read anything back from Singular.
Wire contract
Singular has two EVENT endpoint versions. This integration picks between them per event, based on which device identifier is available on the subscriber (see Identity resolution):
| When the subscriber has | Endpoint | Device field |
|---|---|---|
$singularDeviceId (Singular Device ID / SDID) | POST https://s2s.singular.net/api/v2/evt | sdid |
$idfa (no SDID) | POST https://s2s.singular.net/api/v1/evt | idfa |
$gpsAdId (no SDID, no IDFA) | POST https://s2s.singular.net/api/v1/evt | aifa |
All request parameters — including your SDK key (a) and app ID (i) — are sent as application/x-www-form-urlencoded POST body fields, never as a query string. Every event also carries n (the mapped event name), is_revenue_event/amt/cur on revenue events (see Event mapping), and an e field — a JSON object with outbox_event_id and rovenue_event — so every Singular event traces back to its Rovenue source. Singular does not deduplicate events server-side; Rovenue's own delivery pipeline is the real dedup guarantee.
Known limitations:
- The subscriber's IP address (
ip) is only sent when Rovenue already has it, and is never derived from Singular'suse_ipfallback — that would attribute the event to Rovenue's own server IP rather than your subscriber's device, corrupting Singular's geolocation-based attribution. In practice, most server- or webhook-driven revenue events (a purchase your backend reports, or a store webhook Rovenue relays) have no client HTTP request in the loop and therefore no known IP, soipis frequently omitted and Singular may reject those events withmissing argument: ip. If you want these attributed, passidentityContext.ip(the real device/customer IP, not your server's) on your own calls toPOST /v1/events— Rovenue forwards it through to Singular whenever it's present. att_authorization_status(Apple's App Tracking Transparency status) is documented by Singular as "Always required" for iOS — but Rovenue does not fabricate a value. It is sent only when the subscriber's$attConsentStatusreserved attribute is set (mapped to Singular's numeric codes:notDetermined→0,restricted→1,denied→2,authorized→3), and only on the iOS branch of the identity ladder. If$attConsentStatusisn't set, the field is omitted rather than defaulted to0.- The platform field (
p) is alwaysiOS/Androidwhen the IDFA/GAID ladder branch matched, but is only sent for the SDID branch when the subscriber'splatformattribute is set to a recognized value — Rovenue never guesses a platform.
1. Get your credentials from Singular
- In the Singular dashboard, open Developer Tools → SDK Integration → SDK Keys.
- Copy your SDK Key — not the Reporting API Key, which Singular's own EVENT endpoint rejects.
- Copy your app's App ID — the OS bundle id (iOS, e.g.
com.example.app) or package name (Android) you registered the app with in Singular. This is required on every event (i); there is no way to deliver without it.
2. Add the integration in Rovenue
- In the Rovenue dashboard, navigate to Apps (left sidebar).
- Find Singular under Attribution and click it.
- The side drawer opens. Fill in the Credentials step:
- App ID (bundle id / package name) — paste the app id from step 1.
- SDK key — paste the SDK key from step 1.
- Click Validate (see Validation below — it only checks that both fields are filled in, it sends nothing to Singular).
- Choose which events to forward, optionally override the default mapping, then activate.
3. Event mapping
Every forwarded key becomes a Singular event name, per the mapping table below (override any name in the drawer's mapping step). Three keys map onto Singular's own standard event vocabulary; the rest use a short, Rovenue-namespaced custom event name, since Singular has no built-in equivalent:
| Rovenue event key | Default Singular event name |
|---|---|
revenue.INITIAL | sng_subscribe |
revenue.TRIAL_CONVERSION | sng_subscribe |
revenue.RENEWAL | sng_ecommerce_purchase |
revenue.CREDIT_PURCHASE | sng_ecommerce_purchase |
revenue.REFUND | (not mapped — see below) |
revenue.CANCELLATION | rovenue_cancellation |
subscription.trial.started | sng_start_trial |
subscription.cancel_requested | rovenue_cancel_requested |
subscription.expired | rovenue_expired |
subscription.billing_issue | rovenue_billing_issue |
subscription.grace_period | rovenue_grace_period |
subscription.uncancelled | rovenue_uncancelled |
subscription.product_changed | rovenue_product_changed |
subscription.paused | rovenue_paused |
subscription.recovered | rovenue_recovered |
subscription.revoked | rovenue_revoked |
subscription.offer_redeemed | rovenue_offer_redeemed |
Singular's own EVENT endpoint reference caps event names at 32 ASCII characters — that's why the lifecycle names above drop the word "subscription" (Rovenue's usual rovenue_subscription_<key> convention would exceed the limit for several of these keys).
On revenue.REFUND: Singular's full standard-event vocabulary has no reversal, cancellation, or refund event, and its EVENT endpoint documents no negative-amount convention for amt. The only cancellation-adjacent idea in Singular's own docs ("Order Canceled") is listed as a freeform, non-standard suggestion with no reserved name or reversal semantics — so, unlike the Airbridge integration (which has a real dedicated reversal event to map to), revenue.REFUND is intentionally left unmapped here and is skipped rather than forwarded.
Every forwarded event carries e.rovenue_event (the original Rovenue event key) and e.outbox_event_id, so you can always trace a Singular event back to its Rovenue source.
Amount/currency are included only when both are present and valid. Rovenue never guesses a currency (it does not default to "USD"). When a revenue event has an amount but no known currency (or an unparseable amount), the event is still forwarded — with is_revenue_event=true and no amount — rather than dropped; Singular's own docs describe this exact pattern for "a revenue event that has no amount."
Identity resolution
Every event needs a Singular device identifier. Rovenue checks, in order:
$singularDeviceId— Singular's own Device ID (SDID), obtained from the Singular SDK once it has initialized. This is Singular's current, recommended identifier (it works across iOS, Android, web, and more) and routes the event through Singular's newer V2 EVENT endpoint.$idfa— Apple's Identifier for Advertisers, if no SDID is set. Routes through the legacy V1 endpoint.$gpsAdId— Android's Google Advertising ID, if neither of the above is set. Also routes through V1.
Advertising ids are default-deny (see Advertising ids): $idfa is used only when the subscriber's $attConsentStatus is authorized, and $gpsAdId is never used, because no Android opt-in signal exists. Set $singularDeviceId to attribute every other user.
If none of these survive for a subscriber, every event for that subscriber is skipped with reason no_user_data (visible in the connection's Delivery Log).
See Subscriber Identity Attributes for the full reserved-attribute reference, including $singularDeviceId, $idfa, and $gpsAdId.
Validation
Singular's EVENT/SESSION endpoints are write-only and reachable with just an SDK key — Singular's own docs state that device-level data "cannot be deleted after ingestion," so there is no zero-footprint, read-only way to confirm an SDK key/App ID pair is valid without sending a real, permanent, attributable event.
So, like the AppsFlyer and Airbridge integrations, clicking Validate here only checks that both fields are filled in — the first real event delivery is the live proof. Check the connection's Delivery Log after activating to confirm events are landing.
Verify events are arriving
- Open Apps → Singular → Delivery Log in the Rovenue dashboard and check for
succeededrows with HTTP200and a bodystatusof"ok". - In the Singular SDK Console, look up the test device by its device identifier and confirm the mapped event names above are appearing.
- If events are not appearing:
- Singular's EVENT API always answers HTTP
200— a rejected request still shows as200with a body{"status":"error","reason":"..."}. Check the Delivery Log's response body for thereasonstring. - A reason containing "missing argument", "invalid platform", "no device ID supplied", or "should have an ... param" means the request itself needs fixing (Rovenue treats these as permanent failures and does not retry them).
missing argument: ipis expected for server/webhook-driven events with no known subscriber IP — see the "Known limitations" note in Wire contract for how to send one.- Any other error reason, or a non-
200HTTP status (429/5xx), is retried automatically. - Confirm the subscriber has
$singularDeviceId,$idfa, or$gpsAdIdset (missing all three skips silently withno_user_data). - Ensure the event type you're sending is included in the connection's enabled events.
- Singular's EVENT API always answers HTTP