TikTok Events API in n8n: server-side tracking for your Ads campaigns
Published 13 August 2026 · 6 min read
Since Apple introduced App Tracking Transparency and third-party script blockers became mainstream, a growing share of the conversions coming out of a TikTok Ads campaign never reach the Pixel installed on a site: private browsing, a blocking extension, or simply a refused measurement consent. Alessandro Acquisti, Curtis Taylor, and Liad Wagman, in their landmark survey The Economics of Privacy (Journal of Economic Literature, 2016), note that this tightening of data-collection rules isn't merely a technical obstacle: it directly redistributes economic value between platforms, advertisers, and users by reducing the amount of signal available to measure a campaign's real effectiveness. Like Meta and LinkedIn before it — see our Meta Conversions API guide and our LinkedIn Conversions API guide — TikTok answers this signal loss with its own Events API: a direct send from your server that no browser-side blocker can intercept. This guide shows how to build that pipeline in n8n, without relying on a dedicated node, since none exists.
Why the Events API matters for TikTok Ads campaigns
The TikTok Pixel, like any client-side tag, depends entirely on what the visitor's browser lets execute. An ad blocker, Safari's ITP, or simply a user closing the tab before the script fully loads is enough to make a real conversion disappear. TikTok Ads Manager displays an Event Match Quality (EMQ) score per pixel: the more events arrive with reliable matching identifiers (email, phone, ttclid), the better the bidding algorithm optimizes campaigns. A well-built server-side flow mechanically improves that score, since it guarantees a conversion event reaches TikTok even when the browser channel failed.
How the TikTok Events API works
Concretely, the Events API is an HTTP endpoint: a POST to https://business-api.tiktok.com/open_api/v1.3/event/track/ (check the current version in TikTok Business documentation at implementation time), with the access token passed in an Access-Token header — generated from the Events API tab of the events manager, for the relevant pixel. The request body describes one or more events in a data[] array, including:
event—CompletePayment,AddToCart,SubmitForm, or a custom event name;event_time— the timestamp of the actual action, not of the API call;event_id— a unique identifier shared with the Pixel for deduplication;user— the person's identifiers, hashed (see Step 2);properties—value,currency(ISO 4217 code),content_id,content_type, depending on the event.
Deduplication via event_id
If the Pixel already captures part of your conversions client-side, sending the same conversion a second time through the Events API would double-count it — unless you share an identical event_id between the two sends. TikTok then matches the two events and keeps only one for reporting and bid optimization. As with Meta's Conversions API, the simplest approach in an n8n workflow is to generate that identifier once (a UUID, via the Crypto node's Generate operation — covered in our Crypto node guide) and pass it both to the front-end Pixel script and to the server-side call.
Building the workflow in n8n
Step 1 — Trigger on the actual conversion event
The workflow starts on the business event that constitutes the conversion: a confirmed-order webhook (see our Shopify connection guide and our article on automating e-commerce orders), or a confirmed-payment webhook from Stripe as described in our article on Stripe webhooks and payment follow-ups. Trigger the send on the actual confirmation of the action (payment captured), not on order creation, so you don't pollute your ad statistics with conversions that never actually completed.
Step 2 — Normalize and hash user data
The user block accepts the customer's email and phone, but only in SHA-256-hashed form, after normalization: lowercase, no whitespace, phone in E.164 format. A Code node placed right after the trigger prepares these fields:
const crypto = require('crypto');
const hash = (value) =>
crypto.createHash('sha256').update(value.trim().toLowerCase()).digest('hex');
const email = hash($json.customer_email);
const phone = hash($json.customer_phone.replace(/[^0-9]/g, ''));
return [{ json: { ...$json, email_hash: email, phone_hash: phone } }];
The Crypto node's Hash operation (SHA256, HEX output) does the exact same computation without code, if you'd rather stick to standard nodes. By contrast, the client IP address, the user agent, and the ttclid click identifier (captured client-side, in the URL parameter appended when a visitor returns from a TikTok ad) must travel in plain text: hashing them would make them useless for the identity matching TikTok performs on its end.
Step 3 — Build the payload and call the API
A Set node assembles the final JSON in the expected format, then an HTTP Request node sends a POST, with the Access-Token header populated from a dedicated n8n credential:
{
"event_source": "web",
"event_source_id": "{{ $json.pixel_id }}",
"data": [{
"event": "CompletePayment",
"event_time": 1755000000,
"event_id": "{{ $json.event_id }}",
"user": {
"email": ["{{ $json.email_hash }}"],
"phone": ["{{ $json.phone_hash }}"],
"ttclid": "{{ $json.ttclid }}"
},
"properties": {
"content_id": "{{ $json.product_id }}",
"content_type": "product",
"currency": "EUR",
"value": "{{ $json.order_total }}"
},
"page": {
"url": "{{ $json.checkout_url }}"
}
}]
}
The access token belongs in a dedicated n8n credential, never hardcoded into the node — the same discipline covered in our guide on securing credentials and API keys. As with any external API call, an order webhook that fires twice for the same transaction (a network redelivery, a double click) needs to be filtered upstream: the same principle as webhook idempotency, applied here to the send toward TikTok rather than to the business processing itself.
Step 4 — Verify with the test_event_code
Before publishing the workflow, temporarily add a test_event_code field to the payload — generated from the Events API tab of the events manager, for the relevant pixel. The event then shows up within seconds under the Test Events tab of TikTok's interface, with the full detail of the fields received and any warnings (a malformed email, a missing event_id). Once validated, remove that field: leaving it in production would keep the event out of your real statistics.
No native TikTok node in n8n: HTTP Request is enough
Unlike Meta (native Facebook Graph API node) or dozens of other services, n8n currently ships no native node covering the TikTok Events API. A few community nodes exist for other use cases (video publishing, post status), but none currently cover this specific one. That's not a blocker: a plain HTTP Request node, with the Access-Token header and the JSON body described above, reproduces exactly what a dedicated node would do — the same approach covered in our community nodes guide for any integration that doesn't yet have an official node.
Best practices and common pitfalls
- Never hash the IP address, user agent, or
ttclid— these fields must stay in plain text, unlike personal identifiers. - Generate the
event_idonce per conversion and pass the exact same value to both the front-end Pixel and the server-side call, or every conversion gets double-counted. - Trigger on the actual confirmation, not on order creation, which could still be canceled or left unpaid.
- Keep test and production environments clearly separated: a
test_event_codeleft in production silently drops real conversions from your statistics. - The same general pattern — server-side send, hashed identifiers, deduplication via a shared identifier — applies almost identically to the three other server-side tracking APIs we've documented: GA4 Measurement Protocol, Meta Conversions API, and LinkedIn Conversions API. Once the first pipeline is built in n8n, the next ones reuse the same node structure with a different endpoint and payload format.
In summary
Building a TikTok Events API pipeline in n8n comes down to four pieces: a trigger on the actual conversion (a confirmed order or payment), normalization and SHA-256 hashing of personal identifiers, a structured HTTP call to the event/track/ endpoint with an event_id shared with the Pixel, and verification through the test_event_code before going live. No dedicated node is required — a Webhook node, a Crypto or Code node, and an HTTP Request node are enough. If your n8n instance already handles your e-commerce orders or payment webhooks, as the FlowKit packs workflows do, adding this send only takes one more node in an existing workflow rather than a new integration built from scratch.
FAQ
Frequently asked questions
Should you disable the TikTok Pixel once the Events API is in place?
No. As with Meta's Conversions API, the two channels are designed to coexist: the Pixel captures whatever the browser lets through, the Events API captures the same event from your server, where nothing can block it. Sending the same event_id from both sides lets TikTok automatically deduplicate and count the event only once.
Which fields need to be hashed before sending them to the Events API?
The identifiers in the user block — email, phone, and external_id (your internal customer ID, if you use it as a matching method) — get SHA-256 hashed after normalization: lowercase, no whitespace, phone in E.164 format. The client IP address, user agent, and the ttclid click identifier must travel in plain text: hashing them would make them useless to TikTok.
Is there a native TikTok node in n8n for the Events API?
No, as of now n8n has no native node covering the TikTok Events API. A plain HTTP Request node doing a POST to the business-api.tiktok.com endpoint, with the token in an Access-Token header, is enough and reproduces exactly what a dedicated node would do. A few community nodes exist for other TikTok use cases (video publishing, post status), but none currently cover this specific case.
How do you test an event before sending it in production?
The test_event_code field, generated from the Events API tab of TikTok's events manager for your pixel, gets added to the payload for the duration of testing: the event then shows up under the Test Events tab of the interface without being counted in the campaign's real statistics. Remove that field before publishing the workflow for good.
Bundle FlowKit Complet
€269