# Operations Videos — Master Implementation Guide (LLM-Ready) > Hospedagem e player de vídeo para a página de quem vende ou ensina. A barra > é sempre o tempo real; o produto mede quanto de cada vídeo foi assistido e, > **entre as pessoas que se identificaram**, quem assistiu e até onde — e > manda isso por webhook assinado para onde a conta quiser. > > Este arquivo é a documentação para agentes. Cole a URL no seu agente > (Claude Code, Cursor, Windsurf, Codex) e peça a integração. **Leia a seção 9 > antes de prometer qualquer coisa**: ela diz o que o produto não faz. Worker (API, player, MCP): https://videos.worker.myoperations.click Painel: https://videos.dashboard.myoperations.click Servidor MCP: https://videos.worker.myoperations.click/mcp — ver a seção 8. O mapa da linha (Analytics, Videos, login): https://myoperations.click/llms.txt ## 0. Estado e credenciais — leia antes de tudo - **No ar desde 02/10/2026.** `GET https://videos.worker.myoperations.click/` responde `{"service":"operations-videos",…}`. - **Login:** o Auth da linha (`https://auth.worker.myoperations.click`, aplicação Operations), com código por e-mail. É o mesmo login do Analytics: mesmo login, autorização por produto. Passo humano obrigatório: a pessoa lê um código de 8 caracteres no e-mail e o informa. Não tente contornar. - **Chave da conta:** `vk_…`, criada no painel em **Integrações e eventos**. Vale para a API (`Authorization: Bearer vk_…`) e para assinar a identidade de quem está logado na página (seção 3). **Fica só no servidor**: nunca no HTML, nunca num repositório. - **MCP:** OAuth, com o mesmo login (seção 8). Não usa a `vk_`. - **Preço:** não há preço público e nada é cobrado. Não cite valor, plano, franquia em minutos nem "grátis". Se perguntarem: "Ainda não há planos, e nada é cobrado." Sem credencial de verdade, **pare e peça**. Não gere código com `vk_xxx` de placeholder e diga que terminou. ## 1. Modelo mental - **Vídeo** — tem `id` (UUID), `code` público (`v_XXXX`), título e tipo (`vsl`, `demo`, `lesson`, `other`). `vsl` e `demo` começam mudos e sozinhos; `lesson` espera o play e retoma de onde parou. - **Documento do vídeo** (`videos.config/1`) — capítulos, player, ações e domínios permitidos num documento só, com versões. O painel (ou o agente) edita um **rascunho**; **publicar** cria a versão seguinte e nunca sobrescreve; uma versão antiga reabre como rascunho. Publicar por cima de outra aba responde conflito. - **Sessão** — quem abriu o player. Só ganha dono por quatro portas (seção 3). - **Minutos, não plays** — três medidas separadas: - **entregue**: todo tempo tocado, com ou sem som; - **assistido**: só depois do clique para ouvir; salto não conta; - **com dono**: assistido por pessoa identificada. O produto não conta plays. ## 2. Enviar e incorporar **Enviar:** - Do computador: envio retomável (tus), direto do navegador ao fornecedor; MP4, MOV, MKV ou WebM até 20 GB. - Por link: um `https://` para o arquivo. Links de YouTube, Vimeo, Instagram, TikTok e Facebook são recusados no servidor. - Pela API ou pelo MCP: a criação devolve um link de envio tus válido por 1 hora. **O arquivo nunca passa pelo chat.** **Gravar (Clips):** pelo painel, sem instalar nada (tela inteira, uma janela, uma aba ou só a câmera, com microfone), ou pelo Clips para Mac (macOS 14 ou mais novo, Apple Silicon; ainda sem a assinatura da Apple). As partes sobem enquanto grava, e o link existe quando a gravação para; o webhook `video.recorded` avisa. ``` POST https://videos.worker.myoperations.click/api/videos Authorization: Bearer vk_… { "title": "Demo v4", "kind": "demo" } → 201 { "video": { "id": "…", "code": "v_8KQ2", "status": "uploading", … }, "upload": { "protocol": "tus", "url": "…", "headers": { … }, "expiresIn": 3600 } } ``` O vídeo toca quando a primeira qualidade fica pronta, e as outras entram sozinhas; o webhook `video.ready` avisa. Substituir o arquivo (`POST /api/videos/:id/replace`) mantém código, link e dados. **Incorporar** — um script onde o vídeo deve aparecer. O código aponta para o vídeo, não para uma versão: publicar de novo não exige colar nada. ```html ``` O player é um Web Component com Shadow DOM, em JavaScript puro: não herda nem quebra o CSS da página. Autoplay mudo com "Clique para ouvir" (volta a 0:00 com som), retomada guardada no navegador de quem assiste, velocidade, tela cheia e atalhos de teclado. A barra é sempre o tempo real (`progress_bar: "real_time"` é constante no esquema). Com `player.captions` ligado e a transcrição pronta, o player traz legendas geradas da transcrição. **Só toca nos domínios permitidos da conta** (`access.allowed_domains`): eles são conferidos antes de o endereço do streaming ser assinado, e a assinatura vale 6 horas. Fora deles, o player responde "Este vídeo não toca neste endereço." **Página do link** (`https://videos.worker.myoperations.click/w/`): todo vídeo pode ter uma página própria, ligada no painel em Compartilhar (`PATCH /api/videos/:id/link`). Acesso: qualquer pessoa, e-mail antes, só pessoas da conta ou link pessoal; senha, validade, download (só quando `access.download` permite), transcrição e comentários. Um link que não é aberto a qualquer pessoa não mostra o título na prévia de chat. ## 3. Quem assistiu — as quatro portas Uma sessão só ganha dono por uma destas portas: | Porta | Como | `identifiedBy` | |---|---|---| | Link com identidade | `?vw=` na URL da página: código opaco, com validade (7, 30 ou 90 dias), gerado por lista de e-mails; só o hash fica guardado; vencido, abre anônimo | `identity_link` | | E-mail no player | a ação `email_gate` (sempre pulável) e a pessoa responde | `email_gate` | | Login provado pelo servidor da página | `data-viewer` com um token assinado pela `vk_` ou com o JWT da aplicação no Auth | `signed_login` | | Compra informada pela API | `POST /api/conversions` com a sessão que o botão levou ao checkout | — | **`data-email` sem assinatura é ignorado**: qualquer um escreveria. O token de `data-viewer` é `v1.." com a vk_>`. Em Node: ```js import { createHmac } from 'node:crypto' const key = process.env.VIDEOS_KEY // vk_…, só no servidor const b64 = text => Buffer.from(text).toString('base64url') export function viewerToken({ email, name }) { const payload = b64(JSON.stringify({ email, name, exp: Math.floor(Date.now() / 1000) + 3600 })) return `v1.${payload}.${createHmac('sha256', key).update(`v1.${payload}`).digest('base64url')}` } ``` **Anônimo continua anônimo:** sem fingerprint, sem IP gravado. Identificar liga a sessão daquele momento em diante; visitas anônimas antigas não são ligadas a quem se identificou depois. Visitante frio de anúncio vira contagem. ## 4. Medir - **Retenção** (`GET /api/videos/:id/retention?days=7|30`): de quem escolheu assistir (com som), quantos ainda estavam lá a cada trecho, para todo o público e só para as pessoas conhecidas; maior queda (trecho e capítulo), quanto se assiste em média, quantos chegaram à metade, clicaram no botão e terminaram. Marcos de 25, 50, 75 e 100%. - **Quem assistiu** (`GET /api/viewers`, `GET /api/videos/:id/viewers`): só pessoas identificadas, com `filter=all|cta|half_no_cta` ("passaram da metade e não clicaram"). - **Apagar uma pessoa** (`DELETE /api/viewers/:id`) apaga também as sessões dela. - **Interações** (`GET /api/videos/:id/interactions?days=7|30`): quantos interagiram, comentários (sem resposta, esperando, tempo até a resposta), reações por tipo, o mapa por trecho de 5 s e o resultado de cada elemento. - **Transcrição** (`GET /api/videos/:id/transcript`): o estado, o idioma, o resumo, os capítulos sugeridos e as frases com o segundo. A fala é transcrita pelo Workers AI; o resumo e os capítulos, por um modelo de linguagem. **Capítulo sugerido nunca entra no vídeo sem uma pessoa.** ## 5. O documento `videos.config/1` ``` player autoplay (muted|off) · resume · speeds · accent (#RRGGBB) poster_at_s · progress_bar ("real_time", constante) chapters [{ id, start_s, title }] o primeiro em 0; até 50 actions [{ id, trigger, effect, audience, once_per_session: true }] até 20 captions (legendas da transcrição; ausente = desligado) access allowed_domains (até 20) · signed_urls · download interaction enabled · comments · reactions · who (anyone|named|account) publish (now|after_approval) · markers (everyone|owner) opcional elements [{ id, type, trigger, audience, … }] até 30; type: question · scale · spot · stretch_reactions opcional edit { start_s, end_s|null, cuts: [{ from_s, to_s }] } ou null opcional ``` `interaction`, `elements` e `edit` são opcionais: um documento sem eles toca como antes, sem camada de interação. O corte (`edit`) é virtual: o arquivo não muda, e todo tempo do documento continua no tempo do arquivo. - `trigger.on`: `time` (`at_s`) · `chapter_start` (`chapter`) · `progress` (`percent`: 10, 25, 50, 75 ou 90) · `first_pause` · `end`. - `effect.type`: `button` (`text`, `url`, `placement`: `below_video` | `over_video`) · `reveal` (`selector`) · `card` (`title`, `body`, `link`) · `email_gate` (`prompt`, `skippable: true`) · `emit` (`event: "video.reached"`, `name`). - `audience`: `all` · `known` · `anon`. Toda ação dispara uma vez por sessão. - Referência sempre por `id`, nunca por posição. **Verificações antes de publicar:** capítulo em 0:00, capítulo de 10 s ou mais, ação apontando para capítulo que existe, link `https://`, domínio válido. Com erro, o worker recusa publicar. **Mostrar um bloco no minuto certo:** marque a seção com a classe `videos-oculto`; o player esconde, e uma ação `reveal` tira a classe no momento escolhido. ```html
O preço e o botão de compra.
``` ## 6. Rotas Base: `https://videos.worker.myoperations.click`, com `Authorization: Bearer vk_…` (ou a sessão do painel). Coleções respondem `{items, page}`; erros, `{error: {code, message}}`. ``` GET /api/videos os vídeos, com os números de 30 dias POST /api/videos cria um vídeo e devolve o envio (tus, 1 hora) POST /api/videos/import cria por link https:// para o arquivo GET /api/videos/:id o vídeo, a versão publicada e o rascunho PATCH /api/videos/:id título e tipo (fora do rascunho) DELETE /api/videos/:id apaga o vídeo — só com pedido explícito de uma pessoa PUT /api/videos/:id/draft salva o rascunho do videos.config/1 POST /api/videos/:id/publish publica o rascunho GET /api/videos/:id/versions as versões POST /api/videos/:id/versions/:n/reopen reabre uma versão como rascunho POST /api/videos/:id/replace substitui o arquivo, mantendo código e dados POST /api/videos/:id/thumbnail miniatura (JPEG) GET /api/videos/:id/retention?days=30 GET /api/videos/:id/viewers quem assistiu, só pessoas conhecidas PATCH /api/videos/:id/link a página do link: acesso, senha, validade GET /api/videos/:id/transcript a transcrição, o resumo e os capítulos sugeridos GET /api/videos/:id/comments os comentários do vídeo, em fios GET /api/comments?status=unanswered os da conta, sem resposta (ou pending) POST /api/comments/:id/approve aprova um comentário (ou /reject) GET /api/pending-actions respostas de agente esperando uma pessoa GET /api/videos/:id/interactions as interações do vídeo, com as métricas GET /api/viewers?filter=half_no_cta passaram da metade e não clicaram DELETE /api/viewers/:id apaga a pessoa e as sessões dela POST /api/identity-links um link com identidade por e-mail POST /api/conversions uma venda liga a sessão (vs=) a quem comprou GET /api/webhooks os endpoints e os eventos que cada um assina POST /api/webhooks { url, description?, events?, secret? } PATCH /api/webhooks/:id { url?, description?, events?, isActive? } POST /api/webhooks/:id/secret troca o segredo (gerado, ou o seu) POST /api/webhooks/:id/test um evento de teste assinado, agora DELETE /api/webhooks/:id GET /api/deliveries?endpointId= as últimas entregas POST /api/deliveries/:id/retry reenvia uma entrega que falhou GET /api/keys · POST /api/keys a chave vk_ da conta GET /api/plan o uso do mês ``` **Vendas:** o botão leva `vs=` com a sessão no link. Qualquer checkout guarda esse valor e, quando a venda confirmar, o seu servidor chama: ``` POST /api/conversions Authorization: Bearer vk_… { "session": "s_Q2x8Rk91LmFb", "email": "…", "name": "…", "externalId": "pedido-1042", "value": 197, "currency": "BRL" } → 200 { "session": "s_Q2x8Rk91LmFb", "video": "v_7PX4", "becameKnown": true, "converted": true } ``` Nenhuma plataforma de venda tem integração própria; todas usam esta chamada. ## 7. Webhooks Até 20 endpoints por conta, cada um com os eventos que assina (sem `events`, assina todos). Padrão **Standard Webhooks** com o prefixo `x-ccm`: ``` x-ccm-id: s_Q2x8Rk91LmFb.video.watched.75 x-ccm-timestamp: 1790265731 x-ccm-signature: v1, ``` A chave do HMAC é o base64 depois de `whsec_`. Recuse timestamp com mais de 5 minutos e ignore um `x-ccm-id` já visto. O segredo é gerado (mostrado uma vez) ou o seu — uma fonte do People já traz o dela. Corpo: `{ type, timestamp, data: { person, channel, properties } }`. Evento de pessoa só existe com pessoa conhecida. | Evento | Quando | |---|---| | `video.watched` | a pessoa passou de 25, 50, 75 e 100% — um evento por marco | | `video.cta_clicked` | clicou no botão do vídeo, com a UTM da página | | `video.reached` | uma ação `emit` disparou, com o nome e o minuto | | `video.converted` | uma compra chegou por `/api/conversions` | | `person.identified` | a sessão ganhou dono | | `video.ready` | a conversão terminou e o vídeo toca (sem pessoa) | | `video.recorded` | uma gravação do Clips terminou | | `video.commented` | um comentário no segundo do vídeo | | `video.comment_approved` | um comentário que esperava moderação foi aprovado | | `video.reacted` | uma reação no segundo do vídeo | | `video.interacted` | uma resposta a um elemento (pergunta, escala, ponto) | | `video.updated` | o vídeo mudou (título, link, publicação…) | Os eventos de 09/10/2026 em diante (`video.recorded` a `video.updated`) são sobre o vídeo: saem sempre, com `data.person` quando a pessoa é conhecida. Se o destino falhar: nova tentativa em 1 min, 5 min, 30 min, 2 h e 12 h. **Para a ficha no People** (Customers): o People é um endpoint como qualquer outro — a URL e o segredo de uma fonte criada no People, assinando só os eventos com pessoa. Não é automático. ## 8. Servidor MCP - **Endereço:** `https://videos.worker.myoperations.click/mcp` (Streamable HTTP), para a conta inteira — a conta vem do token, nunca de um argumento. - **OAuth 2.1:** metadados em `https://videos.worker.myoperations.click/.well-known/oauth-protected-resource`. O token só vale para este servidor (audiência conferida). Escopos: `auth:read` lista as oito de leitura; `auth:write` acrescenta as quatro de escrita. - **Limite:** 120 chamadas por minuto por conta. | Tool | Escopo | O que faz | |---|---|---| | `videos_list` | leitura | os vídeos, com os números de 30 dias; filtra por tipo ou título | | `videos_get_retention` | leitura | a curva de retenção (7 ou 30 dias), maior queda, metade, botão, fim | | `videos_list_viewers` | leitura | as pessoas conhecidas dos últimos 90 dias; `filter=half_no_cta` | | `videos_usage` | leitura | o uso do mês (minutos entregues, guardados, vídeos) | | `videos_create_upload` | escrita | cria um vídeo e devolve o link de envio tus (1 hora) | | `videos_update_player` | escrita | altera **só o rascunho** e devolve as verificações | | `videos_identity_links` | escrita | gera um link com identidade por e-mail | | `videos_get_transcript` | leitura | a transcrição: estado, resumo, capítulos sugeridos e frases com o segundo; `format=text` | | `videos_list_comments` | leitura | os comentários em fios, de um vídeo ou da conta; `status` sem resposta ou esperando | | `videos_interaction_stats` | leitura | as interações de um vídeo: reações, comentários, trechos e elementos | | `videos_list_interactions` | leitura | cada comentário, reação e resposta a elemento, do mais recente | | `videos_reply_comment` | escrita | propõe uma resposta a um comentário; **só aparece depois que uma pessoa aprova** | As de escrita levam `destructiveHint`: um cliente sério pede confirmação à pessoa. `videos_reply_comment` é a única com a regra "Confirma" no servidor: a resposta vira uma ação pendente que uma pessoa aprova, edita ou recusa em Interações, e só então aparece como "Agente · aprovado por ". **Publicar e apagar não são ferramentas** — são de uma pessoa, no painel. O agente escreve no rascunho. Quem publica é você. ## 9. Escopo — o que este produto NÃO faz - **Teste A/B**, conversa com IA sobre os vídeos no painel (pelo MCP, o seu agente lê transcrição, comentários e interações). - **Marca d'água, DRM**, proteção contra download como recurso. - **Domínio próprio** no player ou na entrega: o endereço do streaming leva o nome do fornecedor. - **Contagem de plays.** O produto conta minutos. - **Identificar todo espectador.** Só quem se identificou por uma das quatro portas; o resto é contagem. - **Integração pronta com plataformas de venda ou com o Payments.** A venda entra pela chamada `POST /api/conversions`, que alguém precisa fazer. - **Área de membros, live, tutor por IA, dublagem.** - **Cobrança.** Não há plano publicado, e nada é cobrado. - **Armazenamento no Brasil.** Um fornecedor de vídeo guarda, converte e entrega; o armazenamento principal fica na **Alemanha, com réplica em São Paulo**. O player, a medição, o documento e as integrações são do Videos. Não afirme "vídeo guardado no Brasil". ## 10. Checklist para agentes de IA 1. **Peça a `vk_`** para a API; para o MCP, o login por código no e-mail. 2. **Cadastre o domínio da página** em `access.allowed_domains` antes de testar: fora dele, o vídeo não toca. 3. **Nunca ponha a `vk_` no HTML.** O token de `data-viewer` é feito no servidor. 4. **Não mande o arquivo pelo chat.** Entregue o link de envio tus. 5. **Escreva no rascunho e peça a uma pessoa para publicar.** 6. **"Quem assistiu" vem sempre com "entre quem se identificou".** 7. **Não invente rota nem tool.** A seção 6 lista as rotas de integração; a 8, as tools.