Skip to main content

TikTok Pixel

Web Source code Package

The TikTok Pixel forwards browser conversions to TikTok Ads for optimization, audience building, and reporting. This destination wraps the standard TikTok Pixel snippet (loaded from analytics.tiktok.com/i18n/pixel/events.js); there is no npm SDK for the browser pixel. Events become ttq.track(name, params, { event_id }) calls; identity becomes ttq.identify({ email, phone_number, external_id }); consent toggles ttq.enableCookie / ttq.disableCookie.

Where this fits

TikTok Pixel is a web destination in the walkerOS flow:

Installation

npm install @walkeros/web-destination-tiktok
import { startFlow } from '@walkeros/collector';
import { destinationTikTok } from '@walkeros/web-destination-tiktok';

await startFlow({
consent: { marketing: false },
destinations: {
  tiktok: {
    code: destinationTikTok,
    config: {
      consent: { marketing: true },
      settings: {
        apiKey: 'C4XXXXXXXXXXXXXXXXXXX',
      },
    },
  },
},
});

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

PropertyTypeDescriptionMore
apiKey*stringYour TikTok Pixel ID. Find it in TikTok Ads Manager under "Assets" → "Events" → "Web Events" → select your pixel → "Setup Web Event" → "Pixel ID".
auto_configbooleanTikTok default: true. Enable automatic form field detection for Advanced Matching.
limited_data_usebooleanTikTok default: false. Restrict data use for compliance with U.S. state privacy laws.
identifyanywalkerOS mapping value resolving to an Advanced Matching object with any of: email, phone_number, external_id. All values are auto-hashed (SHA256) by the TikTok SDK before sending.
* Required fields

Mapping

Per-event rules under config.mapping. For the standard rule fields (consent, condition, data, batch, name, policy) see mapping.

PropertyTypeDescriptionMore
identifyanyPer-event Advanced Matching mapping. Resolves to an object with any of: email, phone_number, external_id. Overrides destination-level settings.identify for this rule.

Examples

Consent granted

A walker consent grant for marketing calls ttq.enableCookie so TikTok can set and read its attribution cookie.

Event
{
  "marketing": true
}
Out
ttq.enableCookie()

Consent revoked

A walker consent revoke for marketing calls ttq.disableCookie so TikTok stops using its first-party cookie.

Event
{
  "marketing": false
}
Out
ttq.enableCookie();

ttq.disableCookie()

Default track

Without a mapping the walker event name is forwarded to ttq.track with an event_id for TikTok dedup.

Event
{
  "name": "product view",
  "data": {
    "id": "ers",
    "name": "Everyday Ruck Snack",
    "color": "black",
    "size": "l",
    "price": 420
  },
  "context": {
    "shopping": [
      "detail",
      0
    ]
  },
  "globals": {
    "pagegroup": "shop"
  },
  "custom": {
    "completely": "random"
  },
  "user": {
    "id": "us3r",
    "device": "c00k13",
    "session": "s3ss10n"
  },
  "nested": [],
  "consent": {
    "functional": true
  },
  "id": "0123456789abcdef",
  "trigger": "load",
  "entity": "product",
  "action": "view",
  "timestamp": 1783556017000,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "collector",
    "schema": "4"
  }
}
Out
ttq.track("product view", {}, {
  "event_id": "0123456789abcdef"
})

Advanced matching

Destination-level identify calls ttq.identify with email, phone, and external id for TikTok advanced matching.

Event
{
  "name": "page view",
  "data": {
    "email": "user@acme.com",
    "phone": "+14135552671"
  },
  "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": "44444444dddddddd",
  "trigger": "load",
  "entity": "page",
  "action": "view",
  "timestamp": 1783556017000,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "collector",
    "schema": "4"
  }
}
Out
ttq.identify({
  "email": "user@acme.com",
  "phone_number": "+14135552671",
  "external_id": "us3r"
});

