TikTok Events API
Server-side event delivery to TikTok's Events API for enhanced conversion tracking, bypassing browser limitations and improving match quality for ad optimization.
TikTok Events API is a server destination in the walkerOS flow:
Sends events server-side to TikTok's Events API using raw HTTP (no SDK). Identity fields (email, phone_number, external_id) are SHA-256 hashed before sending. Events include a stable `event_id` for deduplication with the browser pixel.
Installation
npm install @walkeros/server-destination-tiktok- Integrated
- Bundled
import { startFlow } from '@walkeros/collector';
import { destinationTikTok } from '@walkeros/server-destination-tiktok';
await startFlow({
destinations: {
tiktok: {
code: destinationTikTok,
config: {
settings: {
accessToken: 'YOUR_ACCESS_TOKEN',
pixelCode: 'YOUR_PIXEL_CODE',
},
},
},
},
});Add to your flow.json destinations:
"destinations": {
"tiktok": {
"package": "@walkeros/server-destination-tiktok",
"import": "destinationTikTok",
"config": {
"settings": {
"accessToken": "YOUR_ACCESS_TOKEN",
"pixelCode": "YOUR_PIXEL_CODE"
}
}
}
}Configuration
This destination uses the standard destination config wrapper (consent, data, env, id, ...). For the shared fields see destination configuration. Package-specific fields live under config.settings and are listed below.
Settings
| Property | Type | Description | More |
|---|---|---|---|
pixelCode | string | TikTok Pixel Code from Events Manager | |
accessToken | string | Events API access token from TikTok Events Manager | |
url | string | Custom Events API endpoint URL | |
test_event_code | string | Test event code for debugging in TikTok Events Manager | |
doNotHash | Array<string> | User data fields to skip hashing | |
user_data | Record<string, string> | Mapping for user data fields | |
partner_name | string | Partner name for TikTok attribution | |
ip | any | boolean | Client IP, sent as context.ip. Resolves against { ingest, event }. Default: ["ingest.ip", "event.user.ip"]. false switches it off. | |
userAgent | any | boolean | Client user agent, sent as context.user_agent. Resolves against { ingest, event }. Default: ["ingest.userAgent", "event.user.userAgent"]. false switches it off. |
Mapping
This package does not define custom rule-level settings. For the standard rule fields (consent, condition, data, batch, name, policy) see mapping.
Examples
Add to cart
A product add is sent to TikTok as an AddToCart event with value, currency, and product contents.
{
"name": "product add",
"data": {
"id": "SKU-B2",
"name": "Running Shoes",
"price": 89.99,
"color": "blue"
},
"context": {
"shopping": [
"intent",
0
]
},
"globals": {
"pagegroup": "shop"
},
"custom": {
"completely": "random"
},
"user": {
"id": "us3r",
"device": "c00k13",
"session": "s3ss10n"
},
"nested": [
{
"entity": "product",
"data": {
"id": "SKU-B2",
"name": "Running Shoes",
"price": 89.99,
"quantity": 1
}
}
],
"consent": {
"functional": true
},
"id": "d1e2f3a4b5c60002",
"trigger": "click",
"entity": "product",
"action": "add",
"timestamp": 1700000901000,
"timing": 3.14,
"source": {
"count": 1,
"trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
"type": "browser",
"platform": "web",
"url": "https://shop.example.com/products/running-shoes"
}
}{
"name": "AddToCart",
"data": {
"map": {
"content_type": {
"value": "product"
},
"value": "data.price",
"currency": {
"value": "EUR"
},
"contents": {
"loop": [
"nested",
{
"condition": {
"$code": "e=>k(e)&&\"product\"===e.entity"
},
"map": {
"content_id": "data.id",
"content_name": "data.name",
"quantity": {
"key": "data.quantity",
"value": 1
},
"price": "data.price"
}
}
]
}
}
}
}sendServer("https://business-api.tiktok.com/open_api/v1.3/event/track/", "{\"pixel_code\":\"PIXEL_CODE\",\"partner_name\":\"walkerOS\",\"data\":[{\"event\":\"AddToCart\",\"event_id\":\"d1e2f3a4b5c60002\",\"timestamp\":\"2023-11-14T22:28:21.000Z\",\"context\":{\"page\":{\"url\":\"https://shop.example.com/products/running-shoes\"}},\"properties\":{\"content_type\":\"product\",\"value\":89.99,\"currency\":\"EUR\",\"contents\":[{\"content_id\":\"SKU-B2\",\"content_name\":\"Running Shoes\",\"quantity\":1,\"price\":89.99}]}}]}", {
"headers": {
"Access-Token": "s3cr3t",
"Content-Type": "application/json"
}
})Submit form
A newsletter form submission is sent to TikTok as a SubmitForm event with the hashed email in user context.
{
"name": "form submit",
"data": {
"type": "newsletter"
},
"context": {
"dev": [
"test",
1
]
},
"globals": {
"lang": "elb"
},
"custom": {
"completely": "random"
},
"user": {
"email": "user@example.com"
},
"nested": [
{
"entity": "child",
"data": {
"is": "subordinated"
}
}
],
"consent": {
"functional": true
},
"id": "d1e2f3a4b5c60004",
"trigger": "test",
"entity": "form",
"action": "submit",
"timestamp": 1700000903000,
"timing": 3.14,
"source": {
"count": 1,
"trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
"type": "browser",
"platform": "web",
"url": "https://example.com/contact"
}
}{
"name": "SubmitForm",
"data": {
"map": {
"user_data": {
"map": {
"email": "user.email"
}
}
}
}
}sendServer("https://business-api.tiktok.com/open_api/v1.3/event/track/", "{\"pixel_code\":\"PIXEL_CODE\",\"partner_name\":\"walkerOS\",\"data\":[{\"event\":\"SubmitForm\",\"event_id\":\"d1e2f3a4b5c60004\",\"timestamp\":\"2023-11-14T22:28:23.000Z\",\"context\":{\"user\":{\"email\":\"b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514\"},\"page\":{\"url\":\"https://example.com/contact\"}},\"properties\":{}}]}", {
"headers": {
"Access-Token": "s3cr3t",
"Content-Type": "application/json"
}
})Page view
A page view is forwarded to TikTok as a page view event with the source URL in the page context.
{
"name": "page view",
"data": {
"domain": "www.example.com",
"title": "walkerOS documentation",
"referrer": "https://www.walkeros.io/",
"search": "?foo=bar",
"hash": "#hash",
"id": "/docs/"
},
"context": {
"dev": [
"test",
1
]
},
"globals": {
"pagegroup": "docs"
},
"custom": {
"completely": "random"
},
"user": {
"id": "us3r",
"device": "c00k13",
"session": "s3ss10n"
},
"nested": [
{
"entity": "child",
"data": {
"is": "subordinated"
}
}
],
"consent": {
"functional": true
},
"id": "d1e2f3a4b5c60003",
"trigger": "load",
"entity": "page",
"action": "view",
"timestamp": 1700000902000,
"timing": 3.14,
"source": {
"count": 1,
"trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
"type": "browser",
"platform": "web",
"url": "https://example.com/docs/"
}
}sendServer("https://business-api.tiktok.com/open_api/v1.3/event/track/", "{\"pixel_code\":\"PIXEL_CODE\",\"partner_name\":\"walkerOS\",\"data\":[{\"event\":\"page view\",\"event_id\":\"d1e2f3a4b5c60003\",\"timestamp\":\"2023-11-14T22:28:22.000Z\",\"context\":{\"page\":{\"url\":\"https://example.com/docs/\"}},\"properties\":{}}]}", {
"headers": {
"Access-Token": "s3cr3t",
"Content-Type": "application/json"
}
})Complete payment
A completed order is sent to the TikTok Events API as a CompletePayment with value, currency, and contents.
{
"name": "order complete",
"data": {
"id": "ORD-300",
"total": 249.99,
"currency": "EUR"
},
"context": {
"shopping": [
"complete",
0
]
},
"globals": {
"pagegroup": "shop"
},
"custom": {
"completely": "random"
},
"user": {
"id": "user-123",
"device": "device-456"
},
"nested": [
{
"entity": "product",
"data": {
"id": "SKU-A1",
"name": "Widget Pro",
"price": 124.99,
"quantity": 2
}
}
],
"consent": {
"functional": true
},
"id": "d1e2f3a4b5c60001",
"trigger": "load",
"entity": "order",
"action": "complete",
"timestamp": 1700000900000,
"timing": 3.14,
"source": {
"count": 1,
"trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
"type": "browser",
"platform": "web",
"url": "https://shop.example.com/checkout/complete"
}
}{
"name": "CompletePayment",
"data": {
"map": {
"value": "data.total",
"currency": {
"key": "data.currency",
"value": "EUR"
},
"order_id": "data.id",
"content_type": {
"value": "product"
},
"contents": {
"loop": [
"nested",
{
"condition": {
"$code": "e=>k(e)&&\"product\"===e.entity"
},
"map": {
"content_id": "data.id",
"content_name": "data.name",
"quantity": {
"key": "data.quantity",
"value": 1
},
"price": "data.price"
}
}
]
},
"user_data": {
"map": {
"external_id": "user.id"
}
}
}
}
}sendServer("https://business-api.tiktok.com/open_api/v1.3/event/track/", "{\"pixel_code\":\"PIXEL_CODE\",\"partner_name\":\"walkerOS\",\"data\":[{\"event\":\"CompletePayment\",\"event_id\":\"d1e2f3a4b5c60001\",\"timestamp\":\"2023-11-14T22:28:20.000Z\",\"context\":{\"user\":{\"external_id\":\"fcdec6df4d44dbc637c7c5b58efface52a7f8a88535423430255be0bb89bedd8\"},\"page\":{\"url\":\"https://shop.example.com/checkout/complete\"}},\"properties\":{\"value\":249.99,\"currency\":\"EUR\",\"order_id\":\"ORD-300\",\"content_type\":\"product\",\"contents\":[{\"content_id\":\"SKU-A1\",\"content_name\":\"Widget Pro\",\"quantity\":2,\"price\":124.99}]}}]}", {
"headers": {
"Access-Token": "s3cr3t",
"Content-Type": "application/json"
}
})Client IP and user agent
The destination sends the client IP as context.ip and the user agent as context.user_agent by default. The ip and userAgent settings resolve against { ingest, event } and default to ["ingest.ip", "event.user.ip"] and ["ingest.userAgent", "event.user.userAgent"], so they fill in whenever the source provides them (see the config.ingest recipe on the Express source). A value that resolves to nothing is not sent. Set either setting to false to turn it off, or to another mapping value to read it elsewhere.
Hashing
The destination automatically SHA-256 hashes email, phone_number, and external_id before sending to TikTok. Pass raw values and the hashing is handled for you. If your data is already hashed or you want to skip hashing for specific fields, use the doNotHash setting:
"settings": {
"accessToken": "YOUR_ACCESS_TOKEN",
"pixelCode": "YOUR_PIXEL_CODE",
"doNotHash": ["email"]
}Deduplication
Each event is sent with event_id set to the walkerOS event id. If you also run the TikTok browser pixel, TikTok deduplicates server and browser events that share the same event_id, so conversions are not double-counted.