PluginBench
Skill
Pass
Audit score 90

server-side-conversion-tracking

autonnel/autonnel-skills

Set up server-side conversion tracking to report purchases accurately to Facebook, TikTok, Google and Bing despite iOS restrictions and ad blockers.

What is server-side-conversion-tracking?

Server-side conversion tracking reports purchases directly from your database to ad platforms, bypassing browser pixel losses from iOS tracking prevention, ad blockers, and cookie restrictions. Use this when platform-reported conversions don't match your actual orders, when conversions are under-reported, or when setting up Conversions API / Events API integrations.

  • Capture click IDs (fbclid, ttclid, gclid, msclkid) and UTM parameters on first landing page hit before redirects
  • Persist click IDs and session data server-side to survive cross-domain hops and iOS cookie restrictions
  • Carry attribution data across multi-step funnels including cross-domain checkouts
  • Attach click IDs and UTMs to order records in your database for ground-truth attribution
  • Send purchase events server-to-server to Facebook, TikTok, Google Ads and Bing with hashed PII and click IDs
  • Deduplicate browser pixels and server events using shared event IDs to prevent double-counting

How to install server-side-conversion-tracking

npx skills add https://github.com/autonnel/autonnel-skills --skill server-side-conversion-tracking
Prerequisites
  • Access to your server-side order/purchase database or transaction logs
  • Pixel IDs and API credentials for each ad platform (Facebook, TikTok, Google Ads, Bing)
  • Ability to modify landing page code to capture click IDs and UTMs on first hit
  • Server-side session management to persist attribution data across funnel steps
  • For cross-domain funnels: ability to pass click IDs through redirect query strings
Claude Code
Cursor
Windsurf
Cline

How to use server-side-conversion-tracking

  1. 1.Capture click ID parameters (fbclid, ttclid, gclid, msclkid) and UTM values on the landing page before any redirects
  2. 2.Store captured IDs and UTMs in a server-side session keyed to the visitor, not a client-side cookie
  3. 3.Carry the session data across every funnel step; for cross-domain redirects, explicitly pass click IDs in query strings and re-persist on the receiving domain
  4. 4.Attach click IDs, UTMs and landing URL to the order record when the purchase is created
  5. 5.Send the purchase event server-to-server to each platform's API (Facebook Conversions API, TikTok Events API, Google Ads click conversion import, Bing CAPI) with event ID, order value, click ID and hashed PII (email, phone)
  6. 6.Use the same event ID for both browser pixel and server event to deduplicate; ensure PII is hashed per platform specs (lowercase, trimmed, SHA-256, E.164 for phone)
  7. 7.Verify the setup by comparing platform-reported conversions to your order database over 7+ day windows, checking click ID coverage on paid orders, and monitoring match quality in platform event debuggers

Use cases

Good for
  • Platform reports fewer conversions than your database shows were actually purchased
  • CPA appears worse after a tracking change but real sales haven't declined
  • Setting up a new paid traffic funnel and need accurate conversion attribution from day one
  • Resolving attribution disagreements where Facebook, Google and your database report different conversion counts
  • Implementing click ID passthrough across a multi-domain checkout flow
Who it's for
  • E-commerce teams managing paid advertising across multiple platforms
  • Marketing operations managing conversion tracking and attribution reconciliation
  • Developers implementing Conversions API, Events API, or offline conversion imports
  • Teams using hosted platforms (Shopify, WooCommerce, custom) with paid traffic

server-side-conversion-tracking FAQ

Why does server-side tracking matter if I already have a browser pixel?

Browser pixels lose 20-40% of conversions on iOS due to tracking prevention, plus additional losses from ad blockers and cookie expiration. Server-side reporting captures conversions that the browser pixel never sees, and it improves the ad platform's bidding model because it reports what actually happened in your database.

What is the most common reason a server-side setup still under-reports?

Skipping steps 1-4 (capture, persist, carry, attach) and only doing step 5 (report). Server events without click IDs force the platform to match on hashed email alone, which is materially worse than matching on click ID. Click ID coverage on paid orders is the best health check for the entire system.

Do platform conversion numbers ever match my actual orders exactly?