ttq.track("page view", {}, {
  "event_id": "44444444dddddddd"
})

Include data

Destination-level include flattens the event data section into prefixed TikTok event parameters.

Event
{
  "name": "product view",
  "data": {
    "id": "ers",
    "name": "Everyday Ruck Snack",
    "color": "black",
    "size": "l",
    "price": 420
  },
  "context": {
    "shopping": [
      "detail",
      0
    ]
  },
  "globals": {
    "pagegroup": "shop"
  },
  "custom": {
    "completely": "random"
  },
  "user": {
    "id": "us3r",
    "device": "c00k13",
    "session": "s3ss10n"
  },
  "nested": [],
  "consent": {
    "functional": true
  },
  "id": "22222222bbbbbbbb",
  "trigger": "load",
  "entity": "product",
  "action": "view",
  "timestamp": 1783556017000,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "collector",
    "schema": "4"
  }
}
Out
ttq.track("product view", {
  "data_id": "ers",
  "data_name": "Everyday Ruck Snack",
  "data_color": "black",
  "data_size": "l",
  "data_price": 420
}, {
  "event_id": "22222222bbbbbbbb"
})

Complete payment

A completed order is mapped to TikTok CompletePayment with value, currency, and nested product contents.

Event
{
  "name": "order complete",
  "data": {
    "id": "0rd3r1d",
    "currency": "EUR",
    "shipping": 5.22,
    "taxes": 73.76,
    "total": 555
  },
  "context": {
    "shopping": [
      "complete",
      0
    ]
  },
  "globals": {
    "pagegroup": "shop"
  },
  "custom": {
    "completely": "random"
  },
  "user": {
    "id": "us3r",
    "device": "c00k13",
    "session": "s3ss10n"
  },
  "nested": [
    {
      "entity": "product",
      "data": {
        "id": "ers",
        "name": "Everyday Ruck Snack",
        "color": "black",
        "size": "l",
        "price": 420
      },
      "context": {
        "shopping": [
          "complete",
          0
        ]
      },
      "nested": []
    },
    {
      "entity": "product",
      "data": {
        "id": "cc",
        "name": "Cool Cap",
        "size": "one size",
        "price": 42
      },
      "context": {
        "shopping": [
          "complete",
          0
        ]
      },
      "nested": []
    },
    {
      "entity": "gift",
      "data": {
        "name": "Surprise"
      },
      "context": {
        "shopping": [
          "complete",
          0
        ]
      },
      "nested": []
    }
  ],
  "consent": {
    "functional": true
  },
  "id": "66666666ffffffff",
  "trigger": "load",
  "entity": "order",
  "action": "complete",
  "timestamp": 1783556017000,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "collector",
    "schema": "4"
  }
}
Mapping
{
  "name": "CompletePayment",
  "data": {
    "map": {
      "content_type": {
        "value": "product"
      },
      "value": "data.total",
      "currency": {
        "key": "data.currency",
        "value": "EUR"
      },
      "order_id": "data.id",
      "contents": {
        "loop": [
          "nested",
          {
            "map": {
              "content_id": "data.id",
              "content_name": "data.name",
              "quantity": {
                "key": "data.quantity",
                "value": 1
              },
              "price": "data.price"
            }
          }
        ]
      }
    }
  },
  "include": []
}
Out
ttq.track("CompletePayment", {
  "content_type": "product",
  "value": 555,
  "currency": "EUR",
  "order_id": "0rd3r1d",
  "contents": [
    {
      "content_id": "ers",
      "content_name": "Everyday Ruck Snack",
      "quantity": 1,
      "price": 420
    },
    {
      "content_id": "cc",
      "content_name": "Cool Cap",
      "quantity": 1,
      "price": 42
    },
    {
      "content_name": "Surprise",
      "quantity": 1
    }
  ]
}, {
  "event_id": "66666666ffffffff"
})

View content

A product view is renamed to the TikTok ViewContent standard event with content_type, id, value, and currency.

