Skip to content

Migrate from Livepeer Studio

If you’re running on Livepeer Studio today and considering FrameWorks, this page is for you. The two platforms overlap on live streaming basics, but the API shape and a few access-control features differ enough that a straight endpoint swap is not enough.

This guide covers what stays, what changes, what you gain, and exactly how to do the swap.

Livepeer Studio is the canonical Livepeer gateway: steward of the public transcoding pipeline, and the team that made building on the protocol easy. For most teams, “building on Livepeer” has meant building on Studio. FrameWorks is another gateway on the same network, with a different product shape around it. The rest of this page maps Studio’s surface to FrameWorks: what’s a clean swap, what changes shape, and where they diverge.

  • What stays: RTMP/RTMPS and WHIP ingest, HLS and WebRTC playback, multistream targets, clips, recordings.
  • What changes: REST → GraphQL · API key → JWT or developer token · outbound webhooks → GraphQL subscriptions or polling · playback access policies are recreated with FrameWorks signing keys or webhooks · @livepeer/react@livepeer-frameworks/player-*.
  • Capabilities to evaluate: E-RTMP ingest (HEVC/AV1 contribution over RTMP) · SRT ingest · LL-HLS/DASH delivery · sprite-sheet seek previews · AV1 in the codec list · self-hosting / hybrid options · MCP, wallet auth, and x402 for agents · Skipper · experimental device-discovery productization.
  • What’s not yet at parity: AI Pipelines, outbound HTTP webhooks, C2PA signing, and first-class Python/Go SDKs. See the gap list before planning a production cutover.

The shapes you know in Studio, mapped to FrameWorks:

StudioFrameWorks
POST /streamcreateStream(input) mutation
GET /stream/{id}stream(id) query
PATCH /stream/{id}updateStream(id, input) mutation
DELETE /stream/{id}deleteStream(id) mutation
DELETE /stream/{id}/terminateNo direct equivalent today; stop the encoder, or delete the stream if you are retiring it
record: true flag at createrecord: true at create, or startDVR(streamId) post-create
POST /stream with pull: { source } configcreateStream(input: { ingestMode: PULL, pullSource: { sourceUri, allowedClusters } }) — see Pull-input streams
StudioFrameWorks
POST /clipcreateClip(input) mutation
GET /session/{id}/clipsclipsConnection or clip(id)
POST /asset/request-upload (direct/TUS upload)createVodUpload(input) → S3 multipart upload
POST /asset/upload/urlNo direct equivalent today
Tasks API for upload/transcode progressvodUploadStatus, liveVodLifecycle, or liveProcessingEvents depending on the workflow
GET /asset/{id}vodAsset(id) query
DELETE /asset/{id}deleteVodAsset(id) mutation
StudioFrameWorks
POST /multistream/targetNo global target library; use createPushTarget(streamId, input)
PATCH /multistream/target/{id}updatePushTarget(id, input) mutation
DELETE /multistream/target/{id}deletePushTarget(id) mutation
Inline or attach target to a streamcreatePushTarget(streamId, input) creates a stream-scoped target
Per-target rendition profile / videoOnlyNot exposed today

Studio uses the term “access control” for two separate concerns: authenticating your application to the API, and deciding whether a viewer can play a stream or asset. FrameWorks covers both: developer tokens handle API access, and playback policies handle viewer-side authorization.

API and dashboard access

StudioFrameWorks
API key (Bearer)Developer token (Bearer) — see Agent Access for wallet/x402 alternatives
Team loginEmail/password login
Wallet sign-in (EIP-191)

Viewer-side playback access

StudioFrameWorks
Default public playback IDDefault public playback ID
POST /access-control/signing-keycreateSigningKey(input: { name }) — see Playback Access Control
playbackPolicy: { type: "jwt" }setPlaybackPolicy({ streamId, policy: { type: JWT, jwt: { allowedKids } } })
playbackPolicy: { type: "webhook" }setPlaybackPolicy({ ..., policy: { type: WEBHOOK, webhook: { url, secret, timeoutMs } } })
Livepeer-Jwt header?jwt=<token> universally; Authorization: Bearer where playback requests can carry headers
?jwt= query (MP4 only on Studio)?jwt=<token> works for HLS, LL-HLS, DASH, WebRTC/WHEP, and MP4 — single transport
playback.accessControl webhookWebhook policy: HMAC-signed POST to your URL; return 200 to allow, anything else denies
Studio webhookFrameWorks path
stream.started / stream.idleliveStreamEvents(streamId)
recording.ready / recording.started / recording.waitingliveDvrLifecycle(streamId) plus liveStorageEvents(streamId)
asset.ready / asset.created / asset.updated / asset.failed / asset.deletedliveVodLifecycle for VOD uploads; liveStorageEvents for artifacts
multistream.connected / multistream.error / multistream.disconnectedPoll stream(id).pushTargets.status; no dedicated subscription today
task.spawned / task.updated / task.completed / task.failedNo one-for-one Task API; use workflow-specific lifecycle subscriptions
playback.user.newliveConnectionEvents(streamId)
playback.accessControlPlanned
stream.detection (object detection)Planned

