---
seo:
  description: >-
    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…
sidebar:
  label: Overview
title: 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.

## Download the spec

This entire reference is generated from one OpenAPI 3.1 document:
**[openapi.yaml](https://docs.tippage.com/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. 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:

```js
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/developer-api](https://docs.tippage.com/developer-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.

<ApiOverview source="api" />

## Identity

Key introspection and API discovery.

<ApiTagOperations source="api" tag="identity" />

## 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](https://docs.tippage.com/developer-api/claim-and-finish).

<ApiTagOperations source="api" tag="tts-queue" />

## 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`.

<ApiTagOperations source="api" tag="media-queue" />

## 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).

<ApiTagOperations source="api" tag="tipping" />

## AI voices

AI voice TTS (closed beta): 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 enabled for your account - the feature is invite-only while in beta.

<ApiTagOperations source="api" tag="ai-voices" />

## Viewers

Viewer management for the sub-rewards system. A viewer is someone who has signed in to the tip page with Twitch - 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`). Everywhere a `{user}` appears in a path you can pass either the Twitch user id or the viewer's name (login or display name, case-insensitive). Someone who has never signed in can't be looked up or granted credits - those requests answer `404 viewer_not_signed_in`.

<ApiTagOperations source="api" tag="viewers" />

## 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.

<ApiTagOperations source="api" tag="channel-points" />

## Webhook endpoints

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

<ApiTagOperations source="api" tag="webhook-endpoints" />
