Documentation Index
Fetch the complete documentation index at: https://na-36-handover-docs-v2-into-docs-v2-dev-20260518.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
By the end of this tutorial you’ll have a Next.js 15 app that accepts video files, uploads them via TUS (resumable, multi-gigabyte capable), tracks transcoding status, and plays back the finished asset via HLS in the
@livepeer/react Player. The path uses the standard Livepeer Asset API; any gateway provider that exposes it works without code changes.
This is the Persona 2 activation moment for VOD. The live streaming tutorial proved the real-time path; this one proves the persistent-asset path. Most video platforms ship both; the Asset API plus the Stream API together cover the full “Mux with AI bolted on” surface.
Required Tools
- Node.js 20 or later
- A Livepeer gateway endpoint that exposes the Asset API (paid provider or self-hosted)
- API key for the gateway provider
- A code editor
Asset Lifecycle
An asset passes through four phases between upload and playback.| Phase | What’s happening | Player state |
|---|---|---|
waiting | Upload URL issued; file not yet received | Cannot play |
uploading | Bytes streaming to storage | Cannot play |
processing | Transcoding to HLS renditions and MP4 fallbacks | Cannot play |
ready | All renditions available | Plays |
uploading to processing to ready happens server-side after the TUS upload completes. Five webhook events expose lifecycle transitions to your backend: asset.created, asset.updated, asset.ready, asset.failed, asset.deleted. For development the tutorial below polls GET /asset/{id}; production setups use the webhooks.
Project Bootstrap
Install dependencies
livepeer Node SDK runs server-side (asset creation, status checks). @livepeer/react powers the playback Player. tus-js-client handles the resumable upload from the browser.Upload Endpoints
Two server routes handle the asset lifecycle: one creates the asset and returns a TUS URL, the other polls status by ID. Save assrc/app/api/assets/route.ts:
src/app/api/assets/[id]/route.ts:
waiting, uploading, processing, ready, failed). The client polls this endpoint until ready arrives.
Upload Component
Save assrc/app/components/Uploader.tsx:
Playback Page
Save assrc/app/watch/[playbackId]/page.tsx:
livepeer.playback.get() call runs at request time on the server, builds the source array via getSrc(), and hands it to the Player. The Player accepts HLS, MP4, and WebRTC source candidates; for VOD it picks HLS by default and falls through to MP4 if the asset has static MP4 renditions enabled.
Home Page
Save assrc/app/page.tsx:
http://localhost:3000. Pick a video file; watch the upload progress bar, then the processing phase, then the watch link. Click through to playback.
Production Webhooks
Polling works for development. Production replaces it with webhook subscriptions. The gateway emits five events:| Event | Fires when | Use for |
|---|---|---|
asset.created | Asset record created (TUS URL issued) | Initialise database row |
asset.updated | Asset metadata changed | Sync metadata to your DB |
asset.ready | Transcoding complete; playback URLs available | Mark asset as playable in your app |
asset.failed | Transcoding failed | Surface error to user, clean up |
asset.deleted | Asset deleted from the gateway | Cascade delete in your DB |
Uploader component above is replaceable with a webhook listener that updates a database row, which the client subscribes to via Server-Sent Events or WebSocket.
Production Considerations
Six things change between this local setup and a production deployment. Webhooks over polling. Replace the polling loop with webhook subscriptions. Polling is fine for ten files a day; webhooks scale to ten thousand. Access control. Add JWT-based access control on playback for paid or gated content. Public playbacks need no token; gated ones use Livepeer’s playback access control with signed JWTs. Static MP4 renditions. SetstaticMp4: true in asset.create() for assets that need fast time-to-first-frame. Short-form video benefits; long-form prefers HLS.
Encryption. For content that must not be downloaded raw, enable encryption on asset creation. The gateway encrypts assets with AES-CBC and serves decryption keys gated by access control.
Storage policies. Decide which assets persist on IPFS for permanence and which stay in regional cloud storage for cost. Long-tail catalogue goes to IPFS; trending content stays in fast cache.
Webhook signature verification. Always verify the signature header before trusting webhook payloads. Replay attacks are trivial without verification.
Full hardening guidance in .
Common Errors
TUS upload returns 404 on first chunk
TUS upload returns 404 on first chunk
The
tusUploadUrl field on the asset response is empty or stale. Confirm the gateway provider exposes tusEndpoint (some implementations use tusUploadUrl; check the response shape). Adjust the assets/route.ts handler to match.Upload progresses but processing never starts
Upload progresses but processing never starts
The TUS upload completed locally but the gateway didn’t finalise. Check the asset status; if it sits at
uploading after the file completes, the TUS endpoint may not have received the final chunk. Lower chunkSize to 1 MB and retry; some intermediate proxies cap large chunk sizes.Asset reaches `processing` then stalls
Asset reaches `processing` then stalls
The transcoding queue is backed up, or the file is in an unsupported format. Most providers process common formats (MP4, MOV, WebM, MKV) without issue; exotic codecs or DRM-protected files fail at the transcode step. Check
status.errorMessage for the specific reason.Playback shows black screen with HLS source selected
Playback shows black screen with HLS source selected
The asset is in the
ready state but the playback URL hasn’t propagated to the CDN. Wait 30 seconds and retry. If it persists, the asset may have failed silently; query GET /asset/{id} and inspect the status object.`livepeer.playback.get(playbackId)` returns null
`livepeer.playback.get(playbackId)` returns null
The playback ID is wrong, or the asset belongs to a different account. Confirm the playback ID matches the asset created with the same API key. Cross-account playback requires explicit permission grants on the asset.
AI agent prompt
Next Steps
Low-Latency Live Streaming
The live-streaming counterpart to this tutorial.
Transcoding Quickstart
Self-hosted RTMP-to-HLS for full control of the transcoding path.
Multi-Tenant Billing
Add per-customer auth, quotas, and usage tracking.
Production Hardening
Webhook signatures, access control, encryption, storage policies.