1. Create a FrameWorks account and a stream

Section titled “1. Create a FrameWorks account and a stream”

Sign up at https://chartroom.frameworks.network — email + password, or sign in with an EVM wallet if you’d rather skip email entirely. Then:

mutation CreateStream {
createStream(input: { name: "my-first-stream" }) {
... on Stream {
id
streamKey
playbackId
}
}
}

You get back a streamKey (secret — for your encoder) and a playbackId (public — for viewers).

Studio uses GeoDNS-routed rtmp://rtmp.livepeer.com/live and a WHIP endpoint at https://playback.livepeer.studio/webrtc/{streamKey}. FrameWorks publishes its equivalents via the dashboard or resolveIngestEndpoint(streamKey). RTMP/RTMPS and WHIP remain the low-effort path; FrameWorks additionally accepts E-RTMP (HEVC/AV1 contribution over RTMP) and SRT when your encoder supports them.

OBS / FFmpeg / hardware encoder → swap the URL, keep the rest.

If you were using @livepeer/react:

import { Player } from "@livepeer/react";
<Player playbackId={playbackId} />
import { Player } from "@livepeer-frameworks/player-react";
<Player playbackId={playbackId} gatewayUrl="https://bridge.frameworks.network" />

Svelte and Web Components are first-class too:

  • @livepeer-frameworks/player-svelte
  • @livepeer-frameworks/player-wc (<fw-player>)
  • @livepeer-frameworks/player-core (headless / vanilla)

The component prop names and theming model differ — see Player (Playback) for the full surface.

Pick your client:

  • Houdini (SvelteKit-native GraphQL codegen)
  • urql or Apollo for React
  • graphql-request for plain Node/Bun scripts

Codegen against the schema gives you typed queries:

Terminal window
# example with graphql-codegen
pnpm dlx graphql-codegen \
--schema https://bridge.frameworks.network/graphql \
--documents 'src/**/*.graphql' \
--generates ./src/gql/generated.ts:typescript,typescript-operations

If you want to read the schema directly, it lives at pkg/graphql/schema.graphql on GitHub, and the playground is at https://bridge.frameworks.network/graphql/playground.

Before (Studio):

// Express endpoint receiving webhooks
app.post("/livepeer-webhook", verifyStudioSignature, (req, res) => {
if (req.body.event === "stream.started") {
notifyDashboard(req.body.payload.streamId);
}
res.sendStatus(200);
});

After (FrameWorks):

import { createClient } from "graphql-ws";
const ws = createClient({ url: "wss://bridge.frameworks.network/graphql/ws", connectionParams: { authToken } });
ws.subscribe(
{
query: `subscription { liveStreamEvents { streamId state } }`,
},
{
next: (msg) => {
const { streamId, state } = msg.data.liveStreamEvents;
if (state === "live") notifyDashboard(streamId);
},
error: console.error,
complete: () => {},
}
);

If you can’t keep a connection open (a serverless cron, for instance), poll stream(id) every few seconds instead — it’s idempotent and cheap.

If your Studio streams or assets are public, there is no extra work here: move to the new playbackId and update embeds.

If you use Studio signing keys, playbackPolicy: { type: "jwt" }, Livepeer-Jwt, or playback.accessControl webhooks, FrameWorks now ships an equivalent surface — see Playback Access Control. The translation:

  • POST /access-control/signing-keycreateSigningKey mutation (ES256, asymmetric, private PEM returned once).
  • playbackPolicy: { type: "jwt" }setPlaybackPolicy with type: JWT.
  • Livepeer-Jwt header → ?jwt=<token> query string (universal across HLS, LL-HLS, DASH, WebRTC/WHEP, MP4). Authorization: Bearer works on playback request paths where headers are available.
  • playback.accessControl webhook → setPlaybackPolicy with type: WEBHOOK. FrameWorks POSTs an HMAC-signed body; your endpoint returns 200 to allow, 403 to deny.

Note that signing keys do not transfer between platforms — reissue on FrameWorks. Migration concierge available via in-app ticket if you have a large library of pre-signed JWTs to roll over.

Targets are stream-scoped on both platforms — they don’t carry over when a stream is recreated. After your stream is up:

mutation Restream {
createPushTarget(
streamId: "stream_xxx"
input: {
platform: "twitch"
name: "Twitch — main channel"
targetUri: "rtmp://live.twitch.tv/app/live_xxxxxxxxxx_xxx"
}
) {
id
status
}
}

