The TikTok Events API works a lot like Meta's CAPI, and if you already run server-side GTM, it's a natural place to put it. The traps are the same too: duplicated events and weak matching.

Here's how to send TikTok events server-side without losing or double counting them.

What you're connecting

The browser pixel fires as usual and drops the ttclid into a cookie. Your server container holds a TikTok tag that rebuilds the event and sends it to the Events API with an access token.

As with Meta, the pixel is for coverage and the server call is the reliable copy. A shared event_id keeps TikTok from counting both.

Setup, step by step

  1. Capture the ttclid on the landing page and store it, so you can send it as the ttp value later.
  2. Forward web events to your server container, the same client you already use for GA4 or Meta.
  3. Add the TikTok Events API tag in the server container with your pixel code and access token.
  4. Send a shared event_id on both the pixel and the server event so they deduplicate.
  5. Hash email and phone with SHA-256, and pass the real client IP and user agent from the request.

The server sends TikTok something like this:

{
  "event": "CompletePayment",
  "event_id": "order_10432",
  "event_time": 1699999999,
  "user": {
    "email": "<sha256_email>",
    "ttp": "<ttclid cookie>",
    "ip": "<from request>",
    "user_agent": "<from request>"
  },
  "properties": { "value": 49.00, "currency": "USD" }
}

Test it properly

TikTok gives you a test_event_code you add to the payload while testing. Fire a real event and watch it land in the Events Manager test view before you remove the code and go live.

Where it breaks

  • No shared event_id, so the pixel and Events API events both count.
  • The ttclid was never captured, so ttp is empty and matching suffers.
  • Unhashed email or phone, which TikTok rejects, so the identifiers do nothing.
  • The test_event_code is left in production, so live events keep landing in the test view.

Before you start

Get an Events API access token from TikTok Events Manager and store it as a server-container variable, never on the page. Confirm your pixel is already firing standard events in the browser, because the server events mirror those, not replace them.

Map your events to TikTok's expected names up front. A checkout is CompletePayment, a lead is a different event, and sending a name TikTok doesn't recognize means the optimization signal lands nowhere useful.

Matching is the whole game

TikTok can only optimize on events it can tie to a person, so the identifiers matter more than the event itself. Send hashed email and phone, the ttp value built from the ttclid, and the real IP and user agent from the request.

The ttclid is the piece most setups miss. If you never captured it on the landing page, ttp is empty and your match rate stays low no matter how clean the rest is.

What good looks like

In Events Manager you want each event received from both the pixel and the server, deduplicated, with a match rate that isn't embarrassing. Fire a real purchase, confirm it once with the test_event_code, then remove the code so live traffic stops landing in the test view.

Test a lead and a purchase, not just one event, since match rates often differ sharply between them and an average hides the weak one. Fix the weak one before you scale spend behind it.

TikTok's Events API is Meta CAPI with different field names, and server-side GTM handles it the same way. Share the event_id, capture the ttclid, hash the user data, and your TikTok events stop leaking and stop doubling.

Want a stronger data analyst role or a raise? Grab the FREE Product Analyst Playbook and get the exact roadmap to your next offer.