Rovenue SDK
Integrations

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 hasEndpointDevice field
$singularDeviceId (Singular Device ID / SDID)POST https://s2s.singular.net/api/v2/evtsdid
$idfa (no SDID)POST https://s2s.singular.net/api/v1/evtidfa
$gpsAdId (no SDID, no IDFA)POST https://s2s.singular.net/api/v1/evtaifa

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's use_ip fallback — 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, so ip is frequently omitted and Singular may reject those events with missing argument: ip. If you want these attributed, pass identityContext.ip (the real device/customer IP, not your server's) on your own calls to POST /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 $attConsentStatus reserved 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 $attConsentStatus isn't set, the field is omitted rather than defaulted to 0.
  • The platform field (p) is always iOS/Android when the IDFA/GAID ladder branch matched, but is only sent for the SDID branch when the subscriber's platform attribute is set to a recognized value — Rovenue never guesses a platform.

1. Get your credentials from Singular

  1. In the Singular dashboard, open Developer Tools → SDK Integration → SDK Keys.
  2. Copy your SDK Key — not the Reporting API Key, which Singular's own EVENT endpoint rejects.
  3. 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

  1. In the Rovenue dashboard, navigate to Apps (left sidebar).
  2. Find Singular under Attribution and click it.
  3. 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.
  4. Click Validate (see Validation below — it only checks that both fields are filled in, it sends nothing to Singular).
  5. 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 keyDefault Singular event name
revenue.INITIALsng_subscribe
revenue.TRIAL_CONVERSIONsng_subscribe
revenue.RENEWALsng_ecommerce_purchase
revenue.CREDIT_PURCHASEsng_ecommerce_purchase
revenue.REFUND(not mapped — see below)
revenue.CANCELLATIONrovenue_cancellation
subscription.trial.startedsng_start_trial
subscription.cancel_requestedrovenue_cancel_requested
subscription.expiredrovenue_expired
subscription.billing_issuerovenue_billing_issue
subscription.grace_periodrovenue_grace_period
subscription.uncancelledrovenue_uncancelled
subscription.product_changedrovenue_product_changed
subscription.pausedrovenue_paused
subscription.recoveredrovenue_recovered
subscription.revokedrovenue_revoked
subscription.offer_redeemedrovenue_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:

  1. $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.
  2. $idfa — Apple's Identifier for Advertisers, if no SDID is set. Routes through the legacy V1 endpoint.
  3. $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

  1. Open Apps → Singular → Delivery Log in the Rovenue dashboard and check for succeeded rows with HTTP 200 and a body status of "ok".
  2. In the Singular SDK Console, look up the test device by its device identifier and confirm the mapped event names above are appearing.
  3. If events are not appearing:
    • Singular's EVENT API always answers HTTP 200 — a rejected request still shows as 200 with a body {"status":"error","reason":"..."}. Check the Delivery Log's response body for the reason string.
    • 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: ip is 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-200 HTTP status (429/5xx), is retried automatically.
    • Confirm the subscriber has $singularDeviceId, $idfa, or $gpsAdId set (missing all three skips silently with no_user_data).
    • Ensure the event type you're sending is included in the connection's enabled events.

On this page