Guides & Tutorials16 min read

Set Up TikTok Conversions API with OpenClaw: A Workflow Automation Guide

By ButterGrow Team

TL;DR

This tutorial shows exactly how to wire TikTok Conversions API to OpenClaw so you can send reliable server side purchase and signup events with strong validation, consent checks, and retries. You will configure secrets, map standard events, hash emails and phone numbers, and implement dedup using an event_id. It includes ready to use YAML and cURL snippets plus a checklist for go live. The goal is a production grade workflow automation that preserves attribution while staying privacy aware. It adds clear logging for audits.

What you will build

You will build an inbound HTTP hook in OpenClaw that receives normalized commerce events from your site or backend, validates and enriches them, and then posts to the TikTok Events API using your pixel and access token. The flow includes input schema validation, consent gating, idempotency and dedup, retries with backoff, and structured logging so you can trace every event.

  • Architecture summary:
    • Inbound webhook receives a JSON event (cart, checkout, purchase).
    • Validator checks required fields and types.
    • Enricher adds event_id, hashes identifiers, and attaches consent.
    • Sender posts to TikTok with retries and metrics.
    • Logger records request, response, and correlation ids.

If you are new to the product, skim the AI marketing automation features to understand what ButterGrow does and how OpenClaw runs playbooks inside the hosted platform. See AI marketing automation features at the feature set.

For a related server side example in another channel, compare the patterns in our server side Meta Conversions API guide. The shape of payloads differs, but the reliability tactics are the same.

Prerequisites

  • TikTok Ads account with a Pixel set up in Events Manager.
  • Pixel ID and an access token with permissions to send events.
  • An OpenClaw workspace with access to create playbooks in ButterGrow.
  • A source of commerce events from your app, storefront, or backend.

Tip: You can get started in minutes by following the onboarding flow and then creating a new playbook from a blank template.

Standard events and parameters

Before you write any code, decide which standard events you will send and what parameters you can populate from your system. Start with a small, high value set and expand as needed.

Use this as a working model when shaping your payload upstream and inside OpenClaw. The exact names in the TikTok Events API may vary, but these are the common concepts you will need to carry through.

Concept Example Notes
event Purchase, AddToCart, CompleteRegistration Standard event names used by TikTok.
timestamp 2026-08-13T15:06:39Z ISO 8601 in UTC.
event_id 550e8400-e29b-41d4-a716-446655440000 UUID v4 for dedup between web and server.
currency USD ISO 4217 currency code.
value 129.99 Monetary value for revenue events.
contents [{"item_id":"sku_123","price":64.99,"quantity":2}] Array of items for cart and purchase.
email user@example.com Normalize and hash before sending.
phone +15555550123 Normalize to E.164 and hash before sending.
ip 203.0.113.12 Useful when available for attribution.
user_agent Mozilla/5.0 ... Helps match server events to sessions.
consent true or false plus GPP string Gate sends on consent where required.

Create the OpenClaw playbook

Below is a minimal but production ready playbook you can paste into a new OpenClaw playbook. It exposes an authenticated webhook, validates input, enriches the payload, and sends to TikTok with retries and metrics. Replace placeholder environment variables with your values.

# file: playbooks/tiktok-events-api.yaml
name: tiktok-events-api
version: 1

triggers:
  - http:
      path: /hooks/tiktok
      method: POST
      auth: bearer
      secret: ${TIKTOK_HOOK_SECRET}

vars:
  # TikTok config
  TIKTOK_API_URL: ${TIKTOK_EVENTS_API_URL} # set from docs
  TIKTOK_PIXEL_ID: ${TIKTOK_PIXEL_ID}
  TIKTOK_ACCESS_TOKEN: ${TIKTOK_ACCESS_TOKEN}

