StreamVault API

Hosting de video programático. Sube, transcodifica, transmite y gestiona tu infraestructura de video a través de una API RESTful con entrega HLS adaptativa y funciones potenciadas por IA.

Comenzar →
REST API
JSON
HTTPS
HLS Adaptativo
Visión General
Introducción

La API de StreamVault te permite subir, gestionar y transmitir video a escala. Toda la comunicación es sobre HTTPS con cuerpos JSON. Los timestamps son enteros UNIX epoch (segundos).

URL Base
HLS Adaptativo
Auto-transcodificación 360p–4K ABR con cifrado AES-128 y token firmado.
Transcripción IA
Speech-to-text Whisper en 11 idiomas. Subtítulos VTT vía API.
Webhooks
Eventos firmados HMAC-SHA256: video.ready, transcription.complete y más.
Todas las respuestas devuelven Content-Type: application/json. Los errores siempre incluyen { "error": "mensaje" }.
Seguridad
Autenticación

Todos los endpoints autenticados requieren Authorization y X-Workspace-Id.

Bearer Token (JWT)
Authorization: Bearer eyJhbGci...
X-Workspace-Id: ws_abc123

Obtenlo vía POST /auth/login. Expira en 15 min. Renueva con POST /auth/refresh.

API Key Pro+
Authorization: Bearer sv_live_xxx...
X-Workspace-Id: ws_abc123

Las keys inician con sv_live_. Se muestran una sola vez al crearlas.

Ejemplo de petición autenticada
curl BASE_URL/api/videos \
  -H "Authorization: Bearer sv_live_TU_KEY" \
  -H "X-Workspace-Id: TU_WORKSPACE_ID"
Scopes de API Key
videos:readLeer, listar, tokens
videos:writeActualizar metadata
videos:deleteEliminar videos
uploads:writeSubir e importar
analytics:readReportes y export
webhooks:writeGestionar webhooks
folders:writeGestionar carpetas
Referencia
Errores y Límites

Códigos HTTP estándar. Todos los errores incluyen un campo error legible.

EstadoSignificado
200Éxito
201Recurso creado
202Async en cola
204Eliminado
400Petición inválida
401No autenticado
403Sin permisos / scope
404No encontrado
429Rate limit — ver Retry-After
500Error interno
Rate limits: 100 req/min general · 10 req/min upload · 5 req/min auth
Respuesta de error
// HTTP 404
{
  "error": "Video no encontrado",
  "status": 404
}
Health Check (sin auth)
Probar en vivo
Videos
Gestión de Videos
POST /api/upload

Sube un archivo de video. Se transcoda automáticamente a HLS multicalidad.

Scope: uploads:write
Parámetros (multipart/form-data)
file req
file
Video mp4/mov/avi/mkv/webm
title opt
string
Título (default: nombre del archivo)
description opt
string
Descripción
folder_id opt
string
ID de carpeta destino
visibility opt
string
public | private | unlisted
curl -X POST BASE_URL/api/upload \
  -H "Authorization: Bearer sv_live_KEY" \
  -H "X-Workspace-Id: WS_ID" \
  -F "file=@video.mp4" \
  -F "title=Demo" \
  -F "visibility=public"
const form = new FormData();
form.append('file', fileInput.files[0]);
form.append('title', 'Demo');

const res = await fetch('BASE_URL/api/upload', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer sv_live_KEY',
    'X-Workspace-Id': 'WS_ID'
  },
  body: form
});
const video = await res.json();
Respuesta 201
{
  "id": "abc123",
  "title": "Demo",
  "status": "processing",
  "m3u8Url": "https://..."
}
POST/api/import

Importa un video desde una URL externa.

Scope: uploads:write
Body JSON
url req
string
URL directa al archivo de video
title opt
string
Título
cURL
curl -X POST BASE_URL/api/import \
  -H "Authorization: Bearer sv_live_KEY" \
  -H "X-Workspace-Id: WS_ID" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://ejemplo.com/v.mp4"}'
Probar en vivo
GET/api/videos

Lista videos del workspace con paginación cursor.

Scope: videos:read
Query params
limit
int
1–100 (default 20)
cursor
string
Cursor de paginación
search
string
Filtrar por título
status
string
ready | processing | error
sort
string
newest | oldest | views
cURL
curl "BASE_URL/api/videos?limit=10&sort=newest" \
  -H "Authorization: Bearer sv_live_KEY" \
  -H "X-Workspace-Id: WS_ID"
Probar en vivo
GET/api/videos/:id

Obtiene el detalle completo de un video incluyendo URLs, embed config y ads.

