API Reference

REST API

Base URL https://api.ollanode.com. JSON in, JSON out. Every resource is scoped to your project by the credential you present.

Authentication

Send a bearer token on every authenticated call โ€” either a session token from /v1/auth/login (12h) or an API key (vbk_โ€ฆ) from Settings โ†’ API Keys.

Authorization: Bearer <session-token-or-api-key>
# or
x-api-key: vbk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Scopes โ€” read (GET), write (create/modify), admin (keys, team, audit). A 401 means a missing/invalid credential; a 403 means the credential lacks the required scope.

Authentication

Exchange credentials for a session token, or manage your own account. Session tokens last 12h; use them (or an API key) as a bearer on every other call.

POST /v1/auth/signup public

Self-serve sign up. Creates a user + a project and sends a verification email.

Body { "email", "username", "password", "full_name"?, "project_name"? }

Returns { token, expires_at, user }

POST /v1/auth/login public

Exchange username + password for a session token.

Body { "username", "password" }

Returns { token, expires_at, user }

GET /v1/auth/me bearer

The authenticated user.

Returns UserView

POST /v1/auth/forgot-password public

Email a password-reset link. Always returns 200 (no account enumeration).

Body { "email" }

POST /v1/auth/reset-password public

Set a new password using the emailed token.

Body { "token", "new_password" }

POST /v1/auth/verify-email public

Confirm an email address with the emailed token.

Body { "token" }

POST /v1/auth/resend-verification bearer

Re-send the verification email.

POST /v1/auth/change-password bearer

Change your password.

Body { "current_password", "new_password" }

GET /v1/account bearer

Your account profile.

PATCH /v1/account bearer

Update your profile.

Body { "full_name"? }

API keys

Machine credentials for server-to-server use. The full key (vbk_โ€ฆ) is shown once at creation โ€” store it securely. Scopes: read, write, admin.

POST /v1/api-keys admin

Create a key. Response includes the secret once.

Body { "name", "scopes": ["read","write","admin"] }

Returns { id, name, prefix, scopes, key }

GET /v1/api-keys admin

List keys (no secrets).

DELETE /v1/api-keys/:id admin

Revoke a key immediately.

Videos

Create a video record, attach a source (upload or remote URL), then poll status until "ready". Statuses: created โ†’ uploading โ†’ processing โ†’ ready (or failed).

POST /v1/videos write

Create a video. Pass source_url to ingest from the web automatically, or upload next.

Body { "title"?, "source_url"?, "playback_policy"? ("signed"|"public"), "max_height"?, "quality_preset"? ("low"|"standard"|"high"), "metadata"? }

Returns VideoView

GET /v1/videos read

List videos (newest first). Query: ?limit, ?status.

GET /v1/videos/:id read

Full video record.

PATCH /v1/videos/:id write

Update title, playback_policy, or metadata.

Body { "title"?, "playback_policy"?, "metadata"? }

DELETE /v1/videos/:id write

Delete the video and all its assets.

GET /v1/videos/:id/status read

Lightweight status poll: { id, status, error_reason? }.

GET /v1/videos/:id/assets read

Rendition ladder + asset metadata (heights, bitrates, sizes).

POST /v1/videos/:id/clip write

Create a sub-clip as a new video.

Body { "start", "end", "title"? }

PUT /v1/videos/:id/chapters write

Set chapter markers.

Body { "chapters": [{ "start_seconds", "title" }] }

GET /v1/videos/:id/views read

View counts / play analytics for the video.

GET /v1/videos/:id/download read

Presigned URL to download the original/source.

Upload

Direct-to-storage uploads. Request a presigned URL, PUT the bytes straight to storage (no proxy), then mark the upload complete to start processing.

POST /v1/videos/:id/upload-url write

Mint a single-shot presigned PUT URL.

Body { "content_type"? }

Returns { url, source_key, expires_in }

POST /v1/videos/:id/multipart write

Begin a multipart upload for large files.

Body { "content_type"? }

POST /v1/videos/:id/upload-complete write

Signal bytes are uploaded; transcoding begins.

Body { "source_key"?, "size_bytes"? }

Playback & media

When status is "ready", request a playback URL. Signed videos return a short-lived token; public videos do not. Or drop in the hosted <iframe> player.

GET /v1/videos/:id/playback read

HLS master URL (+ token for signed), poster, subtitles.

Returns { master_url, token?, policy, expires_at, poster_url?, subtitles[] }

GET /v1/videos/:id/thumbnails read

Generated thumbnails (presigned URLs).

GET /v1/videos/:id/thumbnail?t=SECONDS read

Thumbnail nearest a timestamp.

GET /v1/videos/:id/transcript read

Transcript status + VTT/SRT URLs + segments.

POST /v1/videos/:id/subtitles write

Add a subtitle track (WebVTT).

Body { "language", "label"?, "content" }

GET /v1/videos/:id/subtitles read

List subtitle tracks.

DELETE /v1/videos/:id/subtitles/:lang write

Remove a subtitle track.

GET /embed/:id?token=โ€ฆ public

Hosted HLS player (iframe-embeddable). Token required for signed videos.

CDN pull zones

Map a hostname to an origin (or a storage zone) and serve it cached from the edge. Supports hotlink-protection (signed URLs), CORS, granular purge, traffic analytics, and edge rules.

POST /v1/zones write

Create a pull zone. Use origin_url OR storage_zone_id.

Body { "hostname", "origin_url"?, "storage_zone_id"?, "default_ttl_secs"?, "cache_bypass"?[], "signed"?, "cors"? }