Run a low-traffic test stream through both platforms in parallel for a day. Watch the FrameWorks side via liveStreamEvents and liveViewerMetrics, compare against Studio’s dashboard, and only point production traffic over once you trust the numbers.

  • SRT ingest — available for encoders and networks where SRT is the better fit.
  • Pull-input streams — point FrameWorks at an RTSP camera, an SRT/RIST contribution feed, an HLS re-stream, or MPEG-TS over UDP and we open the upstream connection for you. Self-hosted edges can pull from RFC1918 / multicast sources when the source is pinned to clusters with allow_private_pull_sources=true — Studio doesn’t have an equivalent. See Pull-input streams. Experimental media-engine discovery exists locally today; FrameWorks packaging through Edge, Tray, CLI, API, and dashboard workflows is tracked on the roadmap.
  • LL-HLS and DASH — served from MistServer’s CMAF output on every cluster, alongside HLS and WebRTC.
  • AV1 in the codec list where the selected cluster and process configuration support it.
  • Sprite-sheet seek previews — a 10×10 grid plus a WebVTT cue file, generated for every recording, ready for hover-scrub UIs.
  • Self-hosting and hybrid hosting. Run the whole stack on your hardware, or mix BYO edge with our managed control plane. Production rollouts need the same capacity, DNS, TLS, and monitoring validation as any operator-owned infrastructure.
  • Cross-cluster federation — one viewer pool routed across clusters; the closer edge wins automatically.
  • Agent-first surface. MCP server at /.well-known/mcp.json, wallet auth (EIP-191), and gasless USDC payments via x402 (EIP-3009). See Agent Access.
  • Skipper — an in-product AI consultant for API discovery and diagnostics. Reach it from the dashboard, the docs (⌘J), or MCP.
  • Player on Svelte and as Web Components, not just React.

What’s not yet at parity, and what’s coming

Section titled “What’s not yet at parity, and what’s coming”

Current gaps to account for:

  • AI Pipelines (text-to-image, image-to-video, ASR, live-video-to-video, segmentation) — not exposed as a FrameWorks API today. The Livepeer team’s productized AI offering is Daydream (real-time AI video, runs on the same Livepeer network); keep AI workloads there or on existing managed Livepeer surfaces until FrameWorks exposes an equivalent.
  • Outbound webhooks — today we use GraphQL subscriptions. HTTP-callback delivery is tracked in the roadmap.
  • C2PA signing and creator-ID provenance on transcode — not exposed as a public FrameWorks API today.
  • First-class Python and Go SDKs — use GraphQL codegen against the public schema until first-party SDKs are published.
  • Derived thumbnail asset enforcement — playback access control protects endpoint resolution and media sessions. Chandler thumbnail URLs are preview assets and are not a separate auth boundary yet.
  • VOD URL import and Studio library importer — no direct upload via URL equivalent or one-shot Studio-asset importer today. If you have a sizeable library, open an in-app ticket or message us in Discord so we can scope the migration.
  • Per-target rendition selection on multistream — Studio lets you pick which rendition goes to each push target. We push the default output today.
  • RTMP and FLV-over-HTTP pull sources — our MistServer fork has no RTMP/FLV pull module, so a pull: { source: "rtmp://…" } or a Studio-style https://.../*.flv upstream won’t work. Everything else Mist documents (RTSP, SRT, RIST, HLS, DTSC, MPEG-TS over UDP including multicast) does. If RTMP pull is a critical requirement, contact support before planning your migration — there is no committed timeline yet.

Heads-up so nothing surprises you:

  • Billing rates are still maturing. Rates may adjust as we scale, but you keep the allowances you signed up for.
  • Self-hosted and hybrid production cutovers need operator validation. Test DNS, TLS, firewall policy, capacity, node enrollment, and upgrade runbooks before moving production traffic.
  • Marketplace primitives ship; the vetted public marketplace program is on the roadmap. Validate operator vetting, pricing, approval flow, and support boundaries before relying on third-party clusters for production traffic.
  • GraphQL learning curve. If you’ve never written a query, the docs playground is the quickest way to inspect available fields. Skipper can also draft queries.
  • Subscriptions are connection-oriented. Polling fallback is fine if a long-lived connection is awkward in your stack.
  • The player swap is not API-compatible. Props and theming differ — budget a few hours to walk through Player (Playback) examples.
  • Stream keys, playback IDs, and asset IDs are not transferable. Reissue stream keys and update embeds.
  • Playback signing is not key-portable. Re-create signing keys in FrameWorks and update your token transport from Livepeer-Jwt to ?jwt= or Authorization: Bearer.
  • No webhook history backfill. Past Studio events stay on Studio; subscribe fresh on FrameWorks.

Pick whichever channel fits.

ChannelBest forWhere
Skipper (AI consultant)Quick troubleshooting, API discovery, diagnostics, after-hours, non-human callersDashboard /skipper · ⌘J in docs · MCP at /mcp
In-app ticketingAccount-specific issues, billing, anything tied to your tenant data, library-migration helpDashboard /support
ForumLong-form Q&A, recipes, searchable community knowledgeforum.frameworks.network
DiscordReal-time chat, casual questionsJoin
GitHubBugs, feature requests, deeper technical threadsLivepeer-FrameWorks/monorepo