Content and metadata
Create, update and publish titles, series, seasons and live channels; manage artwork, availability windows and territories.
A platform that cannot be driven by your existing systems is a silo with a nice front end. Every capability is reachable over a documented REST surface, because the integrations you need are the ones nobody predicted.
Every organisation already runs systems that will outlive this decision — a subscriber database, a finance system, a rights tracker, a CRM. The platform has to fit around them rather than demand they be replaced, and that only works if every capability is available over an API rather than only through a console.
So the console is not privileged. It calls the same REST surface any of your systems can call, which is the only reliable way to guarantee the API is complete.
Create, update and publish titles, series, seasons and live channels; manage artwork, availability windows and territories.
Accounts, devices, sessions and what each is entitled to watch — consumable by your subscriber system as the authority.
Plans, subscriptions, pay-per-view purchases, coupons and the events your finance system needs to reconcile.
Submit source, track encoding jobs, publish renditions — so an existing MAM or production workflow can drive the pipeline.
Channel definitions, schedules, blackout and regional variation, drivable from your playout automation.
Playback quality and consumption data available for export into whatever your organisation already reports from.
The failure that matters on a billing or entitlement endpoint is not the request that errors. It is the request that succeeds and whose response never arrives. The caller sees a timeout, cannot tell the difference between that and a rejected request, and has exactly two bad options: retry and risk charging twice, or give up and risk not charging at all.
So every state-changing endpoint accepts an idempotency key supplied by the caller. The first request under a key executes and its response is stored against that key. Any later request under the same key returns the stored response without executing anything, and the outcome is identical whether it is the second attempt or the fiftieth.
Two details decide whether that actually holds. The key is stored in the same transaction as the effect it describes, so there is no window in which the charge exists and the record of it does not. And a key replayed with a different request body is rejected rather than served the stored response, because a caller reusing a key for different work has a bug, and returning a stale success would hide it until reconciliation.
GET, HEAD and DELETE need none of this — they are already idempotent by definition, and a DELETE on something already gone returns success rather than an error, because the caller's intent is satisfied either way. It is POST that needs the key, which is why the header is required rather than optional on the endpoints that move money or grant access.
Both requests below are sent because the first response was lost in transit. The second executes nothing and returns the first response, with a header saying so.
# First attempt — the response never reaches the caller
curl -X POST https://api.example/v1/subscriptions \
-H "Authorization: Bearer $TOKEN" \
-H "Idempotency-Key: 7f3c1a02-5b8e-4d21-9f6a-1c0b4e8d2a55" \
-H "Content-Type: application/json" \
-d '{"account_id":"acc_8812","plan":"annual"}'
# Retry with the SAME key — nothing new executes
# 200 OK
# Idempotent-Replay: true
# {"subscription_id":"sub_41f9","status":"active","created":"2026-08-25T09:14:02Z"}
# Same key, different body — rejected, because this is a caller bug
# 409 Conflict
# {"error":"idempotency_key_reuse","detail":"Key was used with a different request body."}| Property | Detail |
|---|---|
| Style | REST over HTTPS, JSON |
| Authentication | Bearer tokens for machine clients; cookie sessions for the browser |
| Authorisation | Scoped per client, least privilege by default |
| Versioning | Path-versioned; breaking changes get a new version |
| Rate limiting | Per client, with published limits |
| Errors | Consistent shape and machine-readable codes |
| Write safety | Idempotency-Key required on POST endpoints that move money or grant access |
| Key retention | 24 hours, stored in the same transaction as the effect it records |
| Key reuse | Same key with a different body returns 409 rather than the stored response |
| Concurrency | ETag and If-Match on mutable resources, so a lost update is a 412 rather than a silent overwrite |
| Events | Webhooks signed and retried with exponential backoff; delivery is at-least-once, so consumers deduplicate on event id |
| Documentation | Per endpoint, with request and response examples |
Yes, and for operators it usually should. The platform consumes identity and entitlement rather than maintaining a competing customer record.
Yes, for the events other systems need to react to — new subscription, cancellation, purchase, publish. Polling an API for state changes is a design smell.
Yes. That is deliberate: it is the only way to be sure the API is actually complete rather than mostly complete.
Existing CRM, billing, subscriber management, rights system — the integration list is usually the thing that decides a platform. Send yours.