Scope: videos:read
Probar en vivo
PATCH/api/videos/:id

Actualiza metadata del video.

Scope: videos:write
Body JSON (todos opcionales)
title
string
Nuevo título
description
string
Nueva descripción
visibility
string
public|private|unlisted|password
tags
string[]
Reemplaza array de tags
Probar en vivo
DELETE/api/videos/:id

Elimina el video y todos sus archivos S3 permanentemente. Retorna 204.

Scope: videos:delete
Probar en vivo
POST/api/videos/bulk

Operaciones masivas sobre hasta 200 videos.

Scope: videos:delete o videos:write
Body JSON
action req
string
delete | move | visibility
videoIds req
string[]
Hasta 200 IDs
folderId
string
Para acción move
visibility
string
Para acción visibility
Probar en vivo
Reproducción
Reproducción y Entrega
GET/api/videos/:id/token

Obtiene un token firmado para reproducir el HLS cifrado AES-128.

Scope: videos:read
Probar en vivo
POST/api/videos/:id/download-link

Genera URL firmada de descarga. TTL 10 min. Solo plan Pro+.

Scope: videos:read
POST/api/videos/:id/retry

Reintenta la transcodificación de un video con estado error.

Scope: videos:write
POST/api/videos/:id/views

Incrementa el contador de vistas. Sin auth. Throttle: 1 por IP/video cada 10s.

Probar — Vista
Progreso
Progreso de Reproducción

Rastrea la posición por usuario para experiencias "continuar viendo".

GET/api/videos/:id/progress

Obtiene la posición guardada. Respuesta: { "position": 65.4 }

POST/api/videos/:id/progress
Body
position req
number
Segundos
Probar — Guardar posición
Carpetas
Carpetas

Estructura jerárquica dentro del workspace. Los videos en carpetas eliminadas quedan sin carpeta.

GET/api/folders

Lista todas las carpetas del workspace.

POST/api/folders
Body
name req
string
Nombre
parent_id
string
Padre (null = raíz)
PATCH/api/folders/:id

Renombra o mueve la carpeta.

DELETE/api/folders/:id

Elimina la carpeta.

Probar — Listar carpetas
Probar — Crear carpeta
Playlists
Playlists

Colecciones ordenadas de videos. Las playlists públicas pueden embeberse.

GET/api/playlists
POST/api/playlists
Body
title req
string
Título
visibility
string
public | private
PATCH/api/playlists/:id
DELETE/api/playlists/:id
POST/api/playlists/:id/videos

Agrega un video. Body: {"videoId":"..."}. También: DELETE .../videos/:videoId y POST .../videos/reorder

Probar — Listar playlists
Probar — Crear playlist
Capítulos
Capítulos

Marcadores en la línea de tiempo del reproductor. Exportables como WebVTT.

GET/api/videos/:videoId/chapters

También: GET .../chapters/export.vtt

POST/api/videos/:videoId/chapters
Body
title req
string
start_time req
number
Segundos
end_time req
number
Segundos
PUT/api/videos/:videoId/chapters/:id
DELETE/api/videos/:videoId/chapters/:id
Probar — Listar capítulos
Pistas
Subtítulos y Pistas de Audio

Sube subtítulos VTT/SRT o pistas de audio alternas. Los SRT se convierten automáticamente a VTT.

GET/api/videos/:videoId/tracks
POST/api/videos/:videoId/tracks

Sube una pista (multipart/form-data).

Campos
file req
file
.vtt .srt .mp3 .aac
kind req
string
subtitles|captions|audio
language req
string
ISO 639-1 (en, es, fr...)
label
string
Etiqueta visible
Probar — Listar pistas
Transcripciones
Transcripciones IA

Speech-to-text con Whisper. Procesamiento asíncrono con notificación vía webhook al completar.

POST/api/videos/:videoId/transcriptions

Inicia una transcripción. Responde 202 con { "status": "queued" }.

Body
language req
string
en, es, fr, de, pt, it, ja, ko, zh, ru, ar
Suscríbete al webhook transcription.complete o haz polling al endpoint de listado.
GET/api/videos/:videoId/transcriptions
GET/api/videos/:videoId/transcriptions/search?q=...

Busca texto en los subtítulos. Responde con segmentos y timestamps.

GET/api/videos/:videoId/transcriptions/:lang/subtitles.vtt

Descarga el VTT. Query opcional: ?offset=2.5

Probar — Iniciar transcripción
Probar — Buscar en subtítulos
Analíticas
Analíticas

Reportes por video: vistas, retención, dispositivos, geografía y viewers en tiempo real.

GET/api/videos/:videoId/analytics