steps:
  - id: validate
    type: schema.validate
    input: ${trigger.body}
    schema:
      type: object
      required: [event, timestamp, contents]
      properties:
        event: { type: string }
        timestamp: { type: string }
        event_id: { type: string }
        currency: { type: string }
        value: { type: number }
        contents:
          type: array
          items:
            type: object
            required: [item_id]
            properties:
              item_id: { type: string }
              price: { type: number }
              quantity: { type: integer }
        user:
          type: object
          properties:
            email: { type: string }
            phone: { type: string }
            ip: { type: string }
            user_agent: { type: string }
        consent:
          type: object
          properties:
            allowed: { type: boolean }
            gpp: { type: string }

  - id: enrich
    type: fn
    lang: js
    source: |
      import crypto from 'node:crypto';

      function uuidv4() {
        return crypto.randomUUID();
      }

      function sha256Hex(s) {
        return crypto.createHash('sha256').update(s).digest('hex');
      }

      function normalizeEmail(e) {
        return (e || '').trim().toLowerCase();
      }

      function normalizePhone(p) {
        // Expect E.164 from upstream when possible
        return (p || '').replace(/\s+/g, '');
      }

      const input = steps.validate.output;

      if (!input.consent || input.consent.allowed !== true) {
        return {
          action: 'suppress',
          reason: 'No consent',
          event: input.event,
        };
      }

      const event_id = input.event_id || uuidv4();
      const email_hashed = input?.user?.email ? sha256Hex(normalizeEmail(input.user.email)) : undefined;
      const phone_hashed = input?.user?.phone ? sha256Hex(normalizePhone(input.user.phone)) : undefined;

      const tiktokPayload = {
        pixel_code: env.TIKTOK_PIXEL_ID,
        event: input.event,
        event_id,
        timestamp: input.timestamp,
        context: {
          user: {
            email: email_hashed,
            phone_number: phone_hashed,
            ip: input?.user?.ip,
            user_agent: input?.user?.user_agent,
          },
          consent: {
            gpp: input?.consent?.gpp,
          },
        },
        properties: {
          currency: input.currency,
          value: input.value,
          contents: input.contents,
        },
      };

      return { action: 'send', event_id, payload: tiktokPayload };

  - id: maybe_send
    type: switch
    cases:
      - when: ${steps.enrich.output.action == 'suppress'}
        then:
          - id: suppressed_log
            type: log
            level: info
            message: ${`Suppressed ${steps.enrich.output.event} due to consent`}
      - when: ${steps.enrich.output.action == 'send'}
        then:
          - id: post
            type: http.request
            method: POST
            url: ${env.TIKTOK_API_URL}
            headers:
              Content-Type: application/json
              Access-Token: ${env.TIKTOK_ACCESS_TOKEN}
            body: ${steps.enrich.output.payload}
            retry:
              policy: exponential
              max_attempts: 6
              base_delay_ms: 500
              max_delay_ms: 60000
              retry_on: [429, 500, 502, 503, 504]
          - id: record
            type: log
            level: info
            message: ${`Sent event_id=${steps.enrich.output.event_id}`}

outputs:
  result: ${steps.maybe_send}

Notes:

  • TIKTOK_EVENTS_API_URL points to the TikTok endpoint documented in the official Events API docs. Keep it in env so you can change versions without code edits.
  • The function step handles hashing and dedup, and it suppresses sends when consent is false.
  • The HTTP step retries on transient failures and logs the final status.

Send a test event

From a terminal, post a small payload to your webhook. Replace values and secrets with your own. This simulates a purchase with two items and realistic identifiers.

curl -X POST \
  https://YOUR-OPENCLAW-DOMAIN/hooks/tiktok \
  -H "Authorization: Bearer $TIKTOK_HOOK_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "event": "Purchase",
    "timestamp": "2026-08-13T15:06:39Z",
    "event_id": "550e8400-e29b-41d4-a716-446655440000",
    "currency": "USD",
    "value": 129.99,
    "contents": [
      {"item_id": "sku_123", "price": 64.99, "quantity": 1},
      {"item_id": "sku_456", "price": 65.00, "quantity": 1}
    ],
    "user": {
      "email": "user@example.com",
      "phone": "+15555550123",
      "ip": "203.0.113.12",
      "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)"
    },
    "consent": {"allowed": true, "gpp": "DBAA"}
  }'

If the playbook accepts the request, watch the OpenClaw run logs for the enrich and send steps. In TikTok Events Manager, use the Test Events view to confirm delivery. If you do not see activity within a few minutes, check that your pixel id and token are correct.

Event mapping cheatsheet

Here is a compact mapping you can adapt when shaping inputs upstream.

