Insights / Conversion tracking

Server-side conversion tracking for cash-on-delivery e-commerce

Why browser pixels are not enough for COD, and how we send leads and confirmed orders to Meta and TikTok from the server with reliable deduplication.

VIREXAGROUP Engineering · 13 October 2026

In cash-on-delivery e-commerce the sale is not finished when the customer submits a form. A lead arrives, a salesperson confirms it by phone or chat, and only then is there an order. If the ad platform only sees the form submission, it optimises for people who fill in forms, not for people who buy.

A browser pixel cannot see the confirmation step, and it loses events to ad blockers and closed tabs. So we send conversion events from the server.

The pipeline

Landing page / store→Automation layer (n8n)→Meta Conversions API · TikTok Events API
  1. Landing pages and the store send page views, form interactions and leads to a webhook.
  2. The automation layer validates each event, hashes customer identifiers, attaches campaign information, and forwards it to the ad platforms. It also writes every event to a log sheet for auditing.
  3. Confirmed orders come from our point-of-sale system. A poller pulls new orders from the POS API and sends them as purchase events with the same customer identifiers as the original lead.

Deduplication

Ad platforms count an event once if two reports share the same event ID. We first generated that ID in the browser, per form submission. When a customer submitted twice, or opened the page on a second device, the IDs differed and the lead was counted twice.

We changed the ID for lead and purchase events to a hash of the customer's phone number. The same person now produces the same ID wherever the event comes from, and the over-count went away.

One tracking engine for every landing page

We run more than a dozen landing pages. Each used to carry its own copy of the tracking script, so one fix meant editing every page. We moved the logic into a single shared script, loaded by every page with a small per-page configuration. A fix now ships to all pages at once.

The first fix it delivered was a race condition: when a visitor submitted the form and the page redirected immediately, the final lead event was sometimes lost. The shared engine now flushes pending events before the redirect.

Polling instead of webhooks for orders

We started with POS webhooks for purchase events and found gaps: some orders never produced a webhook. We replaced them with a poller that asks the POS API for new orders on a schedule and remembers what it has already sent. The webhook endpoints now accept requests only from that poller.

Failures worth remembering

The rule we took from this: every pipeline needs an independent check. We built a checker that compares three sources (the event log, the POS system and the landing page's own form data) so that a gap in one of them shows up as a disagreement.

More from VIREXAGROUP: Hourly ad and sales reports · A Claude-powered support assistant