Developer guide · v1.0 · Interactive

AirBridge → Mixpanel Instrumentation Mapping

Owner Sourabh Mishra (MMRP) Route Sourabh → Kapil → Prajwalit Audience iOS + Android
Sink: Mixpanel only Prod 4017469 Staging 4017466 Dev 4002103 Prod is behind — see §Naming

Click any link parameter to see where it lands

This is a real example AirBridge link. Every parameter maps to something in Mixpanel — or explicitly doesn't. Click one to see the rule.

Example link

Also part of the mapping — not URL query params
Maps to Mixpanel Not forwarded — debug only

The chain — what happens on a click

The app owns everything inside the device. The server-side join is MMRP's — but it only works if the app sets the join keys, which is why this is a developer spec.

User
Taps a go.mixxi.ai / abr.ge link (carries channel, campaign, sub-params).
AirBridge → OS
Redirect + store fallback, then the OS deep-link resolver opens the app (or triggers install → first launch).
App — consent gate
DPDP consent gate. Nothing fires until affirmative consent.
App — SDK init order
Mixpanel initializes first (distinct_id ready), then AirBridge SDK initializes.
AirBridge → App
Deferred deep-link params on first launch, or immediate params if already installed.
App → Mixpanel
Sets acquisition_source ($set_once), marketing_affiliate_id, airbridge_id.
App → Mixpanel
Tracks signup_complete / live_watch_anchor with join props attached.
AirBridge → MMRP
Server postback carries deterministic attribution data.
MMRP
Joins the postback to Mixpanel via airbridge_id / marketing_affiliate_id.

Identity & timing model

Four rules, in this order. Breaking the order causes silent attribution loss — no error, just a missing join.

  1. Consent gate is absolute (DPDP). No SDK initializes, no token/cookie/storage is written, and no event fires before affirmative consent. Deny-by-default.

  2. Mixpanel initializes before any attribution event. If an attribution event fires before Mixpanel has a distinct_id, the event is lost and never attributed. Order: consent → Mixpanel init → AirBridge init → read link → set props.

  3. Read the deferred deep link on first launch. For a new install, the affiliate code arrives via AirBridge's deferred deep-link callback — not the immediate handler. Handling only immediate deep links makes every affiliate install look organic, and no affiliate gets paid. Handle both.

  4. acquisition_source is first-touch immutable. Write it with $set_once — never overwrite. Null resolves to unknown. marketing_affiliate_id and airbridge_id are set on first attributed touch and used as deterministic join keys — no fingerprinting.

Step-by-step (app side)

Illustrative pseudo-code — use the real AirBridge/Mixpanel SDK method names per platform. The order and the property names are what matter.

// 1. Consent gate — DPDP deny-by-default. Nothing below runs until true.
if (!userHasConsented()) return;

// 2. Mixpanel FIRST — distinct_id must exist before any attribution event.
mixpanel.init(MIXPANEL_TOKEN);            // token per environment
mixpanel.identify(stableDistinctId());

// 3. AirBridge SDK.
airbridge.init(AIRBRIDGE_APP_TOKEN);      // token per environment — never staging token in prod build

// 4. Deferred deep link (NEW installs) + immediate (existing installs).
airbridge.onDeferredDeeplink(params -> applyAttribution(params));
airbridge.onDeeplink(params -> applyAttribution(params));

function applyAttribution(params) {
    // 5. Map link params → Mixpanel.
    mixpanel.people.setOnce({ acquisition_source: rollup(params.channel) });  // first-touch, immutable
    mixpanel.people.set({ airbridge_id: airbridge.getAirbridgeId() });

    // affiliate links only — code carried in `campaign`, matches deep-link path
    if (isAffiliate(params)) {
        mixpanel.people.set({ marketing_affiliate_id: params.campaign });
        mixpanel.register({ marketing_affiliate_id: params.campaign });        // super prop → rides on events
    }
    mixpanel.register({ acquisition_source: rollup(params.channel) });
}

// 6. Emit funnel events with join props attached (super props ride automatically).
mixpanel.track("signup_complete", { new_otp_unique_account: <bool> });         // NOT account_created
mixpanel.track("live_watch_anchor", { episode_id, episode_number, watch_duration });

Platform notes

  • iOS: register deep-link handling in the SceneDelegate/AppDelegate path AirBridge documents; ensure ATT/consent timing does not fire attribution before Mixpanel init.
  • Android: intent filters + autoVerify; ensure the deferred-link callback is registered before the first activity that could emit an event.
  • Both: enum values are lowercase; Mixpanel will not dedupe WhatsApp vs whatsapp.

Server-side join — why the app props matter

The AirBridge server postback hits MMRP at /api/ingestion/airbridge/*. MMRP joins that postback to the Mixpanel funnel using airbridge_id (and marketing_affiliate_id for affiliate credit). The join is deterministic-only — if the app didn't set these keys, the postback and the funnel can't be joined and the install looks organic.

Affiliate credit follows a no-clawback ledger: money is only released on met conditions, never reversed — so a missing join key means an affiliate is silently underpaid. That's why §2/§3 are strict.

Naming & parity

DPDP hard constraints

Verify — one decisive end-to-end test

Do this on a clean device (no prior install), per platform, per environment.

  1. Cut a test link (go.mixxi.ai or abr.ge) with a known campaign code, e.g. RISING-E0-TESTONLY, deep link mixxi://mixxi/m/RISING-E0-TESTONLY.
  2. Tap it → install → open → complete consent → sign up.
  3. In Mixpanel Live View (correct environment), confirm: signup_complete fired; marketing_affiliate_id == RISING-E0-TESTONLY exactly; acquisition_source set (not null/unknown for a known channel); airbridge_id present.
  4. Confirm the AirBridge postback for the same install carries the same code, and MMRP resolves the join.

Success criteria

The same affiliate code is visible on both sides (AirBridge postback and Mixpanel marketing_affiliate_id) and MMRP joins them. If the code is missing on the Mixpanel side, the break is app-side (deferred link not read, or props set before Mixpanel init). If present in Mixpanel but not joined, the break is the postback field mapping (see Open Pins).

Open pins

Pending — Punit (template owner)

Affiliate-code postback field — partner vs sub_id. Which AirBridge field the tracking template forwards to the MMRP receiver is console-defined and unconfirmed. Build against campaign as the in-app source of marketing_affiliate_id (that's stable); treat the postback field name as unconfirmed. Recommendation on record: forward as a dedicated sub_id, not partner.

In progress — parallel workstream

Branded domain go.mixxi.ai. Being stood up in parallel. Does not change the affiliate-code parameter contract. Deferred-deep-link attribution works regardless of domain; Universal Link / App Link direct-open on the new domain is a separate, freeze-gated entitlement change.

Unresolved

vote_cast source — client Mixpanel vs. server-side Kafka rising.vote.validated — unresolved. Do not build vote_cast client-side until settled.