Event
{
  "name": "product view",
  "data": {
    "id": "ers",
    "name": "Everyday Ruck Snack",
    "color": "black",
    "size": "l",
    "price": 420
  },
  "context": {
    "shopping": [
      "detail",
      0
    ]
  },
  "globals": {
    "pagegroup": "shop"
  },
  "custom": {
    "completely": "random"
  },
  "user": {
    "id": "us3r",
    "device": "c00k13",
    "session": "s3ss10n"
  },
  "nested": [],
  "consent": {
    "functional": true
  },
  "id": "11111111aaaaaaaa",
  "trigger": "load",
  "entity": "product",
  "action": "view",
  "timestamp": 1783556017000,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "collector",
    "schema": "4"
  }
}
Mapping
{
  "name": "ViewContent",
  "data": {
    "map": {
      "content_type": {
        "value": "product"
      },
      "content_id": "data.id",
      "content_name": "data.name",
      "content_category": "data.category",
      "value": "data.price",
      "currency": {
        "key": "globals.currency",
        "value": "EUR"
      }
    }
  },
  "include": []
}
Out
ttq.track("ViewContent", {
  "content_type": "product",
  "content_id": "ers",
  "content_name": "Everyday Ruck Snack",
  "value": 420,
  "currency": "EUR"
}, {
  "event_id": "11111111aaaaaaaa"
})

Rule include overrides

A per-rule include replaces the destination-level include so this event forwards only globals.

Event
{
  "name": "order complete",
  "data": {
    "id": "0rd3r1d",
    "currency": "EUR",
    "shipping": 5.22,
    "taxes": 73.76,
    "total": 555
  },
  "context": {
    "shopping": [
      "complete",
      0
    ]
  },
  "globals": {
    "pagegroup": "shop"
  },
  "custom": {
    "completely": "random"
  },
  "user": {
    "id": "us3r",
    "device": "c00k13",
    "session": "s3ss10n"
  },
  "nested": [
    {
      "entity": "product",
      "data": {
        "id": "ers",
        "name": "Everyday Ruck Snack",
        "color": "black",
        "size": "l",
        "price": 420
      },
      "context": {
        "shopping": [
          "complete",
          0
        ]
      },
      "nested": []
    },
    {
      "entity": "product",
      "data": {
        "id": "cc",
        "name": "Cool Cap",
        "size": "one size",
        "price": 42
      },
      "context": {
        "shopping": [
          "complete",
          0
        ]
      },
      "nested": []
    },
    {
      "entity": "gift",
      "data": {
        "name": "Surprise"
      },
      "context": {
        "shopping": [
          "complete",
          0
        ]
      },
      "nested": []
    }
  ],
  "consent": {
    "functional": true
  },
  "id": "33333333cccccccc",
  "trigger": "load",
  "entity": "order",
  "action": "complete",
  "timestamp": 1783556017000,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "collector",
    "schema": "4"
  }
}
Mapping
{
  "include": [
    "globals"
  ]
}
Out
ttq.track("order complete", {
  "globals_pagegroup": "shop"
}, {
  "event_id": "33333333cccccccc"
})

Search

A search submit fires TikTok Search with the query field mapped from event data.

Event
{
  "name": "search submit",
  "data": {
    "term": "hiking backpack"
  },
  "context": {
    "dev": [
      "test",
      1
    ]
  },
  "globals": {
    "lang": "elb"
  },
  "custom": {
    "completely": "random"
  },
  "user": {
    "id": "us3r",
    "device": "c00k13",
    "session": "s3ss10n"
  },
  "nested": [
    {
      "entity": "child",
      "data": {
        "is": "subordinated"
      }
    }
  ],
  "consent": {
    "functional": true
  },
  "id": "777777770000aaaa",
  "trigger": "test",
  "entity": "search",
  "action": "submit",
  "timestamp": 1783556017000,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "collector",
    "schema": "4"
  }
}
Mapping
{
  "name": "Search",
  "data": {
    "map": {
      "query": "data.term"
    }
  },
  "include": []
}
Out
ttq.track("Search", {
  "query": "hiking backpack"
}, {
  "event_id": "777777770000aaaa"
})