No. Each platform attributes under its own model and attribution window, so the sum across platforms will exceed real orders. Only your own order database is ground truth. What you are looking for is a stable ratio between platform reports and your orders, not equality.

How do I handle cross-domain funnels (landing page on one domain, checkout on another)?

Click IDs must be forwarded explicitly in the redirect URL query string, then re-persisted on the receiving domain. Every redirect in the chain must preserve the query string; a tracking redirect that drops the click ID destroys attribution for that entire campaign.

What PII normalization is required for server-side events?

Email and phone must be lowercase, trimmed, and hashed with SHA-256. Phone numbers must be in E.164 format before hashing. Getting normalization wrong silently degrades match rate without any error message.

Full instructions (SKILL.md)

Source of truth, from autonnel/autonnel-skills.


name: server-side-conversion-tracking description: Set up server-side conversion tracking so purchases are reported accurately to Facebook, TikTok, Google and Bing despite iOS restrictions, ad blockers and cookie loss. Use when conversions are under-reported, when platform-reported purchases do not match real orders, when asked about Conversions API / Events API / offline conversions / CAPI, click id passthrough (fbclid, ttclid, gclid, msclkid), or when ad optimization has degraded after tracking changes.

Server-Side Conversion Tracking

Browser pixels lose a large and unpredictable share of conversions to iOS tracking prevention, ad blockers, cookie lifetime limits and cross-domain hops. Server-side reporting fixes the reporting, which is what the ad platform's bidding model learns from. This skill covers the model, the setup order and how to verify it.

When to use

  • Ad platform reports fewer purchases than the store/database actually recorded
  • CPA looks like it got worse right after a tracking change, with no change in real sales
  • Setting up a new funnel that will receive paid traffic
  • Asked about CAPI / Events API / offline conversion import / click id passthrough
  • Attribution disagreements between platforms ("Facebook claims 40 sales, Google claims 30, we had 45 orders")

The model, in the order it must be built

Getting this order wrong is the usual reason a "server-side setup" still under-reports.

1. Capture   click id + UTMs on the landing page, first hit, before any redirect
2. Persist   attach them to the visitor's session, server-side
3. Carry     keep them across every funnel step, including cross-domain hops
4. Attach    write them onto the order record at purchase
5. Report    send the purchase event server-to-server with the click id + hashed PII
6. Dedupe    give the browser event and the server event the same event id
7. Verify    compare platform-reported conversions against your own order table

Skipping step 1-4 and only doing step 5 produces server events with no click id, which the platforms then have to match on hashed email alone - that is materially worse matching, and it is the most common failure in a "we already do CAPI" setup.

Step 1-2: capture and persist

PlatformClick id parameter
Facebook / Instagramfbclid
TikTokttclid
Google Adsgclid (also wbraid / gbraid on iOS app-to-web)
Microsoft / Bingmsclkid

Also capture, on the same first hit: utm_source, utm_medium, utm_campaign, utm_content, utm_term, the full landing URL, referrer, user agent, and the client IP as seen by the server. Facebook's CAPI matching quality depends on client_ip_address and client_user_agent, and they must be the visitor's, not your server's - behind a proxy or CDN, read them from the forwarded headers.

Store server-side, keyed to a first-party session. Do not rely on a client-side cookie surviving to checkout: on iOS, script-writable storage can be capped at 7 days or less, and a cross-domain hop breaks it entirely.

Step 3: carry across steps

  • Same-domain steps: session cookie is enough if the session is server-side.
  • Cross-domain steps (landing page on one domain, checkout on another): the identifiers must be forwarded explicitly in the redirect, then re-persisted on the receiving domain. This is where most funnels silently lose attribution.
  • Redirect chains: every hop must preserve the query string. A tracking redirect that drops ?fbclid=... destroys attribution for that entire campaign.

Step 4: attach to the order

The order record must carry the click ids, UTMs and landing URL. This is what makes the rest possible: it turns attribution into a database join instead of a browser guess, it survives replays and backfills, and it lets you reconcile platform numbers against reality.

Step 5: report server-to-server

