Skip to content
TipPage Docs
Esc
navigateopen⌘Jpreview
On this page

TipPage Developer API

Build your own integrations on top of a TipPage: read tip history, watch and control the TTS queue and media queue, and receive signed webhooks the moment things happen. Every tip produces one TTS entry and, optionally, one media entry.

Changed 2026-09-06 - viewers are platform-neutral. A viewer can sign in to the tip page with Twitch or Kick and link the other account, so every tip, viewer and credit object that carried twitch_user_id now carries platform (twitch | kick) and platform_user_id instead - there is no alias. The viewer object gained linked_accounts, and the {user} path segment accepts <platform>:<id> (kick:123) alongside a bare id or a name.

Download the spec

This entire reference is generated from one OpenAPI 3.1 document: openapi.yaml. Import it into Postman, Insomnia, or Bruno, or feed it to a client generator.

Authentication

Every request needs an API key sent as a bearer token:

Authorization: Bearer tp_live_...

Keys are created by the streamer in Dashboard → Settings → Developer (owner and super admins only). The key identifies the streamer, so there is no account id anywhere in these paths. Each key carries scopes - the operations below name the scope they need. Keys are server-side credentials: never embed one in a browser page or show it on stream.

Rate limits

240 requests/min per IP and 120 requests/min per key. Chat sends (POST /chat/messages) have their own budget on top: 20 messages per 30 seconds per streamer, shared across all keys. Limit state is returned in standard RateLimit-* headers; exceeding it returns 429.

Errors

Errors are JSON: { "error": "<human message>", "code": "<machine_code>" }.

Webhooks

Register endpoint URLs (here or in the dashboard) and TipPage POSTs a JSON event envelope to them. See the Webhooks section at the bottom for every event type and payload.

Verify every delivery before trusting it. Your endpoint is a public URL - anyone who discovers it can POST a perfectly-shaped envelope. The TipPage-Signature header is the only proof a request came from TipPage; every other header and the envelope itself are plain text anyone can forge, and source-IP allowlisting doesn’t work (delivery IPs are not stable). Each request carries:

Header Value
TipPage-Signature t=<unix seconds>,v1=<hex HMAC-SHA256> - the proof of origin
TipPage-Event the event type (e.g. tip.created) - informational
TipPage-Event-Id the envelope id (evt_...) - informational
TipPage-Delivery the delivery id (wd_..., or ping for tests) - informational
TipPage-Attempt delivery attempt number (1-8); a value above 1 means this is a retry of the same event, byte-identical body - informational

The MAC is HMAC-SHA256 over <t>.<raw request body> with your endpoint’s signing secret (whsec_..., shown once at creation). Verify against the raw body bytes (before any JSON parser touches the request), reject t more than ~5 minutes from your clock (blocks replays of captured deliveries), and compare MACs in constant time:

import crypto from "node:crypto";

function verify(secret, header, rawBody, toleranceSec = 300) {
  const parts = Object.fromEntries(
    (header || "").split(",").map((p) => p.split("="))
  );
  if (!parts.t || !parts.v1) return false;
  if (!(Math.abs(Date.now() / 1000 - Number(parts.t)) <= toleranceSec)) return false;
  const mac = crypto.createHmac("sha256", secret)
    .update(`${parts.t}.${rawBody}`).digest("hex");
  const got = Buffer.from(parts.v1, "hex");
  const want = Buffer.from(mac, "hex");
  return got.length === want.length && crypto.timingSafeEqual(got, want);
}

On a failed check, respond 401 and do nothing else with the request. Retries re-sign the byte-identical body with a fresh t, so verify every request, not once per event id - and since delivery is at-least-once, dedupe verified events by the envelope id. The full verification guide (with an Express example and common pitfalls) is at docs.tippage.com/api.

Delivery is at-least-once with no ordering guarantee. Non-2xx responses retry with backoff for up to ~44 hours (8 attempts); an endpoint that keeps failing is disabled automatically and the streamer is notified. Endpoint URLs must be public https:// - private and internal addresses are rejected. Answer with any 2xx as fast as possible and do real work async.

