Developer API
Integrate video hosting and streaming capabilities into your applications with our RESTful API. Upload videos, manage content, and deliver media at scale.
Projects API with subprojects, Auto-Delete Plans, Branding (per-project logo with auto-extracted color palette), and the full Player Skin API with 4-level inheritance (account / project / channel / video). See the corresponding sections below for the full reference.
Quick Start
Get your API key and make your first request in 5 minutes.
Get your API keys
Your API requests are authenticated using API keys. Any request that doesn't include an API key will return an error. You can generate an API key from your Dashboard under Videos → Global Settings → API Keys.
API Authentication
Use Bearer authentication. Send an Authorization header in all requests:
Authorization: Bearer YOUR_API_KEY
Base URL
StreamVault has a single canonical base URL. All endpoints — including video uploads — live under it:
| Purpose | URL |
|---|---|
| API base (everything: videos, uploads, channels, streams, projects, branding) | https://api.streamvault.one/v1 |
| Health check (returns Worker version + uptime) | https://api.streamvault.one/v1/health |
Make your first request
Send an authenticated request to the videos endpoint to list your videos:
// List your videos
const response = await fetch('https://api.streamvault.one/v1/videos', {
headers: {
'Authorization': 'Bearer YOUR_API_KEY'
}
});
const data = await response.json();
console.log(data.videos);Example Response
{
"pager": {
"page": 1,
"totalPages": 1,
"totalResults": 10,
"sort": ""
},
"videos": [
{
"id": "fFL56RU6PjvJPeE",
"title": "My Video",
"channelKey": "f8RMr2q4h6yNpv9",
"status": 3,
"created": "2024-01-15T10:30:00Z"
}
]
}Rate Limits
API requests are limited to 120 requests per minute per API key. When you exceed the limit, requests return 429 Too Many Requests until the window resets. Batch endpoints let you act on many videos in a single request — prefer them over per-video loops.
{
"error": "Rate limit exceeded. Please slow down and try again shortly."
}Errors
Errors return a JSON body with an error message and a conventional HTTP status code:
| Status | Meaning |
|---|---|
| 400 | Invalid request — missing or malformed parameters |
| 401 | Missing, invalid, or revoked API key |
| 404 | Resource not found, or not owned by your account |
| 429 | Rate limit exceeded — retry after a short delay |
| 500 | Something went wrong on our side — safe to retry |
{
"error": "Invalid or revoked API key"
}Videos API
The Videos API allows you to upload, manage, and retrieve video content programmatically.
Upload Videos
| Method | Endpoint | Description |
|---|---|---|
| POST | /videos | Upload video via URL |
| POST | /videos/browser | Get TUS upload credentials for browser-based upload |
Manage Videos
| Method | Endpoint | Description |
|---|---|---|
| GET | /videos | Get list of videos |
| GET | /videos/{id} | Get a single video |
| GET | /videos/{id}/status | Get video transcoding status |
| PUT | /videos/{id} | Update video details |
| DELETE | /videos/{id} | Delete a video |
| POST | /videos/{id}/cta/copy | Copy CTA from one video to others |
| POST | /videos/change-project | Move videos between projects |
Additional Operations
| Method | Endpoint | Description |
|---|---|---|
| GET | /videos/{id}/original-file | Get original video URL |
| GET | /videos/collected-emails | Get collected emails (last 365 days) |
| POST | /videos/{id}/expirable-link | Create expirable playback link |
| PUT | /videos/image/{id} | Update video thumbnail |
| POST | /videos/captions/{id} | Add captions/subtitles |
| DELETE | /videos/captions?videoId=&captionsId= | Delete captions/subtitles |
Video Status Codes
| Status | Value | Description |
|---|---|---|
| Na | 0 | Not available |
| Uploaded | 1 | Uploaded |
| Processing | 2 | Transcoding in progress |
| Published | 3 | Ready for playback |
| Error | -1 | Transcoding failed |
| Deleted | 50 | Deleted |
Quick Example
// Upload a video via URL
const response = await fetch('https://api.streamvault.one/v1/videos', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
channelKey: 'abc123',
url: 'https://example.com/video.mp4'
})
});
const video = await response.json();
console.log(video.iframeLink);Projects API
Projects are folders that group your videos. They support arbitrary nesting via parent_id so you can model brands → shows → seasons (or any hierarchy that fits your workflow).
Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /projects | List all your projects |
| POST | /projects | Create a project (or subproject via parent_id) |
| GET | /projects/{id} | Get one project |
| PATCH | /projects/{id} | Update name / description / parent_id |
| DELETE | /projects/{id} | Delete (cascade-deletes videos in the project) |
Create a project
curl -X POST https://api.streamvault.one/v1/projects \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Kinana VOD AR",
"description": "Arabic news bulletins for the Kinana brand",
"parent_id": null
}'Response
{
"id": "448ba8b6-eaa6-4ff8-80be-9a075e47c642",
"user_id": "...",
"name": "Kinana VOD AR",
"description": "...",
"parent_id": null,
"created_at": "2026-05-07T...",
"updated_at": "2026-05-07T..."
}Subprojects (parent_id)
Pass parent_id (a UUID) to create a subproject under an existing parent. Set parent_id to null for a top-level project.
Update / move a project
PATCH /v1/projects/{id} accepts a partial body. Setting parent_id to another project's UUID re-parents it; setting it to null makes it top-level.
{ "name": "Renamed", "description": "New description", "parent_id": "uuid-of-new-parent-or-null" }Auto-Delete Plans
Schedule automatic deletion of old videos based on retention rules. Mirrors DynTube's /auto-delete-plans URL shape so existing DynTube integrations work with just a host swap.
Endpoints
| Method | Endpoint | Description |
|---|---|---|
| POST | /auto-delete-plans/search | List with body-based pagination (DynTube-compatible) |
| GET | /auto-delete-plans | List with query params (?page=1&size=50) |
| GET | /auto-delete-plans/{id} | Get one plan |
| POST | /auto-delete-plans | Create a plan |
| PUT | /auto-delete-plans/{id} | Update a plan |
| DELETE | /auto-delete-plans/{id} | Delete a plan |
Plan shape
{
"id": "uuid",
"name": "Daily News Cleanup",
"project_ids": ["uuid1", "uuid2"],
"retention_days": 15,
"title_filter": { "enabled": false, "value": "" },
"is_active": true,
"last_run_at": "2026-05-09T12:00:00Z",
"last_run_deleted_count": 7,
"created_at": "2026-05-01T00:00:00Z",
"updated_at": "2026-05-09T12:00:00Z"
}project_ids— list of project UUIDs the rule applies to. Empty array = applies to every project you own.retention_days— videos older than this many days are deleted.title_filter.enabled+value— substring match (case-insensitive). Videos must match the filter to be deleted.is_active: false— pauses the rule without deleting it.
Plans run automatically once per hour. The dashboard shows last_run_at and last_run_deleted_count after each run.
Create a plan
curl -X POST https://api.streamvault.one/v1/auto-delete-plans \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Daily News 15-day cleanup",
"project_ids": ["uuid-of-project-1"],
"retention_days": 15,
"title_filter": { "enabled": true, "value": "daily news" },
"is_active": true
}'Search / list
curl -X POST https://api.streamvault.one/v1/auto-delete-plans/search \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "page": 1, "pageSize": 50 }'Response
{
"plans": [ /* ... */ ],
"pager": { "page": 1, "pageSize": 50, "totalResults": 3, "totalPages": 1 }
}Branding (Project Logo)
Upload a brand logo per project, automatically extract its color palette, and apply it to that project's player skin (controls color, big play disc, frame, watermark). The same logo also doubles as a corner overlay on the player.
Endpoints
| Method | Endpoint | Description |
|---|---|---|
| PUT | /projects/{id}/logo | Upload a logo (multipart) and auto-extract its palette |
| GET | /projects/{id}/logo | Get the logo URL + extracted palette |
| DELETE | /projects/{id}/logo | Remove the logo (does not clear applied colors) |
| POST | /projects/{id}/logo/apply | Re-apply the saved palette to player_settings |
Upload a file
curl -X PUT https://api.streamvault.one/v1/projects/PROJECT_ID/logo \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "file=@./logo.png" \
-F "auto_apply=true" \
-F 'palette={"vibrant":"#FFC800","dark_muted":"#121218","muted":"#FFFFFF","text_on_vibrant":"#000000"}'Or pass a remote URL
curl -X PUT https://api.streamvault.one/v1/projects/PROJECT_ID/logo \ -H "Authorization: Bearer YOUR_API_KEY" \ -F "url=https://example.com/logo.png" \ -F "auto_apply=true"
- Max 2 MB
- Accepted: PNG, JPEG, WebP, SVG (SVG is rasterized at 256×256 server-side before extraction)
auto_apply(defaulttrue) — write the extracted palette toproject_player_settings(controls_color,big_play_color,big_play_icon_color,player_color,watermark_color)palette(optional, JSON) — pre-extracted palette to use directly. When provided, the server skips its own extraction and uses your values verbatim. Recommended from a browser context where@vibrant/coreis already running client-side; server-side extraction is best-effort and may return only a partial palette. Shape:{ vibrant?, dark_vibrant?, light_vibrant?, muted?, dark_muted?, light_muted?, text_on_vibrant? }— all fields hex strings, all optional/nullable.
Response (palette extraction)
{
"logo_url": "https://api.streamvault.one/b2/project-logos/PROJECT_ID/logo.png",
"logo_palette": {
"vibrant": "#FF6B35",
"dark_vibrant": "#7A2E0E",
"light_vibrant": "#FFB89A",
"muted": "#806257",
"dark_muted": "#3B2A20",
"light_muted": "#D4C4BA",
"text_on_vibrant": "#FFFFFF",
"extracted_at": "2026-05-09T22:00:00Z"
},
"applied": true,
"applied_fields": [
"controls_color", "big_play_color", "big_play_icon_color",
"player_color", "watermark_color"
]
}Re-apply palette
If you previously declined auto_apply, or you've manually overridden colors and want to revert to the logo-derived palette:
curl -X POST https://api.streamvault.one/v1/projects/PROJECT_ID/logo/apply \ -H "Authorization: Bearer YOUR_API_KEY"
Logo overlay & deletion
When the project's logo_overlay_enabled player setting is true, the uploaded logo renders as a corner watermark on every player in the project. Position, size, and opacity are configured under Player Skin.
Delete logo
curl -X DELETE https://api.streamvault.one/v1/projects/PROJECT_ID/logo \ -H "Authorization: Bearer YOUR_API_KEY"
Note: deleting the logo does not clear the applied colors from project_player_settings. Use the Player Skin API or the dashboard's "Reset to inherit" links to clear those.
Player Skin API
Configure how the video player looks and behaves. Settings can be set at four levels with override precedence.
When controls_color is set (either directly or via the Branding palette auto-apply), the entire control chrome paints with that color: progress fill, seek handle, volume slider, time display, playback speed pill, and every icon (play/pause, next, volume, settings, picture-in-picture, fullscreen). The big-play disc uses big_play_color, which falls back to controls_color when unset. Net effect: one brand color drives every accent on the player.
Inheritance hierarchy
Per-video override (highest priority, video.player_settings JSONB) ↓ Channel default (channel_player_settings) ↓ Project default (project_player_settings — also where logo palette writes) ↓ Account default (account_player_settings — set in Global Settings → Player Skins)
A field set to null means "inherit from parent". The runtime resolution merges left-to-right; per-video wins, then channel, project, account, then a hardcoded system default.
Account / project / channel / video endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /player-defaults | Get your account-level skin defaults |
| PATCH | /player-defaults | Update account-level defaults |
| GET | /projects/{id}/player-settings | Get effective project defaults |
| PATCH | /projects/{id}/player-settings | Override fields at project level |
| GET | /channels/{id}/player-settings | Get effective channel defaults |
| PATCH | /channels/{id}/player-settings | Override fields at channel level |
| GET | /videos/{id}/player-settings | Get the merged effective settings for this video |
| PATCH | /videos/{id}/player-settings | Override at the video level |
Settings shape
{
"skin": "modern",
"player_color": "#0E1014",
"controls_color": "#4CC2E0",
"watermark_color": "#FFFFFF",
"controls_opacity": 90,
"watermark_opacity": 70,
"big_play_button": true,
"big_play_color": null,
"big_play_icon_color": null,
"big_play_opacity": 100,
"big_play_size": "md",
"big_play_shape": "circle",
"big_play_show_on_hover": false,
"autoplay": false,
"start_muted": false,
"loop": false,
"captions_on_by_default": false,
"seek_buttons": true,
"fullscreen_control": true,
"ip_watermark": false,
"playback_speed_control": true,
"allow_fast_forward": true,
"click_for_sound": false,
"click_to_resume": false,
"auto_resume_playback": false,
"default_quality": "auto",
"default_volume": 100,
"show_player_controls": true,
"persistent_control_bar": false,
"volume_control": true,
"quality_selector": true,
"sticky_player": false,
"pause_on_scroll": false
}Skin options
skin accepts one of: modern (default), pill, sharp, halo, minimal, bold, heritage, floating.
Big play button options
big_play_color(hex,null= inheritcontrols_color)big_play_icon_color(hex,null= inheritplayer_color)big_play_opacity(int 0–100)big_play_size(sm/md/lg)big_play_shape(circle/rounded/square)big_play_show_on_hover(boolean — keeps the disc visible during playback while the user hovers; not in DynTube)
Inheritance behavior
Setting any color/opacity/skin field to null clears that override. The next-highest-priority layer takes effect. Example: a project sets controls_color: '#FF6B35', a video sets controls_color: null — that video plays with the project's orange. If the project later clears its override too, the video falls back to the account default cyan.
The dashboard shows an "Inherited from {project|channel|account}" badge next to fields that are inheriting, with a "Reset to inherit" link on overridden fields.
Update example
curl -X PATCH https://api.streamvault.one/v1/projects/PROJECT_ID/player-settings \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"skin": "pill",
"controls_color": "#FF6B35",
"big_play_size": "lg",
"big_play_show_on_hover": true,
"ip_watermark": true
}'CTAs
Overlay call-to-action buttons and email-capture prompts on the player — per video, or per project with inheritance.
Per-video CTAs
| Method | Endpoint | Description |
|---|---|---|
| GET | /videos/{id}/ctas | List a video's CTAs |
| POST | /videos/{id}/ctas | Create a CTA on a video |
| PATCH | /videos/{id}/ctas/{ctaId} | Update a CTA |
| DELETE | /videos/{id}/ctas/{ctaId} | Delete a CTA |
| POST | /videos/{id}/cta/copy | Copy this video's CTAs to other videos |
| POST | /videos/batch/cta | Bulk-assign a CTA to many videos |
Per-project CTAs
| Method | Endpoint | Description |
|---|---|---|
| GET | /projects/{id}/ctas | List a project's CTAs |
| POST | /projects/{id}/ctas | Create a project-level CTA |
| PATCH | /projects/{id}/ctas/{ctaId} | Update a project CTA |
| DELETE | /projects/{id}/ctas/{ctaId} | Delete a project CTA |
Inheritance & override
Videos inherit their project's CTAs by default. Toggle per level withPATCH /videos/{id}or PATCH /projects/{id}with { "cta_inherit": false }. A video's own CTAs render on top of inherited ones.
CTA shape
{
"title": "Subscribe",
"url": "https://example.com",
"type": "link", // "link" or "email"
"show_at": 0, // seconds; when to appear
"hide_at": null, // seconds; null = until end
"position": "bottom-right", // top/bottom x left/center/right
"background": "#2764EB", // null = brand palette fallback
"text_color": "#ffffff"
}Full parameter reference (palette keys, email-capture fields) is in theAPI Referenceunder CTAs.
Webhooks
StreamVault allows you to set up webhooks to receive notifications for specific events such asvideoPublished.
Setting Up Webhooks
- Log in to your StreamVault account.
- Navigate to Dashboard → Settings → Webhooks.
- Set the event type (e.g.
videoPublished). - Provide the webhook URL where you want to receive notifications.
- Webhook configuration will be automatically saved.
Video Published Event
{
"event": "videoPublished",
"video": {
"Id": "YOUR_VIDEO_ID",
"Duration": "00:00:02.2200000",
"ProjectId": "YOUR_PROJECT_ID",
"AccountKey": "YOUR_ACCOUNT_KEY",
"Region": "use",
"Image": {
"xsUrl": "",
"smUrl": "",
"mdUrl": "",
"lgUrl": ""
},
"Captions": [],
"Key": "YOUR_VIDEO_KEY",
"ChannelKey": "YOUR_CHANNEL_KEY",
"PrivateLink": "YOUR_PRIVATE_LINK",
"IframeLink": "YOUR_IFRAME_LINK",
"HLSLink": "YOUR_HLS_LINK",
"PlanType": 1,
"Mp4Url": "",
"Mp4Urls": [],
"Formats": {
"Hls": true,
"Mp4": false,
"Options": { "SecureMp4": true }
},
"HLSUrl": "YOUR_HLS_URL",
"HLSUrlWeb": "YOUR_HLS_URL_WEB",
"Title": "YOUR_VIDEO_TITLE",
"Description": "YOUR_VIDEO_DESCRIPTION",
"Options": {},
"Tags": [],
"Version": 1,
"Status": 3,
"Created": "2024-01-15T10:30:00Z"
}
}SDKs & Libraries
Official client libraries to get you started quickly.
JavaScript
Node.js & browser SDK
Coming soonReact
React component library
Coming soonPython
Python SDK
Coming soonJavaScript Quick Example
import StreamVault from '@streamvault/sdk';
const sv = new StreamVault({ apiKey: 'YOUR_API_KEY' });
// List videos
const { videos } = await sv.videos.list();
// Upload via URL
const video = await sv.videos.upload({
url: 'https://example.com/video.mp4',
title: 'My Video'
});
console.log(video.iframeLink);React Embed Component
import { StreamVaultPlayer } from '@streamvault/react';
function App() {
return (
<StreamVaultPlayer
videoId="YOUR_VIDEO_ID"
autoplay={false}
controls
style={{ width: '100%', maxWidth: 800 }}
/>
);
}Embed via iframe
<!-- Embed StreamVault Player --> <iframe src="https://player.streamvault.one/embed/VIDEO_ID" width="640" height="360" frameborder="0" allow="encrypted-media" allowfullscreen> </iframe>
Need Help?
Can't find what you're looking for? Reach out to our support team or check the user documentation.