Integration: Catena tickets¶
This page is for the developer of a storefront or billing system — the place where you sell access. Instead of answering a callback on every viewer, you create the session ahead of time and hand the viewer a ticket. The streamer admits the ticket on its own: it checks the signature, expiry, and scope locally, with no call to your systems on the playback path.
The payoff: playback no longer depends on your storefront's speed or availability, channels start and switch instantly, and there is no per-viewer callback to keep scaled. Your systems are needed at the moment you sell access — not while the viewer watches.
Create a grant¶
When you sell access, call the management API to create a grant. Catena signs a ticket (a JWT) and returns it — the token is handed back only at creation; the registry keeps the grant, not the ticket.
Authenticate the call the way you authenticate any management API request — with the admin login and password. So your storefront never has to hold full admin credentials, you can instead configure a dedicated issuer key in central — a bearer token whose only power is to issue and manage grants — and send it as Authorization: Bearer <issuer-key>.
POST /play-sessions/grants
{
"streams": ["news", "arena"],
"user_id": "subscriber-4821",
"ttl_secs": 14400,
"max_concurrent": 2,
"bind_ip": "203.0.113.0/24",
"note": "family plan"
}
| Field | Meaning |
|---|---|
streams |
Which channels the ticket may play — its scope |
user_id |
The account the ticket belongs to; groups the viewer's screens |
ttl_secs |
Ticket lifetime; default 4 hours. exp is a hard end — a session cannot outlive it (players do not refresh the token) |
max_concurrent |
Simultaneous screens; default 1, null for unlimited |
bind_ip |
Optional IP or CIDR the ticket is useless outside of |
note |
Optional free-text label |
The reply carries the grant and the token. Hand that token to the player, which presents it on the play URL as ?token=<jwt> (or in an Authorization: Bearer header). Nothing else is required of the viewer — the streamer takes it from there.
Screens¶
max_concurrent limits how many screens actively consume the ticket at once — a household with two TVs, say. Go over the limit and the oldest screen is dropped; the newest wins. You can change the limit later without reissuing the ticket, because it lives in the registry, not in the signed token:
PATCH /play-sessions/grants/{jti}
{ "max_concurrent": 3 }
Revoke¶
Cancel a grant and every streamer drops its viewers within seconds and refuses the ticket from then on — the cancellation travels to the streamers over a revocation feed, not by waiting for the ticket to expire. There is no reauthorization for ticket sessions; revocation is the single channel that ends them early.
Roaming¶
A viewer whose address changes — a phone leaving Wi-Fi for mobile — is admitted afresh on the new address, offline, while the old session dies by idle timeout. It counts as one journey, not a new charge, grouped by the grant. If you set bind_ip, roaming stays within that range.
List and status¶
GET /play-sessions/grants lists grants and their status — issued (handed out, not yet played), played (used at least once), revoked, expired — filterable by user_id and status. This is the record of what you sold and whether it was used.
Migrate at your own pace¶
Tickets and an existing backend run side by side — tickets for some viewers, the callback for others — so you can move audience by audience. The end state is the switch opaque_tokens: false on the auth policy: once set, any non-ticket token is refused outright, without consulting lists or a backend, and the callback path is closed for good.
Next steps¶
- Protect playback — the operator's view of the same policy.
- Session lifecycle — how a ticket session opens, closes, and survives a cluster hiccup.
- flussonic authorization — the callback path you are migrating from.