Vidiyo

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.com

Authentication

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.

ScopeGrants access to
content:readGET /v1/creator/content/*
content:writeUpload, update, delete content
schedule:readGET /v1/creator/schedule
schedule:writeCreate, move, delete schedule blocks
channel:readGET /v1/creator/channels/*
channel:writeCreate, update, publish channels
analytics:readGET /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..."
  }
}
StatusWhen
400Malformed request body or missing required parameter
401Missing or invalid bearer token
403Valid token but insufficient scope or wrong owner
404Resource not found or not owned by the caller
409Conflict — e.g. schedule overlap, duplicate key
422Validation failure — details.issues has field-level errors
429Rate limited — Retry-After header included

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.

MethodPathAuthDescription
GET/v1/creator/api-keysSessionList all non-revoked keys (with isExpired flag)
POST/v1/creator/api-keysSessionCreate a key. Token returned once — store it.
DELETE/v1/creator/api-keys/:idSession or keyRevoke 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.

MethodPathAuthDescription
POST/v1/creator/uploads/direct/initcontent:writeCreate content row + presigned PUT URL (15 min TTL, max 5 GB)
POST/v1/creator/uploads/direct/completecontent:writeVerify bytes landed, enqueue transcoding
POST/v1/creator/uploads/tus/initcontent:writeStart 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.

MethodPathAuthDescription
GET/v1/creator/contentcontent:readList content items (filterable by status, channel, title)
GET/v1/creator/content/:idcontent:readContent detail with renditions and captions
PATCH/v1/creator/content/:idcontent:writeUpdate title, description, channel, tags
DELETE/v1/creator/content/:idcontent:writeSoft-delete (status → deleted)
POST/v1/creator/content/:id/retrycontent:writeRe-enqueue transcoding for a failed item

List query parameters

ParamTypeDescription
statusstringFilter: uploading | pending_transcode | transcoding | ready | failed | blocked
channel_idUUIDFilter by channel
qstringTitle substring search
sortstringcreated_at | title | duration | views (default: created_at)
directionstringasc | desc (default: desc)
pageintPage number (default: 1)
per_pageintItems per page, max 100 (default: 20)

Content status lifecycle

uploading → pending_transcode → transcoding → [pending_moderation →] ready
                                           ↘ failed (retryable 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.

MethodPathAuthDescription
GET/v1/creator/scheduleschedule:readSchedule blocks + gaps for a channel + date window
POST/v1/creator/scheduleschedule:writeCreate a schedule block
PATCH/v1/creator/schedule/:idschedule:writeMove, resize, or swap content of a block
DELETE/v1/creator/schedule/:idschedule:writeRemove a block. Content item is not deleted.
POST/v1/creator/schedule/fillschedule:writeTrigger 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:00Z

POST 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

MethodPathAuthDescription
GET/v1/creator/channelschannel:readList channels owned by the caller
POST/v1/creator/channelschannel:writeCreate a channel
PATCH/v1/creator/channels/:idchannel:writeUpdate name, description, kind, category
POST/v1/creator/channels/:id/publishchannel:writePublish a draft channel
POST/v1/creator/channels/:id/unpublishchannel:writeTake a channel offline

Analytics — /v1/creator/analytics

MethodPathAuthDescription
GET/v1/creator/analytics/streamanalytics:readSSE: live KPIs (viewers, views, impressions) every 5s
GET/v1/creator/analytics/summaryanalytics:readDashboard summary: today vs last week, revenue, content count

Rate limits

Limits apply per user (authenticated) or per IP (anonymous). All limits use a sliding window. Responses include Retry-After when limited.

ScopePathWindowLimit
uploads/v1/creator/uploads/*1 min60
search/v1/search1 min60
api_key_create/v1/creator/api-keys60 min20
oauth_exchange/v1/auth/oauth/*60 min10
beacons/v1/beacons/*1 min600

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.