LW IT Solutions logo LW IT Solutions logo LW IT Solutions
« Blog Overview /Digital Marketing/Tutorials / Tutorial: Implementing the TikTok Events API for...
Read this article in other languages:

Tutorial: Implementing the TikTok Events API for Online Shops

Tutorial: Implementing the TikTok Events API for Online Shops
Contents
  1. Step 1: Generating the Event ID Once
  2. Step 2: The Browser Event
  3. Step 3: Capturing the Identifiers the Server Cannot See
  4. Step 4: The Server Request
  5. Step 5: Verifying That Deduplication Works
  6. Staying Inside the Guardrails
  7. Questions and answers
  8. Sources

A TikTok pixel on its own reports fewer purchases than a shop actually processes. Ad blockers, tracking protection in the browser, and visitors who close the tab before the confirmation page finishes rendering all remove events that did happen. Sending the same purchases a second time from the server closes that gap — and creates a new problem, because TikTok now receives two reports of one purchase.

The mechanism that resolves it is a shared event_id: one value generated per purchase and sent unchanged along both paths. The following walkthrough builds that architecture end to end for a shop — the browser event, the identifiers the server cannot see by itself, the server request, and the verification that deduplication is actually working.

Two-lane flow: one event_id generated per purchase, sent by the browser TikTok pixel and by the server Events API, merging in Events Manager where the matching id makes the purchase count once
The pixel supplies the browser context, the server supplies the reliability – the shared event_id keeps them one conversion.

Step 1: Generating the Event ID Once

The event_id has to be generated in one place and reused, which rules out generating it independently on each side — two random values describe two separate conversions as far as TikTok is concerned. For a shop, the order identifier is the natural source: it already exists at the moment of purchase, it is stable, and it is available to both the confirmation page and the backend.

A prefix keeps event types apart, since a single order can produce more than one tracked event. An event_id of purchase-10432 for the purchase and checkout-10432 for the checkout start keeps each pair matched to its own counterpart. As a side effect, a visitor refreshing the confirmation page produces the same event_id again and is deduplicated rather than counted twice.

Step 2: The Browser Event

In the TikTok pixel, the identifier travels in the third argument of ttq.track, separate from the event properties:

ttq.track('CompletePayment', {
  contents: [
    { content_id: 'SKU-8891', content_type: 'product', quantity: 1, price: 149.00 }
  ],
  value: 149.00,
  currency: 'PLN'
}, {
  event_id: 'purchase-10432'
});

The event name must match on both sides as well. TikTok pairs the two reports on the combination of event name and event_id, so CompletePayment in the browser and Purchase on the server are two different events that will never deduplicate against each other, no matter how carefully the identifier is matched.

Step 3: Capturing the Identifiers the Server Cannot See

Two values exist only in the browser. ttclid arrives as a URL parameter when a visitor lands from a TikTok ad, and _ttp is the first-party cookie the pixel sets. Both improve attribution on the server request, and both have to be persisted at landing time — the confirmation page is usually several navigations later, by which time the URL parameter is long gone:

// On landing: persist the click id for the rest of the visit
const ttclid = new URLSearchParams(window.location.search).get('ttclid');
if (ttclid) {
  document.cookie = 'shop_ttclid=' + encodeURIComponent(ttclid) +
                    ';max-age=2592000;path=/;secure;samesite=Lax';
}

At checkout, read both values from the cookies and store them on the order record so the backend can use them when building the server event. Store the visitor’s IP address and user agent from the original HTTP request in that record too. A later request from the server does not provide those visitor details on its own.

Step 4: The Server Request

The Events API takes a POST to the event/track/ endpoint of the TikTok Business API, authenticated with an access token from TikTok Events Manager and addressed to a pixel through event_source_id. Identifying fields are normalised and then SHA-256 hashed; nothing identifying leaves in plain text:

{
  "event_source": "web",
  "event_source_id": "PIXEL_CODE",
  "data": [{
    "event": "CompletePayment",
    "event_time": 1789000000,
    "event_id": "purchase-10432",
    "user": {
      "email": "1f3a...",
      "phone": "9c22...",
      "external_id": "7b41...",
      "ttclid": "E.C.P.xxxxx",
      "ttp": "2ABCDe...",
      "ip": "203.0.113.7",
      "user_agent": "Mozilla/5.0 ..."
    },
    "properties": {
      "contents": [
        { "content_id": "SKU-8891", "content_type": "product", "quantity": 1, "price": 149.00 }
      ],
      "currency": "PLN",
      "value": 149.00
    },
    "page": { "url": "https://shop.example.com/thank-you" }
  }]
}

