What a TikTok LIVE webhook does
A webhook is a go-live alert delivered to your server. We watch the creators you list and send an HTTPS POST to your URL when one of them starts a LIVE (live.start) and again when the stream ends (live.end). Your service does not need to hold a connection open or run a scheduler - it only has to answer a POST.
Typical uses: post to Discord or Slack when a creator goes live, start recording or analytics the moment a stream begins, open a WebSocket connection for chat and gifts only while the creator is actually live, or keep an agency roster's live status current.
How it works
- Register a webhook with your endpoint URL, the events you want and the TikTok usernames to watch.
- Store the signing secret returned once on creation. Every delivery is signed with it.
- Receive live.start and live.end as JSON POSTs. Verify the signature, answer 200, then do your work.
Events
| Event | Plans | When it fires |
|---|---|---|
live.start | All paid plans | A watched creator started a LIVE. Includes the room id, start time and live URL. |
live.end | All paid plans | A watched creator ended the stream. Same envelope, with the end time. |
live.peak_viewers | Ultra and Agency Global | A stream reached a new viewer peak. |
live.gift.high_value | Ultra and Agency Global | A high-value gift was sent during a watched stream. |
creator.follower_milestone | Agency Global | A watched creator crossed a follower milestone. |
Register a webhook
Create webhooks from the dashboard, or server-to-server with your API key. The response includes the signing secret, shown only once.
curl -X POST "https://api.tik.tools/webcast/webhooks" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Go-live alerts",
"target_url": "https://your-app.com/hooks/tiktok-live",
"event_types": ["live.start", "live.end"],
"creators": ["creator_one", "creator_two"]
}'
# The response carries webhook.id and the signing secret.
# The secret is returned once - store it with your config.Keep creators scoped to the accounts you care about (up to 1,000 per webhook). To change the list later, PATCH the webhook with the full new array - it replaces the previous list. Full parameters are in the API reference.
Payloads
live.start
{
"event_id": "evt_01J...",
"event": "live.start",
"occurred_at": "2026-06-18T13:00:00.000Z",
"is_test": false,
"creator": {
"unique_id": "creator_one",
"nickname": "Creator One",
"avatar_url": "https://...",
"user_id": "6569595380449902593",
"follower_count": 1250000
},
"live": {
"room_id": "7000000000000000000",
"started_at": "2026-06-18T13:00:00.000Z",
"live_url": "https://www.tiktok.com/@creator_one/live"
}
}live.end
{
"event_id": "evt_01J...",
"event": "live.end",
"occurred_at": "2026-06-18T14:30:00.000Z",
"is_test": false,
"creator": { "unique_id": "creator_one", "nickname": "Creator One" },
"live": { "room_id": "7000000000000000000", "ended_at": "2026-06-18T14:30:00.000Z" }
}Every delivery also carries X-Tiktool-Timestamp, X-Tiktool-Event, X-Tiktool-Event-Id, X-Tiktool-Delivery-Id, X-Tiktool-Attempt and X-Tiktool-Webhook-Id headers.
Verify the signature
Each POST includes X-Tiktool-Signature: t=<unix_ts>,v1=<hex>, where v1 is the HMAC-SHA256 of "<t>.<raw body>" keyed with your webhook secret. Compute it over the raw body before parsing JSON, compare in constant time, and reject timestamps older than five minutes.
Node.js
import crypto from 'node:crypto'
export function verifyTiktoolWebhook(rawBody, header, secret) {
const t = Number(header.match(/t=(\d+)/)?.[1])
const v1 = header.match(/v1=([a-f0-9]+)/)?.[1]
if (!t || !v1) return false
if (Math.abs(Date.now() / 1000 - t) > 300) return false
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex')
return expected.length === v1.length
&& crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1))
}Python
import hashlib, hmac, time
def verify_tiktool_webhook(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(",") if "=" in p)
t, v1 = parts.get("t"), parts.get("v1")
if not t or not v1 or abs(time.time() - int(t)) > 300:
return False
expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, v1)Delivery and retries
- At-least-once. The same event can arrive more than once, so dedupe on
event_id. - Retries. A failed delivery is retried on a schedule of 0s, 30s, 2m, 10m, 1h and 6h. After the last attempt it is kept as a dead letter you can replay from the dashboard or the API once your endpoint is healthy.
- Answer fast. Return a 2xx as soon as the POST arrives and process afterwards. Connect timeout is 10 seconds and read timeout 15 seconds; a refused connection or a timeout counts as a failed attempt.
- Test anytime. Send a signed test delivery to your endpoint from the dashboard or the test endpoint to check parsing and signature handling before a creator goes live.
Plans
Webhooks with live.start and live.end are included on every paid plan, with more endpoints and a larger daily delivery allowance on higher plans. A Sandbox account can set up one test webhook from the dashboard and send signed test deliveries to it, so the integration can be built before you subscribe. Live deliveries, and registering webhooks with an API key, need a paid plan. See pricing for plan details.