> Part of the walkerOS documentation. Project overview and full index: <https://www.walkeros.io/llms.txt>

# TikTok Events API

[Server](#)[ Source code](https://github.com/elbwalker/walkerOS/tree/main/packages/server/destinations/tiktok)[ Package](https://www.npmjs.com/package/@walkeros/server-destination-tiktok)

Server-side event delivery to TikTok's Events API for enhanced conversion tracking, bypassing browser limitations and improving match quality for ad optimization.

Where this fits

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[​](#installation "Direct link to Installation")

```bash
npm install @walkeros/server-destination-tiktok
```

* Integrated
* Bundled

```typescript
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:

```json
"destinations": {
  "tiktok": {
    "package": "@walkeros/server-destination-tiktok",
    "import": "destinationTikTok",
    "config": {
      "settings": {
        "accessToken": "YOUR_ACCESS_TOKEN",
        "pixelCode": "YOUR_PIXEL_CODE"
      }
    }
  }
}
```

[See bundled mode setup](/preview/pr-749/docs/getting-started/modes/bundled.md) | [CLI reference](/preview/pr-749/docs/apps/cli.md)

## Configuration[​](#configuration "Direct link to Configuration")

This destination uses the standard destination config wrapper (consent, data, env, id, ...). For the shared fields see [destination configuration](/docs/destinations#configuration). Package-specific fields live under `config.settings` and are listed below.

## Settings[​](#settings "Direct link to 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. |      |

\* Required fields

## Mapping[​](#mapping "Direct link to Mapping")

This package does not define custom rule-level settings. For the standard rule fields (consent, condition, data, batch, name, policy) see [mapping](/docs/mapping).

## Examples

### Add to cart

A product add is sent to TikTok as an AddToCart event with value, currency, and product contents.

Event

```json
{
  "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"
  }
}
```

Mapping

```json
{
  "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"
            }
          }
        ]
      }
    }
  }
}
```

Out

```javascript
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.

Event

```json
{
  "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"
  }
}
```

Mapping

```json
{
  "name": "SubmitForm",
  "data": {
    "map": {
      "user_data": {
        "map": {
          "email": "user.email"
        }
      }
    }
  }
}
```

Out

```javascript
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.

Event

```json
{
  "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/"
  }
}
```

Out

```javascript
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.

Event

```json
{
  "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"
  }
}
```

Mapping

```json
{
  "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"
        }
      }
    }
  }
}
```

Out

```javascript
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[​](#client-ip-and-user-agent "Direct link to 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](/preview/pr-749/docs/sources/server/express.md#ingest-metadata)). 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[​](#hashing "Direct link to 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:

```json
"settings": {
  "accessToken": "YOUR_ACCESS_TOKEN",
  "pixelCode": "YOUR_PIXEL_CODE",
  "doNotHash": ["email"]
}
```

## Deduplication[​](#deduplication "Direct link to Deduplication")

Each event is sent with `event_id` set to the walkerOS event `id`. If you also run the [TikTok browser pixel](/preview/pr-749/docs/destinations/web/tiktok.md), TikTok deduplicates server and browser events that share the same `event_id`, so conversions are not double-counted.