Returns ZoneView (token_secret shown once if signed)

GET /v1/zones read

List zones.

GET /v1/zones/:id read

Zone detail incl. edge rules.

PATCH /v1/zones/:id write

Update origin, TTL, cache-bypass, CORS, active, signing, or rules.

Body { "origin_url"?, "default_ttl_secs"?, "cache_bypass"?, "cors"?, "active"?, "signed"?, "rules"? }

DELETE /v1/zones/:id write

Delete the zone.

POST /v1/zones/:id/purge write

Purge cache. Empty body = full purge; pass paths for granular.

Body { "paths"?: ["/file.m3u8"] }

POST /v1/zones/:id/rotate-token write

Rotate the hotlink signing secret (returned once).

GET /v1/zones/:id/analytics read

Live edge traffic: requests, egress bytes, hit ratio.

Edge rules

Declarative, ordered rules applied at the edge per zone. Set via PATCH /v1/zones/:id with a "rules" array. A matching block/redirect short-circuits; set_header is added to the response.

โ€” rule: set_header

{ "prefix"?: "/path", "action": { "type": "set_header", "name", "value" } }

โ€” rule: redirect

{ "prefix"?: "/old", "action": { "type": "redirect", "status": 301|302|307|308, "location" } }

โ€” rule: block

{ "prefix"?: "/private", "action": { "type": "block" } } โ†’ 403

Storage zones

Object buckets for static files. Upload via presigned PUT, list/delete files, and optionally front a bucket with a CDN pull zone (storage_zone_id on the zone).

POST /v1/storage-zones write

Create a storage zone.

Body { "name" }

GET /v1/storage-zones read

List storage zones.

GET /v1/storage-zones/:id read

Detail incl. bytes_used + file_count.

DELETE /v1/storage-zones/:id write

Delete the zone and its files.

POST /v1/storage-zones/:id/upload-url write

Presigned PUT for a file path.

Body { "path", "content_type"? }

Returns { url, path }

GET /v1/storage-zones/:id/files read

List files (presigned GET URLs).

DELETE /v1/storage-zones/:id/files?path=โ€ฆ write

Delete a file by path.

Edge functions

Deploy JavaScript/TypeScript that runs at the edge in a V8 isolate (Deno.serve). Invoke at /__fn/<id> on the CDN host. Deploys propagate in seconds.

POST /v1/functions write

Create + deploy a function.

Body { "name", "code" }

Returns FunctionView

GET /v1/functions read

List functions.

GET /v1/functions/:id read

Get a function (incl. code).

PATCH /v1/functions/:id write

Update code / name / active. Re-deploys.

Body { "name"?, "code"?, "active"? }

DELETE /v1/functions/:id write

Delete + undeploy.

GET /__fn/:id public

Invoke the function (any method/path/body passed through).

Webhooks

Subscribe to events. Deliveries are HMAC-signed (X-Signature) with the per-webhook secret (shown once) and retried with backoff. Events: video.asset.ready, video.asset.errored, video.asset.processing, video.asset.created, video.asset.thumbnail.ready, video.asset.track.ready, transcript.ready.

POST /v1/webhooks write

Create a subscription.

Body { "url", "events": ["video.asset.ready"] }

Returns { id, url, events, secret }

GET /v1/webhooks read

List subscriptions.

GET /v1/webhooks/:id/deliveries read

Recent delivery attempts + statuses.

Analytics & usage

Account-wide rollups for dashboards and billing.

GET /v1/analytics read

Storage, video count, total views, by-status, per-day, top videos.

GET /v1/usage read

Current storage_bytes + video_count.

Team & audit

Manage teammates and review the audit trail. Requires the admin scope.

POST /v1/users admin

Add a teammate.

Body { "username", "password", "email"?, "full_name"?, "scopes": [] }

GET /v1/users admin

List teammates.

PATCH /v1/users/:id admin

Update scopes or disable.

Body { "scopes"?, "disabled"? }

DELETE /v1/users/:id admin

Remove a teammate.

POST /v1/users/:id/reset-password admin

Set a teammate password.

Body { "new_password" }

GET /v1/audit admin

Audit log (?limit). Every mutation is recorded.

End-to-end: upload & play

# auth
TOKEN=$(curl -s -X POST https://api.ollanode.com/v1/auth/login \
  -H 'content-type: application/json' \
  -d '{"username":"you","password":"โ€ขโ€ขโ€ขโ€ข"}' | jq -r .token)

# 1) create a video
VID=$(curl -s -X POST https://api.ollanode.com/v1/videos \
  -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{"title":"launch.mp4","playback_policy":"signed"}' | jq -r .id)

# 2) presigned upload, then PUT the bytes
URL=$(curl -s -X POST https://api.ollanode.com/v1/videos/$VID/upload-url \
  -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{"content_type":"video/mp4"}' | jq -r .url)
curl -X PUT "$URL" -H 'content-type: video/mp4' --data-binary @launch.mp4

# 3) start processing
curl -s -X POST https://api.ollanode.com/v1/videos/$VID/upload-complete -H "authorization: Bearer $TOKEN"

# 4) poll until ready
curl -s https://api.ollanode.com/v1/videos/$VID/status -H "authorization: Bearer $TOKEN"

# 5) get a playback URL (signed)
curl -s https://api.ollanode.com/v1/videos/$VID/playback -H "authorization: Bearer $TOKEN"
# โ†’ { "master_url": "โ€ฆm3u8", "token": "โ€ฆ", "subtitles": [...] }