Reference
REST API
Full HTTP/JSON API for channels, content, scheduling, uploads, and analytics. All responses use the standard error envelope and ISO 8601 timestamps.
Base URL
All paths are relative to:
https://api.vidiyo.comAuthentication
Pass your token in the Authorization header. For automation, use a creator API key. Generate one in Studio → Account → API Keys.
Authorization: Bearer vk_live_...Scopes
API keys are scoped. Requests from a key missing a required scope receive 403 forbidden. Session-authenticated callers (browser or TV) are not scope-restricted.
| Scope | Grants access to |
|---|---|
content:read | GET /v1/creator/content/* |
content:write | Upload, update, delete content |
schedule:read | GET /v1/creator/schedule |
schedule:write | Create, move, delete schedule blocks |
channel:read | GET /v1/creator/channels/* |
channel:write | Create, update, publish channels |
analytics:read | GET /v1/creator/analytics/* |
Errors
Every non-2xx response uses this envelope:
{
"error": {
"code": "schedule.overlap",
"message": "That time slot already has a block.",
"details": { "conflictingBlockId": "01936f4e-..." },
"requestId": "req_01HJ..."
}
}| Status | When |
|---|---|
| 400 | Malformed request body or missing required parameter |
| 401 | Missing or invalid bearer token |
| 403 | Valid token but insufficient scope or wrong owner |
| 404 | Resource not found or not owned by the caller |
| 409 | Conflict — e.g. schedule overlap, duplicate key |
| 422 | Validation failure — details.issues has field-level errors |
| 429 | Rate limited — Retry-After header included |
Pagination
List endpoints page by cursor. Send limit for the page size and pass the response's nextCursor back as cursor for the next page. nextCursor is null on the last page. A cursor belongs to one sort and filter set. Change either and start again without it.
GET /v1/creator/content?sort=title&limit=50
GET /v1/creator/content?sort=title&limit=50&cursor=eyJ2IjoiRXBpc29kZSA5IiwiaWQiOiIwMTkzLi4uIn0
{ "items": [...], "meta": { "limit": 50, "nextCursor": "eyJ2Ijo...", "has_more": true } }The older page and per_page parameters still work until 4 March 2027. Responses that use them carry Deprecation and Sunset headers. After that date they are ignored and every list answers in the cursor form.
API Keys — /v1/creator/api-keys
Create and revoke long-lived API keys for automated access. The plaintext token is returned once on creation. List and create operations require a session (browser or TV token). API keys cannot create other keys.
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /v1/creator/api-keys | Session | List all non-revoked keys (with isExpired flag) |
| POST | /v1/creator/api-keys | Session | Create a key. Token returned once. Store it. |
| DELETE | /v1/creator/api-keys/:id | Session or key | Revoke a key immediately. Idempotent. |
Create a key — request body
{
"name": "My video agent", // required, 1–100 chars
"scopes": ["content:write", "schedule:write"], // required, at least one
"expiresInDays": 90 // optional; omit for non-expiring
}Create a key — response
{
"key": {
"id": "01936f4e-...",
"name": "My video agent",
"keyPrefix": "vk_live_Ab1cD2", // first 16 chars, safe to display
"scopes": ["content:write", "schedule:write"],
"expiresAt": "2026-08-01T00:00:00.000Z",
"createdAt": "2026-05-03T09:00:00.000Z",
"token": "vk_live_Ab1cD2..." // full plaintext — shown ONCE
}
}Uploads — /v1/creator/uploads
Two-step direct-to-R2 upload. Init returns a presigned URL; your client PUTs the bytes directly to R2 (no Vidiyo server in the upload path), then calls complete to trigger transcoding.
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /v1/creator/uploads/direct/init | content:write | Create content row + presigned PUT URL (15 min TTL, max 5 GB) |
| POST | /v1/creator/uploads/direct/complete | content:write | Verify bytes landed, enqueue transcoding |
| POST | /v1/creator/uploads/tus/init | content:write | Start resumable upload (TUS, up to 50 GB) |
Init request body
{
"filename": "episode-1.mp4",
"title": "Episode 1",
"contentType": "video/mp4",
"sizeBytes": 52428800,
"channelId": "01936f4e-..." // optional — attach immediately
}Init response
{
"contentItemId": "01936f4e-...",
"objectKey": "uploads/.../episode-1.mp4",
"uploadUrl": "https://r2.vidiyo.com/...?X-Amz-Signature=...",
"expiresInSeconds": 900
}Content — /v1/creator/content
Manage content items in your library. Filter by status to find ready-to-schedule videos.
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /v1/creator/content | content:read | List content items (filterable by status, channel, title) |
| GET | /v1/creator/content/:id | content:read | Content detail with renditions and captions |
| PATCH | /v1/creator/content/:id | content:write | Update title, description, channel, tags |
| DELETE | /v1/creator/content/:id | content:write | Soft-delete (status → deleted) |
| POST | /v1/creator/content/:id/retry | content:write | Cancel the current encode if needed and re-enqueue transcoding |
List query parameters
| Param | Type | Description |
|---|---|---|
status | string | Filter: uploading | pending_transcode | transcoding | ready | failed | blocked |
channel_id | UUID | Filter by channel |
q | string | Title substring search |
sort | string | created_at | title | duration | views (default: created_at) |
direction | string | asc | desc (default: desc) |
limit | int | Page size, max 100 (default: 20) |
cursor | string | nextCursor from the previous page |
page, per_page | int | Deprecated offset paging, removed 2027-03-04 |
Content status lifecycle
uploading → pending_transcode → transcoding → [pending_moderation →] ready
↘ failed (retry / cancel-and-retry via POST /:id/retry)Only ready items can be placed on a schedule. Poll until the status field equals ready.
Schedule — /v1/creator/schedule
Manage the EPG (electronic programme guide) for your channels. Blocks are non-overlapping; the API returns 409 with conflictingBlockId if you try to place a block in an occupied slot.
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /v1/creator/schedule | schedule:read | Schedule blocks + gaps for a channel + date window |
| POST | /v1/creator/schedule | schedule:write | Create a schedule block |
| PATCH | /v1/creator/schedule/:id | schedule:write | Move, resize, or swap content of a block |
| DELETE | /v1/creator/schedule/:id | schedule:write | Remove a block. Content item is not deleted. |
| POST | /v1/creator/schedule/fill | schedule:write | Trigger async auto-fill to cover gaps with library content |
GET parameters
GET /v1/creator/schedule?channelId=UUID&from=2026-05-03T00:00:00Z&to=2026-05-04T00:00:00ZPOST body (create block)
{
"channelId": "01936f4e-...",
"contentItemId": "01936f4e-...", // must have status "ready"
"startsAt": "2026-05-03T14:00:00Z",
"kind": "content",
"durationSeconds": 1800 // optional; defaults to content's transcoded duration
}GET response
{
"channelId": "01936f4e-...",
"from": "2026-05-03T00:00:00.000Z",
"to": "2026-05-04T00:00:00.000Z",
"blocks": [
{
"id": "01936f4e-...",
"startsAt": "2026-05-03T14:00:00.000Z",
"endsAt": "2026-05-03T14:30:00.000Z",
"durationSeconds": 1800,
"kind": "content",
"content": { "id": "...", "title": "Episode 1", "status": "ready", ... }
}
],
"gaps": [
{ "from": "2026-05-03T00:00:00.000Z", "to": "2026-05-03T14:00:00.000Z" }
],
"total": 1
}Channels — /v1/creator/channels
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /v1/creator/channels | channel:read | List channels owned by the caller |
| POST | /v1/creator/channels | channel:write | Create a channel |
| PATCH | /v1/creator/channels/:id | channel:write | Update name, description, kind, category |
| POST | /v1/creator/channels/:id/publish | channel:write | Publish a draft channel |
| POST | /v1/creator/channels/:id/unpublish | channel:write | Take a channel offline |
Analytics — /v1/creator/analytics
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /v1/creator/analytics/stream | analytics:read | SSE: live KPIs (viewers, views, impressions) every 5s |
| GET | /v1/creator/analytics/summary | analytics:read | Dashboard summary: today vs last week, revenue, content count |
Brand Kits / Your brand: /v1/brands
Branded viewer surfaces scoped to a channel allowlist. Pass X-Vidiyo-Brand or ?brand= on discovery endpoints to scope the catalog. Product overview: Your brand. MCP tools: Brand Kit MCP.
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /v1/brands/:slug | None | Public Brand Kit DTO (active kits only) |
| GET | /v1/brands/resolve?host= | None | Resolve kit from hostname; pending custom hosts 404 |
| GET | /v1/creator/brands | Session / API key | Manage DTO + previewHost + entitlement |
| POST | /v1/creator/brands | Session / API key | Create a draft Brand Kit |
| PATCH | /v1/creator/brands | Session / API key | Update channels, chrome, legal URLs, exclusive |
| POST | /v1/creator/brands/publish | Session / API key | Publish draft → active when entitled |
| GET | /v1/creator/brands/domains | Session / API key | List custom domains + CNAME target |
| POST | /v1/creator/brands/domains | Session / API key | Attach a custom hostname (Cloudflare for SaaS) |
Rate limits
Limits apply per user (authenticated) or per IP (anonymous). All limits use a sliding window. Responses include Retry-After when limited.
| Scope | Path | Window | Limit |
|---|---|---|---|
uploads | /v1/creator/uploads/* | 1 min | 60 |
search | /v1/search | 1 min | 60 |
api_key_create | /v1/creator/api-keys | 60 min | 20 |
oauth_exchange | /v1/auth/oauth/* | 60 min | 10 |
beacons | /v1/beacons/* | 1 min | 600 |
Need the full spec?
The authoritative machine-readable spec is the OpenAPI 3.1 document. Browse it interactively or download it to generate client SDKs.