Reporte completo. Query: ?days=7|30|90

Scope: analytics:read
GET/api/videos/:videoId/analytics/live

Viewers activos en ventana de 5 min. Respuesta: { "liveViewers": 42 }

Scope: analytics:read
GET/api/videos/:videoId/analytics/export.csv

Exporta CSV. Rate limit: 5 req/min.

Probar — Reporte
Webhooks
Webhooks

Callbacks HTTP firmados HMAC-SHA256. Eventos: video.ready, video.failed, video.deleted, transcription.complete, *

GET/api/webhooks
POST/api/webhooks
Body
url req
string
Endpoint HTTPS
events req
string[]
Tipos de evento
Respuesta incluye secret (una vez). Verifica: X-Webhook-Signature: sha256=HMAC(secret,rawBody)
PATCH/api/webhooks/:id
DELETE/api/webhooks/:id
GET/api/webhooks/:id/deliveries

Últimos 50 intentos con códigos de estado y latencia.

Probar — Listar webhooks
Probar — Crear webhook
Workspaces
Workspaces

Gestión multi-tenant. Invita miembros y gestiona roles.

GET/api/workspaces
POST/api/workspaces
Body
name req
string
GET/api/workspaces/:id
PATCH/api/workspaces/:id

Actualiza nombre, settings, watermark, dominio embed custom.

DELETE/api/workspaces/:id
Elimina todos los videos, carpetas, playlists y configuraciones.
GET/api/workspaces/:id/members

También: POST .../invite, PATCH .../members/:userId, DELETE .../members/:userId

Probar — Listar workspaces
API Keys
Gestión de API Keys

Crea y revoca API keys con scopes. Requiere plan Pro+. Solo con JWT.

GET/api/apikeys

Lista claves (sin secrets).

POST/api/apikeys
Body
name req
string
scopes req
string[]
Permisos
La clave completa se muestra una sola vez al crearse. Guárdala inmediatamente.
DELETE/api/apikeys/:id

Revoca inmediatamente. Todas las peticiones con esa key devuelven 401.

Crear API Key
curl -X POST BASE_URL/api/apikeys \
  -H "Authorization: Bearer TU_JWT" \
  -H "X-Workspace-Id: WS_ID" \
  -H "Content-Type: application/json" \
  -d '{"name":"CI/CD","scopes":["uploads:write","videos:read"]}'
Probar — Listar keys
Probar — Crear key
Sistema
Estado del Sistema
GET/api/health

Health check sin autenticación. Retorna estado de DB, storage y cola.

Respuesta 200
{
  "status": "ok",
  "uptime": 86400,
  "database": "connected",
  "storage": "connected",
  "queue": "connected"
}
Probar en vivo
Guías
Rate Limits

Todos los endpoints están sujetos a límites de tasa. Los headers de respuesta indican el estado actual.