Your app event TikTok event Required params
cart.add AddToCart contents (id, price, quantity), currency
checkout.start InitiateCheckout contents, currency
user.register CompleteRegistration none required, but include value if you use it
order.paid Purchase value, currency, contents

Make sure your upstream emits a single consistent event_id when both client and server fire for the same action. That is the simplest way to handle dedup at scale.

Your pipeline should never send a marketing event without a stored consent decision. The playbook above uses a boolean and a raw GPP string, but you can expand this into a richer model that captures source, jurisdiction, and timestamp.

Error handling and retries

  • Treat 400 level errors as permanent. Log the body and stop retrying.
  • Treat 429 and 500 level errors as transient. Use exponential backoff with jitter.
  • Record the last response body and the event_id so you can find a specific call quickly.
  • Add a circuit breaker if you see a burst of 429 responses, then shift to a lower throughput mode.

Step 1Collect the credentials and set secrets

You will need the Pixel ID and an access token that can post to the Events API. Store them as TIKTOK_PIXEL_ID and TIKTOK_ACCESS_TOKEN in your OpenClaw environment. Also set TIKTOK_HOOK_SECRET for the inbound webhook and TIKTOK_EVENTS_API_URL from the current TikTok docs. This separates secrets and endpoints from code so you can rotate quickly.

Step 2Shape your upstream payloads

Emit a normalized event from your app or storefront whenever a user completes an action you want to send. Include event, timestamp, contents, currency, value, basic device info, and a client generated event_id if you already have one in the browser. This is an easy way to support how to send TikTok events from server without rewriting your entire data layer.

Step 3Validate early with a JSON schema

Use the schema step in OpenClaw to reject malformed inputs. This prevents bad events from reaching the sender and wasting rate limits. Keep your schema strict on types and required fields, but allow optional fields like ip and user_agent to pass through cleanly when available.

Step 4Hash identifiers and normalize values

Hash emails and phone numbers only after you normalize them. Here is a standalone example you can run anywhere Node is available.

import crypto from 'node:crypto';

const sha256Hex = s => crypto.createHash('sha256').update(s).digest('hex');

function normalizeEmail(e) {
  return (e || '').trim().toLowerCase();
}

function normalizePhone(p) {
  // Expect E.164 upstream; this just removes spaces
  return (p || '').replace(/\s+/g, '');
}

console.log({
  email_hashed: sha256Hex(normalizeEmail('User@Example.com')),
  phone_hashed: sha256Hex(normalizePhone('+1 555 555 0123')),
});

Step 5Implement dedup using event_id

When both the browser pixel and your server send a Purchase, set the same event_id in both paths. If the browser cannot produce one, generate a UUID v4 on the server and echo it back to the client for future events. For background on UUIDs, see the short primer on UUID v4 which is a practical choice for dedup keys.

Pass the GPP string and any jurisdiction flags you store with the user session. Branch logic so you send only when allowed. Emit structured logs on both suppressed and sent events with the event_id, pixel id, and a request id. Later, you can index these logs and build simple dashboards that highlight drop rates and retry counts.

Step 7Run end to end tests

Set your OpenClaw playbook to a staging environment and send a handful of sample events from a staging storefront. Use TikTok Events Manager to watch Test Events in real time, then check the standard diagnostics over the next day. Compare counts to your server logs and make sure they match within a few percent. This is a good time to tune retry limits and timeouts.

Common pitfalls and fixes

  • Missing or invalid currency codes. Always send ISO 4217 values like USD or EUR.
  • Unhashed identifiers. Hash only after normalization or match rates will dip.
  • Wrong pixel id or token. Fail fast with clear errors and pager rules.
  • Overly broad retries. Avoid retrying 4xx responses, and cap attempts for 500 level errors.
  • Missing contents arrays on cart and purchase events. Include item ids, price, and quantity.

Go live checklist

  • Secrets stored in OpenClaw and rotated.
  • Event mapping reviewed with growth and analytics teams.
  • Consent gating verified with both allowed and suppressed test users.
  • Logs include event ids, response codes, and correlation ids.
  • Alerts set for spikes in 4xx errors and sustained 429 responses.

Where this fits next

Once the flow is live, layer a simple agentic workflow on top. For example, have an autonomous agent read diagnostics daily and open a ticket if the match rate drops below a threshold, or if purchases fall below your expected baseline by hour. You can later extend the same pattern to other networks and to your own attribution and modeling stack.