Version 1.0
Base URLhttps://api.tippage.com/v1

Identity

Key introspection and API discovery.

Realtime

The webhook event stream, live over a WebSocket at wss://ws.tippage.com - same events, same payloads, same event ids as the webhooks, delivered while you’re connected. No SDK needed: open a plain WebSocket, send one JSON line - {"data": {"apiKey": "tp_live_..."}} for connections you control, or {"data": {"token": "..."}} with a short-lived token from POST /realtime/token to let clients that shouldn’t hold your key (browsers, third parties) connect. The server streams the events your key’s scopes allow. Full guide, wire format, channel table, and lifecycle: https://docs.tippage.com/api/realtime

TTS queue

The pending TTS queue and its played history. Reads need tts:read; controls need tts:control. The start/finish pair is the same protocol the stream overlay speaks - an external consumer can claim a TTS, play it its own way, and finish it into history. Full pattern: claim, do your thing, finish.

Media queue

The media (video request) queue, its played history, live playback status, and direct video queueing. Reads need media:read; controls and queueing need media:control.

Tipping

Tips themselves: create manual tips (tips:create), the accept-new-tips switch (status readable by any valid key, opening/closing via tipping:control), and the supporter leaderboard (any valid key).

AI voices

AI voice TTS (TipPage+): the curated voice catalog with per-voice enabled flags, this month’s character usage, and toggles for the whole feature or individual voices. Needs ai_voices:manage. Every endpoint answers 404 unless AI voices are available to your account.

Viewers

Viewer management for the sub-rewards system. A viewer is someone who has signed in to the tip page - with Twitch or Kick - and every viewer object names that account as platform + platform_user_id, with the person’s other linked account(s) in linked_accounts. List them with their first sign-in dates and credit balances (viewers:read), read one viewer’s grant ledger and sub-reward usage, and grant or revoke credits (viewers:manage). Balances, grants and reward history are read across a person’s linked accounts; a grant or revoke lands on the exact account you name. Everywhere a {user} appears in a path you can pass <platform>:<id> (kick:123), a bare numeric id, or the viewer’s name (login or display name, case-insensitive, on either platform). Someone who has never signed in can’t be looked up or granted credits - those requests answer 404 viewer_not_signed_in.

Channel points

Resolve channel point redemptions on Twitch. Needs channel_points:manage. Only works for TipPage-managed rewards - Twitch restricts redemption resolution to the app that created the reward. Pair with the twitch.channel_point_redemption webhook, whose is_managed flag says whether a redemption is resolvable.

Webhook endpoints

Manage where events get delivered. Needs webhooks:manage. Endpoints can also be managed in the dashboard.

Chat timers

Scheduled chat-bot messages - the bot posts them on a fixed or randomized interval, optionally only while the streamer is live or after enough chat activity. Needs timers:manage. The same timers as the dashboard’s Chat bot page; max 20 per streamer. Editing a timer’s interval restarts its schedule from now.

Counters

Named per-streamer numbers the chat bot bumps with {count} / {count:name} command tokens and overlay Data labels can display live. Addressed by name - a lowercase slug (a-z 0-9 _ -, max 64 chars) is the key on every surface. Needs counters:manage. Every change made here behaves exactly like a chat bump: overlays update live and the counter.updated webhook fires. Max 100 per streamer.

Chat bot

The streamer’s chat bot: its status and send identity (any valid key), speaking in chat as the bot (chat:write), and the custom !commands it answers (commands:manage, addressed by command name - the trigger without the !). Pair with the chat.command webhook event to react when a custom command runs.

Overlays

The account’s overlays: list them, inspect one (name, resolution, widget types - never overlay keys), delete one, reload a connected one, fire the Event celebration widget on demand (all of them or one specific instance), and read, update, or add to each overlay’s tip goal. All of it needs overlays:manage.

Was this page helpful?