Skip to main content

Webhooks Overview

Native Frame uses webhooks in two different ways, and it's worth knowing which is which because they behave very differently.

Event notificationsProgram-states integration
PurposeTell you something happenedAsk your integration for a verdict
DirectionOne-way (fire-and-forget)Request/response — your reply drives platform behavior
DeliveryAsync, fanned out, retried (exponential backoff), with delivery historySynchronous, in the stream lifecycle path
Webhooks registeredMany — you register/manage URLsExactly one registered webhook
What your response doesNothing (only the HTTP status matters)stop / needAuth / appData can veto a start, tear down a stream, or reject a peer

1. Event notifications

You register one or more webhook endpoints and Native Frame delivers events to them as things happen — asynchronously, with retries and full delivery history. Your endpoint just needs to accept the delivery (the response body isn't acted on). Registration and management are done through the dashboard, or via the Events API (GET / PUT / DELETE /events/v2/webhook/{name}). See Webhooks to set one up.

2. Program-states integration

This is the verdict path used for stream-lifecycle decisions and lifecycle notifications. Native Frame calls your single registered webhook synchronously; for some events your response decides what happens next.

Events fall into two kinds.

Respondable (verdict) events — your response can set stop, needAuth, appData:

EventWhat it's for
startingAdmission gate. Fired synchronously on start-stream/start-broadcast. Return stop (at program, stream, or token level) to reject the broadcast before any session is created. Fails closed — if the webhook call itself fails, the start is blocked.
pollingOngoing verdict while a token is connected — return stop to tear the token/stream down.
token / consumer join + poll eventsPer-participant admission/verdict.

Notification events — informational, no response needed:

EventFired when
format-started / format-endedA transcode variant becomes available / unavailable — see the Format Availability guide.
closedFire-and-forget on end-stream/end-broadcast for each active stream.
manifest-ready, disconnected, etc.Various lifecycle notifications.

Delivery shape

Program-states events are delivered nestedprograms → streams → producing/consuming → token.event. On the standard path (the webhook you register in the dashboard) the JSON body is the payload itself: programs and tokens sit at the top level, with no event/id wrapper around them.

Your response must mirror the request — omission means stop

The response is a full mirror, not a delta: echo back every program, stream, producing/consuming entry, and token you received, setting the verdict fields (stop, needAuth, appData) on each. Anything you leave out is treated as stop: true — an empty response stops everything in the request, including your own broadcaster. See the Webhooks API reference for the exact shapes.

Flat events format — not yet available via webhooks

The platform also defines a flat, event-based payload (event: "events"), configured per project in the auth service and delivered only via direct integration — it is not available through the events-service webhook, and only a few integrations use it. Integrate against the nested program-states-v2 shape.

Playback admission — hls-auth

The events above are the producer side. Viewer playback on a private stream is admitted through the same webhook, by a different event, in a different shape.

When a viewer requests the manifest of a private stream with an access token, Native Frame calls your endpoint with an hls-auth action carried under viewTokens — the token / viewTokens shape, not the producing / consuming tree described above. A handler that branches only on the program-states-v2 shape never matches it, and the fail-closed default leaves the viewer unauthorized.

Two rules decide whether you are called at all:

  • Token format. A manifest access token must match ^[0-9A-Za-z_+/=-]+$. A token that fails the check — a JWT (contains .) or an email address — is rejected before the webhook fires. Your endpoint sees nothing, which reads as a delivery failure but is a format failure.
  • The view-private role. A viewer JWT carrying a top-level roles: ["view-private"] claim is honored directly, and no hls-auth webhook fires. This is distinct from videoToken.scopes: ["private-viewer"], which governs WebRTC joining, not HLS playback.

An unauthorized viewer is not refused: they are served a blurred low-resolution substitute rendition at HTTP 200. Both paths, and that failure mode, are in Private Stream Viewer Auth.

Which one do I need?

  • "Tell me when X happens" (recordings ready, analytics, generic notifications) → register an event-notification webhook.
  • "Let me approve/deny streams, or know about live stream state and formats" → you're on the program-states integration (private + program-states auth flow). Format availability, the start admission gate, and polling verdicts all arrive here.
  • "Let me decide who can watch a private stream" → also the program-states integration, via hls-auth — or skip the webhook entirely by minting viewer JWTs with the view-private role. See Private Stream Viewer Auth.

For the payload formats, the response contract (stop / needAuth / appData), and the full event catalog, see the Webhooks API reference.