EndpointLímiteVentana
General100 req1 minuto
/api/upload10 req1 minuto
/auth/*5 req1 minuto
/api/videos/:id/views1 req / IP / video10 segundos
analytics/export.csv5 req1 minuto
Cuando se excede el límite se retorna 429 con header Retry-After: N indicando cuántos segundos esperar.
Headers de Rate Limit en la respuesta
HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1716000060

# Cuando se excede:
HTTP/1.1 429 Too Many Requests
Retry-After: 43
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
Manejo recomendado en código
async function fetchWithRetry(url, opts, retries = 3) {
  const res = await fetch(url, opts);

  if (res.status === 429 && retries > 0) {
    const wait = (res.headers.get('Retry-After') || 5) * 1000;
    await new Promise(r => setTimeout(r, wait));
    return fetchWithRetry(url, opts, retries - 1);
  }

  return res;
}
Guías
Payloads de Webhooks

Todos los webhooks se envían como POST con Content-Type: application/json y header de firma X-Webhook-Signature: sha256=HMAC(secret,rawBody).

Estructura común
Todos los eventos comparten la misma envoltura base:
"event": "video.ready"
"timestamp": 1716000000
"workspaceId": "ws_abc123"
"data": { ... objeto del evento ... }
Verifica siempre la firma antes de procesar. El cuerpo raw (sin parsear) es el que se usa para el HMAC.
{
  "event": "video.ready",
  "timestamp": 1716000000,
  "workspaceId": "ws_abc123",
  "data": {
    "id": "vid_xyz",
    "title": "Mi Video",
    "duration": 125.4,
    "qualities": ["360p", "720p", "1080p"],
    "m3u8Url": "https://cdn.../master.m3u8",
    "thumbnailUrl": "https://cdn.../thumb.jpg"
  }
}
{
  "event": "transcription.complete",
  "timestamp": 1716000060,
  "workspaceId": "ws_abc123",
  "data": {
    "videoId": "vid_xyz",
    "language": "es",
    "duration": 125.4,
    "segments": 42,
    "vttUrl": "https://.../subtitles.vtt"
  }
}
{
  "event": "video.failed",
  "timestamp": 1716000030,
  "workspaceId": "ws_abc123",
  "data": {
    "id": "vid_xyz",
    "title": "Mi Video",
    "error": "Codec no soportado"
  }
}
Verificación de firma (Node.js)
const crypto = require('crypto');

function verifyWebhook(rawBody, signature, secret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature), Buffer.from(expected)
  );
}
Guías
SDK / Librerías

No existe aún un SDK oficial, pero puedes usar este wrapper reutilizable directamente en tu proyecto.

Guarda este módulo en lib/streamvault.js e impórtalo en toda tu app.
// lib/streamvault.js
class StreamVault {
  constructor(apiKey, workspaceId, base = 'BASE_URL') {
    this.headers = {
      'Authorization': `Bearer ${apiKey}`,
      'X-Workspace-Id': workspaceId,
      'Content-Type': 'application/json'
    };
    this.base = base;
  }

  async req(method, path, body) {
    const res = await fetch(this.base + path, {
      method, headers: this.headers,
      body: body ? JSON.stringify(body) : undefined
    });
    if (!res.ok) throw await res.json();
    return res.status === 204 ? null : res.json();
  }

  listVideos(params = {}) {
    const q = new URLSearchParams(params).toString();
    return this.req('GET', `/api/videos?${q}`);
  }
  getVideo(id) { return this.req('GET', `/api/videos/${id}`); }
  updateVideo(id, data) { return this.req('PATCH', `/api/videos/${id}`, data); }
  deleteVideo(id) { return this.req('DELETE', `/api/videos/${id}`); }
  importVideo(url, title) { return this.req('POST', '/api/import', {url, title}); }
  listFolders() { return this.req('GET', '/api/folders'); }
  createWebhook(url, events) { return this.req('POST', '/api/webhooks', {url, events}); }
}

module.exports = StreamVault;

// Uso:
// const sv = new StreamVault('sv_live_KEY', 'ws_ID');
// const { videos } = await sv.listVideos({ limit: 10 });
# lib/streamvault.py
import requests

class StreamVault:
    def __init__(self, api_key, workspace_id,
                 base="BASE_URL"):
        self.base = base
        self.headers = {
            "Authorization": f"Bearer {api_key}",
            "X-Workspace-Id": workspace_id,
            "Content-Type": "application/json"
        }

    def req(self, method, path, body=None):
        r = requests.request(
            method, self.base + path,
            headers=self.headers, json=body
        )
        r.raise_for_status()
        return r.json() if r.text else None

    def list_videos(self, **kw):
        return self.req("GET", "/api/videos")
    def get_video(self, id):
        return self.req("GET", f"/api/videos/{id}")
    def delete_video(self, id):
        return self.req("DELETE", f"/api/videos/{id}")
    def import_video(self, url, title=None):
        return self.req("POST", "/api/import",
                        {"url": url, "title": title})

# sv = StreamVault("sv_live_KEY", "ws_ID")
# data = sv.list_videos()
Guías
Changelog

Historial de cambios de la API. Los cambios incompatibles siempre se anuncian con antelación.

v1.3
May 2026
Sistema de Facturación y Pagos
  • NEW Worker de expiración de planes automático
  • NEW Webhooks de facturación (PayPal, Stripe, DLocal)
  • NEW Idempotency keys para pagos
  • IMP Verificación de webhooks PayPal desde DB
v1.2
Abr 2026
DMCA, Seguridad y Multi-Gateway
  • NEW Soporte DMCA con suspensión automática
  • NEW Multi-gateway de pagos (Stripe + PayPal + Binance + DLocal)
  • NEW Rate limiting avanzado con fingerprinting
  • NEW Permisos jerárquicos por feature
v1.1
Feb 2026
Embed, Analytics y OTT
  • NEW Sistema de embed con dominio custom
  • NEW Analytics por tier: retención, heatmap, geo
  • NEW Player OTT con Chromecast y AirPlay
  • NEW API de pistas de audio y capítulos
v1.0
Ene 2026
Lanzamiento inicial
  • NEW Upload y transcodificación HLS adaptativa
  • NEW Transcripción IA con Whisper
  • NEW Workspaces multi-tenant
  • NEW API Keys con scopes granulares
  • NEW Webhooks HMAC-SHA256