If you want to see how ButterGrow approaches reliability in other channels, check more from the ButterGrow blog and the related posts above. You can reuse the same error handling, validation, and consent patterns in those integrations.

Launching this pipeline inside ButterGrow also makes it easy to connect it to reporting, experimentation, and segmentation features. Start with a small slice of traffic, then increase coverage once you are comfortable with the numbers.

To keep learning, compare choices across platforms with how it stacks up if you are evaluating migration paths from legacy tools.

Finally, remember that client side pixels and server side events work best together when carefully deduplicated and monitored. Clean inputs and clear logs are the difference between trustable reports and confusing dashboards.

Your next extension could be a small enrichment step that looks up product categories or margin bands before sending revenue events, or a no code automation that schedules a nightly reconciliation export to your data warehouse.

This approach also works for the long tail query shaped projects such as server side TikTok conversions API setup where you need reliability and privacy controls out of the box.

ButterGrow and OpenClaw give you room to expand without rewriting core systems as you add more channels and data checks over time.

This is the same pattern used in our other guides and you can adapt it for search and social with minimal changes to the function step.

This workflow also plays nicely with your consent and audit models so that future audits are low stress.

And it will help your team stay focused on creative and product work while the pipeline handles the repetitive delivery mechanics.

The design described here is resilient to most network blips and vendor hiccups, and it has clear fallbacks and logs when something does go wrong.

The small amount of code you write in the function step keeps the rest of the playbook declarative and easy to review in pull requests.

The overall shape is the same if you later add creative optimization agents that respond to performance changes, which keeps your architecture simple.

This guide should give you a strong baseline to ship production server events without surprises.

ButterGrow makes it easy to do that in one place with real time visibility.

When you want to onboard a new team member, this playbook is also straightforward to explain and test.

As with any integration, keep one eye on changes to the upstream API docs and security recommendations.

Your growth data gets better when events are consistent, timely, and trustworthy.

With that foundation in place, you can spend more time on creative testing and product velocity.

You now have an end to end plan that scales with your team.

Keep iterating on your mapping and logs as your catalog and funnel evolve.

If you want to wire this up inside your account, ButterGrow has an opinionated template based on the playbook above. You can get started in minutes with the hosted OpenClaw assistant and then adapt it to your stack. Jump into the onboarding flow and connect the TikTok credentials alongside your other sources.

References

Frequently Asked Questions

How do I map TikTok standard events to my ecommerce actions in OpenClaw?+

Create a small mapping table in your playbook that converts platform events like AddToCart, Purchase, and ViewContent into TikTok's expected names and parameters. Include currency, value, item_ids, and contents arrays where applicable, and validate with a JSON schema step before sending.

What is event_id in the TikTok Events API and how should I generate it?+

event_id is a deduplication key that lets TikTok collapse duplicate web and server hits. Generate a UUID v4 on the server if the client did not send one, and forward the same value across web and server when both fire for the same action. Keep it in your logs for troubleshooting.

How do I hash user data like email and phone for TikTok?+

Normalize values first (trim, lowercase emails, E.164 for phone) and then SHA-256 hash them. Do not salt or HMAC these fields. OpenClaw can run a short JavaScript function step that outputs email and phone hashes to the payload.

How do I pass consent to TikTok when using Events API?+

Include a consent status and, if applicable, the GPP string from your consent management platform in the payload or headers. In OpenClaw, read it from the inbound request and branch logic so marketing events only send when the record shows a valid consent state. Log both permitted and suppressed events for audits.

What status codes indicate a successful TikTok Events API call?+

A 200 level response indicates successful receipt, often with a per-event success flag in the JSON body. Treat 400 level codes as permanent failures that should not be retried without a fix, and 429 or 500 level codes as transient issues that should use exponential backoff with jitter.

How can I test that my server side events are reaching the correct pixel?+

Use TikTok Events Manager to view Test Events and recent activity for your pixel. Send a few sample events from your staging environment with a clearly labeled test_user flag, confirm receipt in the dashboard, and compare counts to your server logs to verify no unexpected drops.

Ready to try ButterGrow?

See how ButterGrow can supercharge your growth with a quick demo.

Book a Demo