Who this is for
This integration is for Shopify app teams that own a marketing, installation or app frontend and need one acquisition ID across referral, campaign and lifecycle reporting.
- 01A connected Marlo workspace and selected Shopify app
- 02Owner or admin access to create credentials
- 03An app-owned HTTPS origin
- 04A server-side session or database record that can retain an opaque acquisition ID
- 05A server route that runs after authoritative Shopify or billing verification
1. Create the source
- 01Open Tracking in Marlo
- 02Choose the Shopify app
- 03Enter every exact app-owned origin that will load the script
- 04Create the source
- 05Copy the browser-safe marlo_pk_ public key
- 06Open API & MCP and generate the private marlo_sk_ server key
- 07Store the private key only in your server secret manager
Rotate either key if it is exposed. Rotating invalidates the previous key. Disabling a tracking source stops new events while preserving existing raw events until the retention window ends.
2. Install the hosted script
Add this to the Shopify app's own frontend or an app-owned marketing or installation landing page. Do not add it to merchant storefront themes.
<script async src="https://withmarlo.app/v1/marlo.js" data-key="marlo_pk_REPLACE_WITH_YOUR_PUBLIC_KEY"></script>
The script creates a first-party opaque ID in local storage and automatically records a page_view on initial load and URL changes, including SPA navigation and back/forward navigation. Page context includes title and path. Sessions restart after 30 minutes without a tracked event; returning visitors are browsers with a retained ID from a previous session.
It retains the first observed marlo_ref, UTMs, gclid, gbraid and wbraid, plus first landing URL and referrer, for that source/browser across navigation and later visits. It exposes window.marlo.track(name, properties) for custom events and window.marlo.getAcquisitionId() for a verified server handoff. Do not send another manual page view for navigation the script already observes.
Inside an embedded Shopify app, load the same snippet alongside your existing App Bridge script. No extra configuration is needed. It detects App Bridge even when it loads shortly afterward and records shopifyShop, shopifyUserId when available, and shopifyIdentitySource: "app_bridge". A browser-only shopify_context event captures context that arrives after the first page view. Ordinary browser tracking continues if App Bridge is unavailable.
These are browser observations. Shopify's Admin User API does not expose names or email. The script reads the user ID from an ID token locally when available; it never stores or sends the raw token and strips authentication query parameters from captured URLs before storing or sending them.
3. Preserve the handoff
Before sending a visitor into Shopify installation, copy window.marlo.getAcquisitionId() into your own signed, first-party server session. Associate it with the shop only after Shopify authentication succeeds.
- 01Do not assume App Store or cross-domain redirects preserve query parameters
- 02Do not put a private Marlo key in a URL or browser bundle
- 03Do not replace Shopify OAuth state with the acquisition ID
- 04Continue to validate Shopify HMAC plus the signed state/nonce cookie
- 05For Shopify-managed installation, retain the ID in your app-owned session and attach it after the authenticated app opens
Shopify's authorization-code guidance requires the callback state/nonce and signed cookie to match. Marlo attribution is an adjacent application record, never a substitute for that CSRF control.
4. Identify an authenticated visitor
Once your server has verified the Shopify session, send identified to the server-events endpoint with the browser's acquisitionId, a unique eventId and the verified shop domain as customerRef. Use the same request shape shown below with name: "identified". This connects browsing history to an existing customer without claiming a new install or payment.
The shop reference must come from verified server authentication. App Bridge context can show the observed shop and user ID, but this server handoff is still required for verified customer and paying-status linkage. A browser-supplied shop name or decoded token does not establish verified identity. Identification does not count as a funnel conversion, create affiliate commissions or upload a Google Ads conversion.
5. Send authoritative events
Send install, trial_started, trial_converted, paid, qualified, canceled, churned or refunded only after your server verifies the corresponding Shopify, qualification or billing event. Reuse the same event ID when retrying; Marlo deduplicates it. Commissionable paid and refunded events also include positive integer amountCents and a three-letter currency.
curl --request POST 'https://app.withmarlo.app/api/tracking/server-events' \
--header 'Authorization: Bearer marlo_sk_REPLACE_WITH_YOUR_PRIVATE_KEY' \
--header 'Content-Type: application/json' \
--data '{"teamSlug":"acme","publicKey":"marlo_pk_REPLACE_WITH_YOUR_PUBLIC_KEY","eventId":"install_webhook_01J123456789","acquisitionId":"01J123456789ABCDEFGHJKMNPQ","name":"install","customerRef":"example-shop.myshopify.com","occurredAt":"2026-07-22T12:00:00Z"}'Maintained generic HTML, Shopify Remix, Next.js and direct HTTP examples live in Marlo's public repository under examples/tracking.
6. Verify
- 01Load the instrumented page on an allowed origin
- 02Open Tracking and confirm a browser page_view appears
- 03Navigate to another page and confirm its title and path are recorded
- 04Confirm the evidence badge says Browser
- 05In an embedded app, confirm Shopify shop and user context appears when available; it remains browser evidence
- 06Send identified from your authenticated server with the same acquisition ID
- 07Refresh and confirm the event says Server and shows the expected shop
- 08Send a verified install only when an installation occurs
- 09Open Distribution and confirm the sequential journey appears
- 10Repeat the same event ID and confirm only one recent event remains
Use the distribution funnel methodology to interpret stage loss, source breakdowns and missing handoffs.
Troubleshooting
- 01Origin not allowed: match scheme, host and port exactly
- 02Tracking source unavailable: replace a rotated key or enable a new source
- 03No page view: verify the script URL, public key and browser network request
- 04No lifecycle event: verify team slug, private key, event name and ISO timestamp
- 05Missing handoff: inspect your own signed session before Shopify redirect and after authenticated return
- 06429 response: wait 60 seconds and remove accidental loops
Security, privacy and limits
- 01The public key identifies one source but grants no read access
- 02The private key can read MCP data and write ingestion events; keep it server-side
- 03Raw tracking events are automatically deleted after 180 days
- 04Marlo does not treat browser events as proof of payment
- 05The script does not read merchant storefront data
- 06Landing URLs and referrers can contain personal data; disclose the integration and avoid placing sensitive values in URLs
- 07Browser storage, consent and advertising-click collection must match your jurisdiction and privacy policy
- 08Cross-domain attribution is not deterministic without an app-owned handoff
Use the method on your own data