Normalisation before hashing decides whether the identifiers match anything: email trimmed and lowercased, phone reduced to digits in international E.164 form. The same value hashed from Anna@Example.com  and from anna@example.com produces two unrelated digests, and only one of them can match. event_time is a Unix timestamp in seconds, and the request belongs immediately after order confirmation rather than in a nightly batch. TikTok rejects events that arrive too late, and a batch job discovers that only once the window has already closed.

Step 5: Verifying That Deduplication Works

The test_event_code field, taken from the Test Events tab in Events Manager, routes a request into the test view without touching reporting — enough to confirm that the payload is accepted and that the hashed fields are recognised.

Deduplication itself only shows up in live data. In Events Manager, an event receiving both channels reports its browser and server counts separately, and the difference between their sum and the deduplicated total is the evidence that pairing is working. A total that equals the sum of both channels means nothing is being matched — usually a mismatched event name, or an event_id generated separately on each side. Comparing the deduplicated count against actual orders in the shop backend is the check that matters; TikTok’s own numbers cannot reveal a purchase that neither channel reported.

Staying Inside the Guardrails

Both channels belong behind the same advertising-consent signal. A server-side request is not exempt from consent because it originates in a backend: it carries the same customer data, and the lawful basis for sending it is the one collected in the browser. That means the consent state has to be stored with the order and checked before the request is built. Raw email addresses and phone numbers exist only long enough to be normalised and hashed, and the access token belongs in a secrets store rather than in application code, since it authorises writing conversions into the ad account.

Questions and answers

Why does the deduplicated count still fall short of the shop’s orders with the Events API in place?

Because the Events API closes only one of two gaps. The server recovers purchases that blockers, tracking protection or a closed tab took away from the pixel. It rightly does not send purchases without advertising consent, because both channels sit behind the same consent signal. Those orders are permanently missing at TikTok, and that is not a measurement error.

The comparison from step 5 therefore only becomes meaningful when both sides cover the same population: the deduplicated count against the orders whose stored consent state records consent. A gap that remains there lies in the shop’s own chain, such as a server event that was never built, a rejected request or an event that arrived too late.

Storing the Events API response on the order helps. Every order can then be assigned to one of four states: not sent for lack of consent, not sent despite consent, sent and rejected, sent and accepted. Only the last belongs in the figure TikTok should show, and each of the two middle ones is a fault that can be fixed.

Does the shop_ttclid cookie last the full 30 days in every browser?

No. max-age=2592000 equals 30 days, but Safari lets cookies set by a script through document.cookie expire after seven days at most, and possibly after 24 hours if the visit came through a link with parameters from a domain classified as a tracker. Keeping the click ID longer requires the shop server itself to set the cookie with a Set-Cookie header; the pixel also sets _ttp by script, and the same limit applies to that cookie.

Do value and basket have to match between the browser event and the server event?

Yes. After deduplication only one of the two reports counts, and if value, currency or contents differ, the reported revenue depends on which one it is. The safest approach is to build both from the same order record that also supplies the event_id: gross or net, with or without shipping, is decided once and not per channel.

Lukas Wojcik

Lukas Wojcik

Systems architect and technology enthusiast specializing in scalable tracking solutions, GMP Stack (GA4 & GTM), and robust backend architectures. Advocate for clean code and privacy-first design.

Get in Touch

Briefly describe your project or inquiry for a tailored response. This site is protected by reCAPTCHA.

Write a comment

Practical experience and questions about the implementation are welcome here.

The email address is not published. Required fields are marked with an asterisk.

Articles & categories

CCTV

All 6 articles in this category Follow this category by RSS

Cloud & AI

All 18 articles in this category Follow this category by RSS

Data Privacy

All 20 articles in this category Follow this category by RSS

Digital Analytics

All 60 articles in this category Follow this category by RSS

Digital Marketing

All 38 articles in this category Follow this category by RSS

IT & Networks

All 19 articles in this category Follow this category by RSS

Music Production

All 18 articles in this category Follow this category by RSS

Raspberry PI

All 11 articles in this category Follow this category by RSS

SaaS & Internet Earning

Follow this category by RSS

Smart Home

All 18 articles in this category Follow this category by RSS

Web Development

All 11 articles in this category Follow this category by RSS

WordPress Plugins & Tricks

All 15 articles in this category Follow this category by RSS