Documentation Index
Fetch the complete documentation index at: https://mintlify.com/pingdotgg/lawn/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The Videos API manages video files, metadata, processing status, and workflow states. Videos belong to projects and support features like public sharing, thumbnails, and time-based commenting.
All video functions are defined in convex/videos.ts.
Functions
create
Creates a new video entry in a project.
import { useMutation } from 'convex/react';
import { api } from '../convex/_generated/api';
function UploadButton({ projectId }) {
const createVideo = useMutation(api.videos.create);
const handleUpload = async (file: File) => {
const videoId = await createVideo({
projectId,
title: file.name,
description: 'Uploaded video',
fileSize: file.size,
contentType: file.type,
});
console.log('Created video:', videoId);
};
return <button onClick={handleUpload}>Upload</button>;
}
The project to create the video in
Video description (optional)
File size in bytes (optional)
MIME type (optional, e.g., “video/mp4”)
The ID of the created video
Permissions: Requires member role in the project’s team.
Implementation: convex/videos.ts:52
list
Lists all videos in a project with comment counts.
import { useQuery } from 'convex/react';
import { api } from '../convex/_generated/api';
function VideoList({ projectId }) {
const videos = useQuery(api.videos.list, { projectId });
return (
<div>
{videos?.map(video => (
<div key={video._id}>
<h3>{video.title}</h3>
<p>{video.workflowStatus} • {video.commentCount} comments</p>
</div>
))}
</div>
);
}
The project to list videos from
Array of video objects with the following fields:status
'uploading' | 'processing' | 'ready' | 'failed'
Processing status
workflowStatus
'review' | 'rework' | 'done'
Workflow status (defaults to ‘review’)
Name of the user who uploaded the video
Number of comments on the video
Video duration in seconds
Mux playback ID for video streaming
Permissions: Requires access to the project.
Implementation: convex/videos.ts:84
get
Retrieves a single video by ID.
import { useQuery } from 'convex/react';
import { api } from '../convex/_generated/api';
function VideoDetail({ videoId }) {
const video = useQuery(api.videos.get, { videoId });
return (
<div>
<h1>{video?.title}</h1>
<p>{video?.description}</p>
<p>Your role: {video?.role}</p>
</div>
);
}
Video object with all fields plus user’s rolerole
'owner' | 'admin' | 'member' | 'viewer'
User’s role in the video’s team
Permissions: Requires access to the video.
Implementation: convex/videos.ts:113
getByPublicId
Retrieves a public video by its public ID (for public sharing).
import { useQuery } from 'convex/react';
import { api } from '../convex/_generated/api';
function PublicVideoPlayer({ publicId }) {
const result = useQuery(api.videos.getByPublicId, { publicId });
if (!result) return <div>Video not found</div>;
return <VideoPlayer video={result.video} />;
}
The public ID of the video
Public video data or null if not found/not public
Permissions: Public - no authentication required. Only returns videos with visibility: 'public' and status: 'ready'.
Implementation: convex/videos.ts:126
update
Updates video title and/or description.
import { useMutation } from 'convex/react';
import { api } from '../convex/_generated/api';
function EditVideoForm({ videoId }) {
const updateVideo = useMutation(api.videos.update);
const handleSubmit = async (title: string, description: string) => {
await updateVideo({
videoId,
title,
description,
});
};
return <form onSubmit={handleSubmit}>...</form>;
}
New description (optional)
Permissions: Requires member role in the video’s team.
Implementation: convex/videos.ts:202
updateWorkflowStatus
Updates the workflow status of a video.
import { useMutation } from 'convex/react';
import { api } from '../convex/_generated/api';
function WorkflowStatusButtons({ videoId }) {
const updateStatus = useMutation(api.videos.updateWorkflowStatus);
return (
<div>
<button onClick={() => updateStatus({ videoId, workflowStatus: 'review' })}>
Review
</button>
<button onClick={() => updateStatus({ videoId, workflowStatus: 'rework' })}>
Needs Rework
</button>
<button onClick={() => updateStatus({ videoId, workflowStatus: 'done' })}>
Done
</button>
</div>
);
}
workflowStatus
'review' | 'rework' | 'done'
required
The new workflow status
Permissions: Requires member role in the video’s team.
Implementation: convex/videos.ts:233
setVisibility
Sets video visibility to public or private.
import { useMutation } from 'convex/react';
import { api } from '../convex/_generated/api';
function VisibilityToggle({ videoId, currentVisibility }) {
const setVisibility = useMutation(api.videos.setVisibility);
const toggle = () => {
const newVisibility = currentVisibility === 'public' ? 'private' : 'public';
setVisibility({ videoId, visibility: newVisibility });
};
return <button onClick={toggle}>{currentVisibility}</button>;
}
visibility
'public' | 'private'
required
The new visibility setting
Permissions: Requires member role in the video’s team.
Implementation: convex/videos.ts:219
remove
Deletes a video and all associated data (comments, share links).
import { useMutation } from 'convex/react';
import { api } from '../convex/_generated/api';
function DeleteVideoButton({ videoId }) {
const removeVideo = useMutation(api.videos.remove);
const handleDelete = async () => {
if (confirm('Delete this video?')) {
await removeVideo({ videoId });
}
};
return <button onClick={handleDelete}>Delete</button>;
}
Permissions: Requires admin role in the video’s team.
Implementation: convex/videos.ts:247
getVideoForPlayback
Retrieves video data specifically for playback purposes.
import { useQuery } from 'convex/react';
import { api } from '../convex/_generated/api';
function VideoPlayer({ videoId }) {
const video = useQuery(api.videos.getVideoForPlayback, { videoId });
return <MuxPlayer playbackId={video?.muxPlaybackId} />;
}
The video to retrieve for playback
Video object with playback information
Permissions: Requires viewer role in the video’s team.
Implementation: convex/videos.ts:450
getByShareGrant
Retrieves a video by share grant token (for external reviewers with share links).
import { useQuery } from 'convex/react';
import { api } from '../convex/_generated/api';
function SharedVideoPlayer({ grantToken }) {
const result = useQuery(api.videos.getByShareGrant, { grantToken });
if (!result) return <div>Access denied or video not found</div>;
return <VideoPlayer video={result.video} shareLink={result.shareLink} />;
}
The share grant token obtained from issueAccessGrant
Video data or null if grant is invalid/expiredMux playback ID for streaming
Share link informationWhether downloads are allowed
Permissions: Public - requires valid share grant token
Implementation: convex/videos.ts:138
getPublicIdByVideoId
Gets the public ID for a video (used for generating public links).
import { useQuery } from 'convex/react';
import { api } from '../convex/_generated/api';
function ShareButton({ videoId }) {
const publicId = useQuery(api.videos.getPublicIdByVideoId, {
videoId: videoId.toString()
});
if (!publicId) return null;
const shareUrl = `https://app.example.com/watch/${publicId}`;
return <button onClick={() => navigator.clipboard.writeText(shareUrl)}>
Copy Public Link
</button>;
}
The public ID, or null if video is not public or not ready
Permissions: Requires access to the video
Implementation: convex/videos.ts:160
Video Status Flow
Videos progress through the following statuses:
- uploading - Video is being uploaded to S3
- processing - Video is being processed by Mux
- ready - Video is ready for playback
- failed - Upload or processing failed
Workflow Status
Videos have an independent workflow status for collaboration:
- review (default) - Video is ready for review
- rework - Changes requested
- done - Video approved
Usage Examples
Creating and Uploading a Video
import { useMutation } from 'convex/react';
import { api } from '../convex/_generated/api';
function VideoUploader({ projectId }) {
const createVideo = useMutation(api.videos.create);
const handleFileSelect = async (file: File) => {
// Create video entry
const videoId = await createVideo({
projectId,
title: file.name,
fileSize: file.size,
contentType: file.type,
});
// Upload file to S3 (implementation depends on your upload strategy)
await uploadToS3(file, videoId);
};
return <input type="file" onChange={(e) => handleFileSelect(e.target.files[0])} />;
}
Video List with Filtering
import { useQuery } from 'convex/react';
import { api } from '../convex/_generated/api';
function FilteredVideoList({ projectId }) {
const allVideos = useQuery(api.videos.list, { projectId });
const [filter, setFilter] = useState<'all' | 'review' | 'done'>('all');
const videos = allVideos?.filter(v =>
filter === 'all' || v.workflowStatus === filter
);
return (
<div>
<select value={filter} onChange={(e) => setFilter(e.target.value)}>
<option value="all">All</option>
<option value="review">Review</option>
<option value="done">Done</option>
</select>
{videos?.map(video => <VideoCard key={video._id} video={video} />)}
</div>
);
}