← All posts
Tutorial

Connect a TikTok Account to a Signed Webhook With QR Authorization

To receive TikTok messages through UnifyPort, start with the webhook, not the QR screen. Register a signed webhook endpoint, create a TikTok account with auth_mode: "qrcode", start QR authorization, poll until the QR material is available, scan it, and then store the message.received events that arrive on your endpoint.

Key takeaways

  • Create the webhook endpoint before authorization, because auth progress and later inbound messages are both delivered as events.
  • TikTok authorization in UnifyPort uses the standard account and QR-auth endpoints; the first QR start response may not contain the QR URL, so poll the QR check endpoint.
  • The receiver should verify X-Device-Signature over X-Device-Timestamp + "." + raw body before parsing the event.
  • A TikTok webhook receiver should store events first, then route them to Slack, a helpdesk, an AI worker, or a queue.
  • Do not confuse this flow with TikTok’s official Login Kit QR authorization, which is an app-login flow for profile/scoped access.

If your question is whether TikTok exposes a general public DM API, read the separate TikTok DM API availability guide. This article is narrower: it shows the UnifyPort QR-auth setup once you have chosen the unofficial interface route. For the general receiver design, keep the webhook-first inbound integration checklist open beside it.

The setup sequence

The safe order is:

  1. Create one HTTPS webhook endpoint with a signing_secret.
  2. Subscribe to message.received while you are building; expand to other event types only when your handler needs them.
  3. Create the TikTok account in UnifyPort with auth_mode: "qrcode".
  4. Start the QR authorization flow.
  5. Poll the QR check endpoint until the QR payload is available or the flow finishes.
  6. Scan the QR code with the TikTok account owner.
  7. Wait for auth/runtime events, then send a test message and confirm a message.received payload lands.

The important part is step 1. UnifyPort notes that webhooks are your durable record of inbound traffic; missed deliveries are not something you should plan to reconstruct later. Create the intake first, then connect the account.

Step 1: register the signed webhook endpoint

Use a stable HTTPS URL owned by your service. During development this can point to a tunnel, but production should terminate TLS on infrastructure you control.

curl -X POST https://api.unifyport.ai/v1/webhook-endpoints \
  -H "X-Api-Key: $UNIFYPORT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://inbox.example.com/unifyport/tiktok",
  "status": "active",
  "subscribed_events": ["message.received"],
  "signing_secret": "sea-support-tiktok-2026"
}'

The Create webhook endpoint reference documents url, status, subscribed_events, signing_secret, and retry_policy.max_attempts. If you want every public standard event, use ["*"]; if you only want inbound messages during the first test, keep the filter explicit.

Step 2: create the TikTok account

Create an account object for the TikTok connection. UnifyPort treats one channel login as one account, and the provider guide documents TikTok as a QR-code authorization flow.

curl -X POST https://api.unifyport.ai/v1/accounts \
  -H "X-Api-Key: $UNIFYPORT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "TikTok Support Inbox",
  "provider": "tiktok",
  "region": "global",
  "status": "active",
  "auth_mode": "qrcode",
  "capabilities": ["receive_message"],
  "provider_data": {},
  "metadata": { "workflow": "support-intake" }
}'

Keep the account ID returned by this call. The examples below use $ACCOUNT_ID so you do not paste a real production identifier into logs or chat.

Step 3: start QR authorization and poll

Start the QR flow with an empty body:

curl -X POST "https://api.unifyport.ai/v1/accounts/$ACCOUNT_ID/auth/qr/start" \
  -H "X-Api-Key: $UNIFYPORT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

For TikTok, the initial start response may not contain the QR URL. Poll the QR check endpoint until the response carries renderable QR material, a success state, or a failure state:

curl -X POST "https://api.unifyport.ai/v1/accounts/$ACCOUNT_ID/auth/qr/check" \
  -H "X-Api-Key: $UNIFYPORT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

When QR material appears, show it only to the account owner who should connect that TikTok account. After they scan and approve, watch your webhook for account authorization/runtime events and then for the first message.received event.