Complete registration

A user register fires ttq.identify for advanced matching and then tracks CompleteRegistration.

Event
{
  "name": "user register",
  "data": {
    "email": "new@acme.com",
    "phone": "+14135551234",
    "user_id": "new-user-123"
  },
  "context": {
    "dev": [
      "test",
      1
    ]
  },
  "globals": {
    "lang": "elb"
  },
  "custom": {
    "completely": "random"
  },
  "user": {
    "id": "us3r",
    "device": "c00k13",
    "session": "s3ss10n"
  },
  "nested": [
    {
      "entity": "child",
      "data": {
        "is": "subordinated"
      }
    }
  ],
  "consent": {
    "functional": true
  },
  "id": "55555555eeeeeeee",
  "trigger": "test",
  "entity": "user",
  "action": "register",
  "timestamp": 1783556017000,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "collector",
    "schema": "4"
  }
}
Mapping
{
  "name": "CompleteRegistration",
  "include": [],
  "data": {
    "map": {
      "content_type": {
        "value": "product"
      }
    }
  },
  "settings": {
    "identify": {
      "map": {
        "email": "data.email",
        "phone_number": "data.phone",
        "external_id": "data.user_id"
      }
    }
  }
}
Out
ttq.identify({
  "email": "new@acme.com",
  "phone_number": "+14135551234",
  "external_id": "new-user-123"
});

ttq.track("CompleteRegistration", {
  "content_type": "product"
}, {
  "event_id": "55555555eeeeeeee"
})

Standard events

TikTok recognizes 14 standard events for ad optimization. Use mapping.name to flip walkerOS event names into the rigid taxonomy. Custom names still work but receive no optimization signal.

TikTok nameWhen to use
ViewContentProduct / content detail view
ClickButtonGeneric CTA click
SearchSite search
AddToWishlistItem added to a wishlist
AddToCartItem added to cart
InitiateCheckoutUser started checkout
AddPaymentInfoPayment details entered
CompletePaymentOrder successfully placed (primary conversion)
PlaceAnOrderOrder placed (alternative)
ContactLead / contact form
DownloadAsset download
SubmitFormGeneric form submission
CompleteRegistrationUser signup
SubscribeSubscription start

Advanced matching

TikTok matches conversions back to TikTok users via three optional parameters: email, phone_number, external_id. The SDK hashes them with SHA256 before sending, so it's safe to pass raw values.

settings.identify (destination-level) fires on first push and re-fires only when the resolved value changes (runtime state diffing). mapping.settings.identify (per-event) overrides for one push, which is useful for signup and checkout flows where the identity becomes available mid-session.

ttq.identify() always fires before ttq.track() so Advanced Matching is set for the conversion event.

TikTok is an advertising platform. The typical walkerOS consent key is marketing (whatever name your CMP reports). The walkerOS config.consent gate blocks unconsented events from reaching the destination in the first place; the destination's on('consent') handler then toggles TikTok's own cookie state for attribution.

config: {
consent: { marketing: true },
settings: { apiKey: 'C4XXXXXXXXXXXXXXXXXXX' },
}

The handler iterates every key in config.consent and calls ttq.enableCookie() only when all required keys are granted. Otherwise it calls ttq.disableCookie(). This is the conservative semantic that matches walkerOS's gating model.

Auto page view

TikTok's SDK auto-fires ttq.page() once on load. The destination has no knob to suppress it. Letting the auto page view fire is the expected behavior.

💡 Need implementation support?
elbwalker offers hands-on support: setup review, measurement planning, destination mapping, and live troubleshooting. Book a 2-hour session (€399)