Skip to content
Welcome to the new, unified Livepeer documentation! πŸ‘‹
VOD w/ Access Control

VOD with Access Control

Adding access control to a video on-demand only takes a few lines of code. The example below uses useCreateAsset and a webhook to create and watch a gated video on-demand.


The webhook access control flow also works the same with Streams - this example just shows it being used with Assets as a demonstration.

Create an Access Control Webhook Handler

Set up an endpoint (e.g., (opens in a new tab)) to handle the logic for granting or denying access to your VOD assets. This endpoint should accept a POST request with a JSON payload containing the access key and webhook context.

This is an example of a payload this endpoint would receive:

POST /api/check-access
  "accessKey": "your-access-key",
  "context": {
    "assetId": "abcd1234",
    "userId": "user5678"
  "timestamp": 1680530722502

The payload in context is defined by your application.

Register an Access Control Webhook

Use the Livepeer Studio dashboard to create a new webhook with the type playback.accessControl and specify the URL of your access control endpoint. (opens in a new tab)

You can then take the ID of the webhook you created and use it in the next step.

Create an asset with a playback policy webhook

You can now create an asset with a playback policy webhook, passing the ID of the webhook you created in Step 2.

import { useCreateAsset } from '@livepeer/react';
import { useCallback, useState, useMemo } from 'react';
import { useDropzone } from 'react-dropzone';
export const CreateAndViewAsset = () => {
  const [video, setVideo] = useState<File | undefined>();
  const {
    mutate: createAsset,
    data: asset,
    status: createStatus,
    error: createError,
  } = useCreateAsset(
      ? {
          sources: [
              file: video,
              playbackPolicy: {
                type: 'webhook',
                // This is the id of the webhook you created in step 2
                webhookId: '<webhook_id>',
                webhookContext: {
                  // This is the context you want to pass to your webhook
                  // It can be anything you want, and it will be passed back to your webhook
          ] as const,
      : null,
  const onDrop = useCallback(async (acceptedFiles: File[]) => {
    if (acceptedFiles && acceptedFiles.length > 0 && acceptedFiles?.[0]) {
  }, []);
  const { getRootProps, getInputProps } = useDropzone({
    accept: {
      'video/*': ['*.mp4'],
    maxFiles: 1,
  const progressFormatted = useMemo(
    () =>
      progress?.[0].phase === 'failed'
        ? 'Failed to process video.'
        : progress?.[0].phase === 'waiting'
        ? 'Waiting'
        : progress?.[0].phase === 'uploading'
        ? `Uploading: ${Math.round(progress?.[0]?.progress * 100)}%`
        : progress?.[0].phase === 'processing'
        ? `Processing: ${Math.round(progress?.[0].progress * 100)}%`
        : null,
  return (
      <div {...getRootProps()}>
        <input {...getInputProps()} />
        <p>Drag and drop or browse files</p>
      {createError?.message && <p>{createError.message}</p>}
      {video ? <p>{}</p> : <p>Select a video file to upload.</p>}
      {progressFormatted && <p>{progressFormatted}</p>}
        onClick={() => {
        disabled={!createAsset || createStatus === 'loading'}

Configure the Player with an Access Key

If you are using the Livepeer.js Player, you can use the accessKey prop to provide your custom access key, which is then passed to the webhook you created to verify that it is valid.

import { Player } from '@livepeer/react';
export const CreateAndViewAsset = () => {
  const accessKey = getAccessKeyForYourApplication();
  return <Player playbackId={playbackId} accessKey={accessKey} />;

If you are not using the player, you will need to manually append the access key to the m3u8 URL as a query parameter, similar to:

Receive the Webhook from Livepeer

When a user attempts to access the VOD asset, Livepeer will call your access control endpoint with the access key and webhook context. Your endpoint should process the request and return a response indicating whether the stream access should be allowed or disallowed.

Here is an example request sent to your access control endpoint:

POST /api/check-access
  "context": {
    // Same value from the `asset.playbackPolicy.webhookContext` field
  "accessKey": "<access_key>" // this is exact value of the prop passed to the Player, or the query parameter

In your access control endpoint, implement the logic to verify the access key and decide whether to grant access to the VOD asset. If the access is allowed, return a 2XX response. Otherwise, the playback will be disallowed.

Wrap Up

That's it! Access control, added. You now have a solution for gating on-demand content with webhooks! This also works for streams as well.