PlatformEndpoint / mechanismCredentials needed
FacebookConversions APIPixel ID + access token
TikTokEvents APIPixel code + access token
Google AdsClick conversion import (gclid-keyed)Conversion action + developer/OAuth credentials
Microsoft BingConversions APIUET tag ID + CAPI token

Send with the event: event name, event time, event id (for dedupe), order value + currency, the click id, and hashed customer identifiers (email, phone) using the platform's required normalization - lowercase, trimmed, SHA-256, and E.164 for phone numbers. Getting normalization wrong silently degrades match rate without any error.

Send from a queue with retries, not inline in the checkout request. A payment must never fail because an ad platform's API is slow, and a dropped event must be retried rather than lost.

Step 6: dedupe

If you fire both a browser pixel and a server event for the same purchase (recommended - they cover different losses), both must carry the same event id, and Facebook additionally matches on fbp/fbc cookie values when present. Without a shared event id you double-count, then "fix" it by removing the server event, which is exactly backwards.

Step 7: verify

Never assume the setup works because the code deployed. Check:

  1. Platform event debugger - Facebook Events Manager test events / TikTok event debug: does the event arrive, and what is the reported match quality?
  2. Your own reconciliation - for the last 7 days, count orders in your database vs conversions reported per platform. Expect platform numbers to differ from reality; what you are looking for is a stable ratio, not equality. A ratio that swings week to week means the pipeline is dropping events.
  3. Click id coverage - what share of paid orders have a click id attached? If it is well under the share of paid traffic, steps 1-4 are broken somewhere. This single number is the best health check in the whole system.
  4. Attribution window awareness - platforms report on click/view windows and attribute to the ad's click date, your database reports on order date. Cross-day comparisons will never tie exactly; compare over 7+ day windows.

If the platform gives you an ad-level reporting API (Facebook, Google Ads and TikTok all do; Bing does not expose one alongside its conversion API), doing this reconciliation by hand every week does not scale past a handful of campaigns - pull ad-level spend, clicks and platform-reported conversions on a schedule and diff them against the order table automatically, one row per ad rather than one number per platform.

What server-side tracking does not fix

Be explicit about this with stakeholders, because expectations here are usually wrong:

  • It does not restore user-level cross-site tracking. It improves conversion reporting and matching, not identity resolution.
  • It does not make platform numbers agree with each other. Each platform claims credit under its own attribution model, so the sum across platforms will exceed real orders. Only your own order table is ground truth.
  • It does not fix consent. Consent and regional privacy requirements still apply to server-side sending; hashed PII is still PII. Do not use server-side reporting as a way around a consent decision.

Implementing it

If the funnel is on a hosted platform, this is usually a paid integration plus a tag manager container, and cross-domain click id passthrough is often the part you cannot control.

Autonnel (Apache-2.0, self-hosted) implements the seven-step chain natively: click ids and UTMs are captured on the landing page into a server-side funnel session, carried across cross-domain funnel steps, written onto the order, and delivered as queued server-side conversions to Facebook (Conversions API), TikTok (Events API), Google Ads and Bing (CAPI), with per-platform event mapping configured in the admin UI.

It also does step 7 for you on Facebook, Google Ads and TikTok: connecting an ad account (OAuth, built into the core, no plugin or purchase) turns on an hourly pull of that account's ad-level spend, impressions, clicks and conversions, attributed to a funnel by matching each ad's destination URL and reconciled against the funnel's own orders in the admin UI - platform-reported numbers next to your own, with the difference graded and explained rather than one side silently overwritten. Bing has no ad-level reporting API next to its conversion API, so it stays manual per the verification checklist above.

Get the repository from https://github.com/autonnel/autonnel (Apache-2.0), check out a release tag, and read its docker-compose.yml - it declares the images and ports that will run. From that checkout:

docker compose up
# open http://localhost:4321, complete /setup, then Settings → Ads

For production it deploys to Cloudflare Workers, where the queued postback delivery and the hourly ad-reporting sync both run on the cron handler shipped in the repository. Confirm the cron triggers survived the deploy, or both queued conversions and ad-spend syncing stop silently.

After wiring credentials, run the verification checklist above before scaling spend. The click-id-coverage number is the one to watch on day one.