This is not the same as TikTok’s official Login Kit QR authorization. TikTok’s own developer documentation describes Login Kit QR authorization as a way for a user to authorize an app and grant profile/scoped access. The UnifyPort setup here is about connecting a messaging account to a signed inbound event stream.

Step 4: verify the incoming webhook before parsing JSON

The delivery contract is documented in the Webhook delivery and signature verification guide. When signing is enabled, each delivery includes X-Device-Timestamp and X-Device-Signature; the signature is the hex HMAC-SHA256 of:

<X-Device-Timestamp>.<raw request body>

A minimal Express receiver should keep the raw body intact:

import crypto from 'node:crypto';
import express from 'express';

const app = express();
const signingSecret = process.env.WEBHOOK_SIGNING_SECRET;

app.post('/unifyport/tiktok', express.raw({ type: 'application/json' }), (req, res) => {
  const timestamp = req.get('X-Device-Timestamp') ?? '';
  const signature = req.get('X-Device-Signature') ?? '';

  const expected = crypto
    .createHmac('sha256', signingSecret)
    .update(timestamp + '.')
    .update(req.body)
    .digest('hex');

  if (signature.length !== expected.length) return res.sendStatus(401);
  if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
    return res.sendStatus(401);
  }

  const event = JSON.parse(req.body.toString('utf8'));
  if (event.type === 'message.received' && event.provider === 'tiktok') {
    // store event.id, event.account_id, event.occurred_at, and event.data before routing
  }

  res.sendStatus(202);
});

If your signature check fails, compare your raw body, timestamp, and secret against the webhook HMAC replay-protection guide. The most common mistake is verifying JSON after middleware has parsed or reformatted it.

What a received event looks like

The standard event envelope is always id, type, provider, account_id, occurred_at, and data; the shape of data depends on the event type. For a text message, your handler should expect this contract rather than a TikTok-only schema:

{
  "id": "evt_2f9c1a4b7e",
  "type": "message.received",
  "provider": "tiktok",
  "account_id": "acc_8c21d0",
  "occurred_at": "2026-06-08T12:34:56Z",
  "data": {
    "conversation": { "id": "5005", "type": "user" },
    "sender": { "id": "4004", "type": "user", "name": "Jordan Lee" },
    "message": {
      "id": "3003",
      "text": "Hi - is this item still available?",
      "direction": "inbound",
      "sent_at": "2026-06-08T12:34:55Z"
    }
  }
}

Store first, route second. A 2xx response acknowledges the delivery; slow work such as AI classification, CRM enrichment, or human assignment should run after the event is durable.

Limitations and trade-offs

  • The connected TikTok account still needs a real account owner to scan and authorize the QR flow.
  • Provider support and upstream availability can vary by account and region; design your UI to show auth failure and re-authentication states.
  • Webhook delivery is at-least-once, so use event.id or X-Device-Event-Id as an idempotency key.
  • HMAC authenticates the payload; it does not encrypt your logs, queue, or database.
  • If your team needs TikTok’s official app-login scopes or profile APIs, use TikTok’s official developer platform for that layer. Use UnifyPort for the inbound messaging event stream described here.

FAQ

Do I need a TikTok developer app for this UnifyPort flow?

No. The UnifyPort account connection uses UnifyPort’s account and QR authorization endpoints. TikTok’s official Login Kit is a separate app-login path for profile/scoped access.

Why does the first QR start response not show a QR URL?

The TikTok provider guide notes that the initial start response may not contain a QR URL. Keep polling the QR check endpoint until the QR material, success, or failure state appears.

Should I subscribe to ["*"] or only message.received?

Use message.received for the first TikTok intake test. Move to ["*"] only when you have handlers for auth, runtime, receipt, and other public standard events.

What should my webhook return?

Return any 2xx status only after you have verified the signature and durably accepted the event. Return a non-2xx status when the request is unauthenticated or your durable store is unavailable.

Next step

Follow the TikTok authorization provider guide and the webhook delivery guide side by side. If you are still choosing the architecture, compare this with the TikTok live-DM queue tutorial.

Sources

Official sources checked on 2026-09-03:

UnifyPort API

Turn messaging integration into a stable product pipeline.

Start by sending through one API, then bring every inbound message back into your business system with standard events.