Gareth Wilson Gareth Wilson

Guide to TikTok Shop Webhooks: Features and Best Practices

Published


TikTok Shop webhooks notify your systems when commerce activity happens on a connected shop: an order changes status, a package updates, a product's status changes. For any app in the TikTok Shop ecosystem, whether an order management tool, a fulfillment integration, or a catalog sync, webhooks are how you react without polling the Partner API.

This guide covers how TikTok Shop webhooks work, the events you can subscribe to, how to verify the signature (which arrives in the Authorization header, not a signature-specific one), the delivery behavior, and the best practices that keep a shop integration reliable in production.

What are TikTok Shop webhooks?

TikTok Shop webhooks are HTTP POSTs delivered to a URL you configure per app, each carrying a JSON payload about a shop event. The webhook signature scheme is distinct from TikTok Shop's API request signing, and the signature is delivered in the standard Authorization header rather than a custom signature header.

Each payload carries a numeric type, a tts_notification_id, a shop_id, a timestamp, and a data object. The numeric type is unreliable to branch on; use the subscribed event type instead.

TikTok Shop webhook features

FeatureDetails
ConfigurationPartner Center (per app) or the Events API
Signature locationAuthorization header (no Bearer prefix)
Signature schemelowercase-hex HMAC-SHA256 over app_key + raw body, keyed with app_secret
Endpoint requirementsHTTPS on a domain (no IP, no custom port), TLS 1.2+
AcknowledgementHTTP 200 with an empty body within 3 seconds
Rejected signatureEndpoint returns 401
DeliveryAt-least-once, 4 retries after failure (2 min, 30 min, 3 h, 12 h)
Deduplicationtts_notification_id
SDKsApp-specific downloads from Partner Center (not on npm/PyPI)

Common events

TikTok Shop groups events by resource. Order, package, and product events are the ones most integrations start with:

EventFires when
ORDER_STATUS_CHANGEAn order moves to a new status
PACKAGE_UPDATEA package (fulfillment unit) is updated
PRODUCT_STATUS_CHANGEA product's status changes

Subscribe only to the events you process, and branch on the subscribed event_type rather than the numeric type field in the payload. Availability of specific events can vary by app scope and market, so check your app's configuration in Partner Center for the events you're entitled to.

Setting up TikTok Shop webhooks

Configure webhooks per shop in Partner Center (App & Service > your app > Basic Information > Developing > Webhook URL / Event subscriptions), or manage them via the Events API:

curl -X PUT "https://open-api.tiktokglobalshop.com/event/202309/webhooks" \
  -H "Content-Type: application/json" \
  -d '{ "event_type": "ORDER_STATUS_CHANGE", "address": "https://your-app.example.com/webhook" }'

Your endpoint must be HTTPS on a domain (no raw IP, no custom port) with TLS 1.2 or higher, and must return HTTP 200 with an empty body within 3 seconds. A 401 signals that you rejected the signature.

Securing TikTok Shop webhooks

The signature arrives in the Authorization header (with no Bearer prefix) as a lowercase-hex HMAC-SHA256. The signing base is your app_key concatenated with the raw request body, and the key is your app_secret. Compute it over the exact raw body and compare in constant time.

const crypto = require("crypto");

const APP_KEY = process.env.TTS_APP_KEY;
const APP_SECRET = process.env.TTS_APP_SECRET;

function verify(rawBody, authHeader) {
  const base = APP_KEY + rawBody; // app_key + raw body
  const expected = crypto.createHmac("sha256", APP_SECRET).update(base).digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(authHeader || "");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  const auth = req.headers["authorization"];
  if (!verify(req.body.toString("utf8"), auth)) {
    return res.sendStatus(401);
  }

  res.status(200).send(""); // empty 200 within 3 seconds
  processQueue.add(JSON.parse(req.body)); // process asynchronously
});

The same check in Python:

import hashlib
import hmac
import os

APP_KEY = os.environ["TTS_APP_KEY"]
APP_SECRET = os.environ["TTS_APP_SECRET"].encode()


def verify(raw_body: str, auth_header: str) -> bool:
    base = (APP_KEY + raw_body).encode()
    expected = hmac.new(APP_SECRET, base, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, auth_header or "")

