Docs › The <video-piper-recorder> web component
The <video-piper-recorder> web component
Let people record their screen, camera or both, or pick a file, right on your site. It uploads straight to your account while they record, so processing starts the moment they stop. Your API key stays on your server: the page gets a one-video upload token instead.
// 1. Your server: an endpoint that makes a token (POST /upload-tokens with your API key)
app.post("/api/upload-token", async (req, res) => { // check the user is signed in, as you need
const r = await fetch(`${API}/upload-tokens`, { method: "POST",
headers: { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ expiresIn: 300, maxSeconds: 600, metadata: { userId: String(req.user.id) } }) });
res.status(r.status).json(await r.json()); // { uploadToken: "vpu_…", … }
});
// Program.cs (ASP.NET Core 8+). The API key stays on your server: set VideoPiper__ApiKey (or appsettings.json).
using System.Collections.Concurrent;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text.Json;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpClient("videopiper", client =>
{
client.BaseAddress = new Uri("https://api.videopiper.dev/v1/");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", builder.Configuration["VideoPiper:ApiKey"]);
});
var app = builder.Build();
// A one-video upload token for <video-piper-recorder token-url="/api/upload-token">.
app.MapPost("/api/upload-token", async (ClaimsPrincipal user, IHttpClientFactory http) =>
{
var userId = user.FindFirstValue(ClaimTypes.NameIdentifier) ?? "anonymous";
using var response = await http.CreateClient("videopiper").PostAsJsonAsync("upload-tokens", new
{
expiresIn = 300, maxSeconds = 600, metadata = new Dictionary<string, string> { ["userId"] = userId },
});
return Results.Content(await response.Content.ReadAsStringAsync(), "application/json", statusCode: (int)response.StatusCode);
}); // add .RequireAuthorization() so only your signed-in users get tokens
app.Run();
<!-- 2. Your page: the recorder asks token-url for a fresh token when someone starts -->
<script src="https://videopiper.com/recorder/recorder.js"></script>
<video-piper-recorder token-url="/api/upload-token" max-seconds="600"></video-piper-recorder>
<script>
document.querySelector("video-piper-recorder")
.addEventListener("vp-uploaded", (e) => console.log("uploaded", e.detail.videoId));
</script>
| Attribute | Description |
|---|---|
token-url | Recommended. Your endpoint; the recorder POSTs to it (with your site's cookies) for a fresh token at the moment someone starts recording or picks a file, and expects {"uploadToken": "vpu_…"}. A page left open for hours never starts with an expired token. |
upload-token | Or a token made when the page loads (or set later with element.setToken(token)). Use token-url or this. |
modes | Any of screen+camera, screen, camera, file (default all, in that order; the first is selected). Modes the browser can't do are hidden. |
layout | For screen + camera: the starting layout: pip (default), side-by-side, or a layout object as JSON. People can change it before recording unless you set layouts; the layout they end up with is what the video is made with. |
layouts | Which layouts people may choose in screen + camera mode: pip side-by-side (default: a picker for the camera corner, its size and side by side), just one of them, or none to hide the picker and always use layout. The preview shows the camera where the video will put it. |
max-seconds | Stops recording at this length (the token's maxSeconds also trims it on the server). |
review | "false" uploads as soon as recording stops; otherwise people watch it back, then Upload or Retake. |
wait | Keep checking until the video is processed, then fire vp-ready. |
- Events:
vp-state({state}),vp-progress({loaded, total}),vp-uploaded({videoId}),vp-ready({videoId, status}) andvp-error({message, code}, with a message fit to show).vp-layout-change({layout}, the full layout object) when someone picks another layout. Your server also gets the usualvideo.readywebhook, with the token'smetadata. - Plans: recording (screen, camera, upload while recording) needs Pro or Scale; picking a file works on every plan.
- Styling: it fills its container's width. Theme it with
--vp-accent,--vp-panel,--vp-lineand--vp-text. - Browsers ask people for camera, microphone and screen permission; screen recording works in desktop Chrome, Edge, Firefox and Safari.
Upload tokens
POST /upload-tokens (with your API key, from your server) makes a token that can upload exactly one video. All settings are optional, and the browser can't change them:
{ "expiresIn": 3600, // 60 to 86400 seconds; default 3600
"maxSeconds": 600, // the video is trimmed to this length
"maxBytes": 2147483648, // the largest upload; default your upload limit
"maxHeight": 1080, // top quality
"captions": true, // Pro and Scale
"metadata": { "userId": "42" } } // set on the video, for your webhooks and listings
→ 201 { "uploadToken": "vpu_…", "tokenId": "u-…", "expiresAt": 1790910405, "policy": { … } }
expiresInis the time to start. Once an upload has started, its token keeps working for that upload for 24 hours, so a long recording (or one started just before the token expired) always finishes. Withtoken-url, a few minutes is plenty.- With the token the browser can only create its one upload, send its parts, complete or abort it, and read its status. Anything else answers
403 UPLOAD_TOKEN_SCOPE; a second video,409 UPLOAD_TOKEN_USED. Aborting an unfinished upload (a retake) frees it again. - It's shown once (only a hash is kept), expires on its own, and counts toward your key's rate limits and your plan, like any of your uploads.
- Make one per recording session. Your Account page has these snippets filled in with your addresses, and a place to try the recorder.