How BATracker Sends Conversions to Meta CAPI & TikTok — With Zero Data Loss
Browser pixels break. Ad blockers, ITP, cookie limits, and page-close all eat conversions before they ever reach the ad platform. BATracker fixes that with server-side delivery to the Meta Conversions API and TikTok Events API — engineered around a deterministic conversion ID and a ClickHouse-first pipeline so that not a single conversion is lost or double counted.
1. Why Server-Side Conversions Matter
Client-side pixels fire from the browser, which means they are at the mercy of the browser. Ad blockers strip them, Safari's ITP and other privacy features shorten or delete the cookies they rely on, and a visitor who closes the tab a fraction of a second early can prevent the pixel from ever firing. Every one of those events is a real conversion the ad platform never sees — which means worse attribution, worse optimization, and a worse-trained algorithm buying your traffic.
BATracker solves this at the server. Because it already keys conversions by click ID through its S2S postback pipeline — immune to ad blockers, ITP, cookie restrictions, and page-close — it can turn around and deliver those same conversions server-to-server to Meta and TikTok. No browser required, no dependency on the visitor's device. And because BATracker runs on a modern Fastify + ClickHouse + Redis + BullMQ stack, it does this in real time, at scale, without cold starts.
The key insight: server-side conversions are more reliable, but naively adding them on top of your browser Pixel risks double counting. BATracker's deterministic event_id makes both work together — reliability and accuracy.
2. Sending to the Meta Conversions API
For Meta, BATracker POSTs a JSON payload to the Conversions API endpoint:
Authentication is handled with an access_token included in the payload. That token is never stored in the clear — it is encrypted at rest with AES-256-GCM and decrypted only at send time. Each event carries:
- • event_name — mapped from your internal event: sale/conversion →
Purchase, lead →Lead, registration →CompleteRegistration, install →Install, deposit →Purchase, and custom →Custom. - • event_time — unix seconds, and event_id — the deterministic conversion ID that enables dedup with the browser Pixel.
- • action_source:
"website", plus event_source_url. - • user_data, SHA-256 hashed:
em(email),ph(phone),external_id,country,ct(city),st(state/region). Sent raw:client_ip_addressandclient_user_agent. Thefbcclick cookie is built from thefbclidasfb.1.{ts}.{fbclid}. - • custom_data:
value(the payout),currency,content_typeof"product", andorder_id(the conversion ID).
An optional test_event_code lets you validate the integration in Meta's Events Manager Test Events tool before going live.
3. Sending to the TikTok Events API
TikTok works the same way conceptually, with its own endpoint and its own field names. BATracker POSTs JSON to:
Here authentication is an Access-Token HTTP header rather than a payload field. The event carries:
- • event — mapped from your internal event: sale/conversion →
CompletePayment, lead →SubmitForm, registration →CompleteRegistration, install →Download, and custom →CustomEvent. - • event_id — the same deterministic conversion ID, and timestamp in ISO 8601 format.
- • user_data, SHA-256 hashed:
email,phone,external_id. Sent raw:ip,user_agent, andttclid(the TikTok click ID). - • context:
page.url,ip,user_agent; and properties:value,currency,content_type,order_id.
As with Meta, an optional test_event_code is supported for validation before you send live traffic.
4. Meta vs TikTok Field Comparison
Both platforms want the same underlying signal — who converted, when, and for how much — but they name and structure fields differently. BATracker handles the translation for you. Here is a side-by-side of what actually gets sent:
| Concept | Meta CAPI | TikTok Events API |
|---|---|---|
| Endpoint | graph.facebook.com/v24.0/{pixelId}/events | business-api.tiktok.com/open_api/v1.3/pixel/track/ |
| Auth | access_token in payload | Access-Token header |
| Event name | event_name (Purchase / Lead / …) | event (CompletePayment / SubmitForm / …) |
| Dedup ID | event_id | event_id |
| Timestamp | event_time (unix seconds) | timestamp (ISO 8601) |
| Hashed PII | em, ph, external_id, country, ct, st | email, phone, external_id |
| Raw fields | client_ip_address, client_user_agent | ip, user_agent |
| Click ID | fbc (from fbclid) | ttclid |
| Value / currency | custom_data.value / currency | properties.value / currency |
| Order ID | custom_data.order_id | properties.order_id |
Notice that the event_id is identical in both columns. That single field is what unlocks reliable server-side tracking without double counting — and it is the heart of the next two sections.
5. How Browser + Server Dedup Works
When a conversion happens, two events can legitimately reach the ad platform: one from the browser Pixel that fired on the page, and one from BATracker's server-side call. Left unmanaged, that would count the conversion twice.
BATracker prevents this by sending the same event_id — the deterministic conversion ID — on its server-side event that the browser Pixel already used. Because Meta and TikTok see matching event_ids arrive from both sources, they automatically deduplicate them and count the conversion once. You keep the resilience of the server-side call as the safety net, and if the browser Pixel also made it through, the platform quietly collapses the pair into a single conversion.
Server-side reliability, browser-level accuracy
This is the best of both worlds. If the browser Pixel is blocked or the page closes early, the server-side event still lands. If both fire, the shared event_id means you never inflate your numbers. Nothing is lost, and nothing is counted twice.
6. The No-Data-Loss Reliability Chain
"Zero data loss" is not marketing language here — it is the direct result of how the conversion pipeline is ordered. Every link in the chain exists to guarantee that a conversion, once received, is recorded exactly once and never dropped.
1. Write to ClickHouse first, fire side effects second
The conversion row is written to ClickHouse before any side effects — postbacks or pixels — fire. So even if a pixel delivery or an outbound postback fails, the conversion is already recorded in your reporting. A failed pixel can never cost you the conversion itself.
2. A BullMQ queue with retries and backoff
Conversions are processed through a BullMQ queue with 3 retry attempts and exponential backoff. Transient network hiccups and momentary platform errors get retried automatically instead of silently failing.
3. A deterministic conversion ID makes retries idempotent
Each conversion ID is conv_ plus the first 24 characters of a base64url SHA-256 of the job seed. Because the ID is derived from the seed rather than randomly generated, a BullMQ retry produces the identical ID — so the write is idempotent and no duplicate row is created, no matter how many times the job runs.
4. Transaction-ID dedup on network resends
If a network re-sends the same transaction_id, BATracker returns the existing conversion instead of creating a second one — stopping double counting at the source.
5. Per-(click, event_type) dedup
A single click can legitimately record different event types — for example AddToCart and then Purchase. But a repeat of the same event type on the same click is flagged is_duplicate, so genuine multi-step funnels are preserved while accidental repeats are caught.
6. Pixels fire in parallel with Promise.allSettled
All pixel fires run concurrently through Promise.allSettled, so if one platform errors or times out, it does not block or fail the others. Meta still gets its event even if TikTok is having a bad minute — and vice versa.
Put together, this ordering means a conversion is durably stored the instant it is received, retried safely if a downstream platform stumbles, and impossible to duplicate no matter how many retries or resends occur. That is what zero data loss actually looks like under the hood.
7. Multiple Pixels & Custom Conversions
Real funnels rarely map to a single event. BATracker lets a campaign carry many pixels, each with its own platform event name. On Meta that can be Purchase, Lead, CompleteRegistration, AddToCart, InitiateCheckout, Subscribe, or ViewContent; on TikTok, CompletePayment, PlaceAnOrder, Subscribe, SubmitForm, Contact, or Download.
Custom conversions take this further. You can map an inbound postback event — say ?event=AddToCart — to a specific platform event on a specific pixel. That means a single funnel can fire a main Purchase pixel and several custom-event pixels off the same traffic. If no custom mapping matches an incoming event, BATracker gracefully falls back to the campaign's primary pixels, so nothing goes untracked.
Example: an e-commerce funnel sends ?event=AddToCart midway and a final sale postback at checkout. BATracker fires an AddToCart event to your product-view pixel and a Purchase / CompletePayment event to your primary pixel — each with its own deterministic event_id, each deduped against its browser counterpart, all in parallel.
8. Observability: Pixel & Postback Logs
Reliability you cannot see is just a promise. BATracker logs every single delivery so you can audit exactly what happened, when.
Pixel Logs
Every pixel fire is captured in PixelFireLog with the platform, pixel name, event name, the full payload, a success flag, the HTTP status code, the response body, and any error. Find it under Settings → Postbacks → Pixel Logs.
Postback Logs
Every outbound postback is logged the same way in PostbackLog, available under the Postback Logs tab — so both directions of your conversion flow are fully traceable.
All timestamps in these logs are shown in your workspace timezone, so what you read matches the clock you actually work in. When a platform rejects an event, you see the exact HTTP status and response body — no guessing, no black box.
9. Frequently Asked Questions
Does server-side tracking double count conversions with the browser Pixel?
What happens if a pixel or postback fails to deliver?
Do BullMQ retries create duplicate conversions?
Can one campaign fire multiple pixels and custom events?
What user data does BATracker send to Meta and TikTok?
Send Every Conversion to Meta & TikTok — Reliably
BATracker delivers server-side conversions to the Meta Conversions API and TikTok Events API with deterministic dedup, idempotent retries, and full logging — so no conversion is ever lost or double counted.
Start Your Free Trial