TikTok Shop webhook limitations and pain points

The signature is in the Authorization header, over app_key + body

The Problem: Two things trip people up: the signature lives in Authorization (which developers often reserve for bearer tokens), and the signing base is app_key + raw body, not the body alone. Miss either and every delivery fails verification.

Why It Happens: TikTok Shop's webhook signing differs from its API request signing and reuses the Authorization header for the digest.

Workarounds:

  • Read the digest from Authorization (no Bearer prefix) and sign app_key + raw body with app_secret.
  • Verify against the exact raw body and compare in constant time.

How Hookdeck Can Help: Hookdeck verifies the TikTok Shop signature at the edge, so your app receives pre-verified events and doesn't have to special-case the header or the concatenated signing base.

The three-second empty-200 window

The Problem: You must return an empty-bodied 200 within 3 seconds. Any synchronous processing eats the budget, and a slow response counts as a failure that triggers retries.

Why It Happens: TikTok Shop caps how long it waits so one slow endpoint doesn't hold up delivery.

Workarounds:

  • Verify, enqueue, and return an empty 200 immediately; process off a queue.
  • Do no blocking work (API calls, DB writes) before responding.

How Hookdeck Can Help: Hookdeck acknowledges TikTok Shop within milliseconds and durably queues events, decoupling the 3-second window from your processing time.

At-least-once delivery with a fixed retry ladder

The Problem: Delivery is at-least-once with 4 retries after a failure (at 2 minutes, 30 minutes, 3 hours, and 12 hours), then TikTok Shop gives up. The same event arrives more than once, and after the ladder is exhausted it's gone.

Why It Happens: Retries prioritize eventual delivery, and the ladder is capped to avoid retrying forever.

Workarounds:

  • Dedupe on tts_notification_id.
  • Reconcile against the Partner API for anything you can't afford to miss once the ladder is exhausted.
  • Make side effects idempotent.

How Hookdeck Can Help: Hookdeck deduplicates on delivery and durably queues events with retry schedules you control, so duplicates don't double-process and a 12-hour ladder isn't your only safety net. See our guide to webhook idempotency.

The numeric type field is unreliable

The Problem: Payloads carry a numeric type, but branching on it is fragile. Handlers keyed to the number can misclassify events.

Why It Happens: The numeric type isn't a stable public contract; the subscribed event type is.

Workarounds:

  • Branch on the subscribed event_type, not the numeric type.
  • Validate the payload shape per event type before acting.

How Hookdeck Can Help: Hookdeck's filters and transformations let you route and normalize on the fields you trust, so downstream handlers aren't reasoning about the numeric type.

Best practices

Verify the Authorization-header signature correctly

Read the digest from Authorization (no Bearer), sign app_key + raw body with app_secret, lowercase-hex, and compare in constant time against the raw body.

Return an empty 200 within three seconds

Acknowledge immediately with an empty body and defer processing to a queue. See why to process webhooks asynchronously.

Dedupe on tts_notification_id

Use tts_notification_id as your idempotency key so at-least-once delivery doesn't double-process.

Branch on the subscribed event type

Ignore the numeric type and route on the event_type you subscribed to.

Reconcile with the Partner API

The retry ladder gives up after 12 hours. Reconcile periodically so a permanently failed delivery doesn't leave you out of sync.

Make TikTok Shop webhooks production-ready

Hookdeck verifies the signature, deduplicates on tts_notification_id, and durably queues every shop event

Conclusion

TikTok Shop webhooks put order, package, and product activity on a push channel your app can act on, but the details are specific: a signature in the Authorization header computed over app_key + raw body, a hard 3-second empty-200 window, at-least-once delivery on a fixed 4-step retry ladder, and a numeric type field you shouldn't trust. Get any of those wrong and you either reject valid events or double-process duplicates.

That leaves verification, fast acknowledgement, deduplication, and reconciliation on you. Hookdeck verifies the signature, deduplicates on tts_notification_id, and durably queues every event at the edge, so your integration processes verified, unique shop events and never races the 3-second window.

Get started with Hookdeck for free and handle TikTok Shop webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.