# Grade — referência completa da API > Gerada do catálogo em https://staging.gradetv.net · build `359bbfd6` > 92 endpoints · 85 estruturas > Índice curto: https://staging.gradetv.net/llms.txt · Spec: https://staging.gradetv.net/openapi.json · MCP: https://staging.gradetv.net/mcp Diretório de transmissões públicas e galeria pessoal com URL estável por pasta. Não armazena nem retransmite vídeo. O M3U e a API apontam para GET https://m3m8.gradetv.net/api/s/:id. Links antigos em gradetv.net/api/s/ ainda 302 para o mesmo path. Playlists principais e internas são servidas pela Grade com headers; segmentos e chaves vêm da origem. O player conta o play uma vez por pessoa, canal e dia, sem o refresh HLS. Falha na obtenção de uma playlist pode retornar 422 ou 502. 401/403 da origem oferece abrir o site oficial do canal e copiar a playlist original para outro player. Mídia HTTP oferece player legado HTTP isolado, sem conta (GET /api/legacy/:id). CORS e recusa do provedor ainda podem impedir reprodução. Produtores: https://staging.gradetv.net/produtores e GET https://staging.gradetv.net/api/producers — transmissão autorizada sob consulta. Informações e contato, sem ativação. Envie o projeto para contato@gradetv.net ou POST /api/contact (Turnstile humano; x402/crédito para agente). ## Como ler - Cada endpoint traz caminho, auth, parâmetros, corpo, estrutura da resposta, erros e uma chamada que roda. - `Pagina` é referência: os campos estão em **Estruturas**, no fim, uma vez só. - `(opcional)` num campo quer dizer que ele pode não vir; `(pode ser null)` quer dizer que vem com valor nulo. - Fatie o que precisa: `https://staging.gradetv.net/llms-full.txt?prefix=/api/` devolve só aquele ramo. ## Autenticação - `credito` — Token de crédito em `Authorization: Bearer cred_…` (ou header `X-Credito`). Não é conta: é portador de saldo. - `none` — Público, sem credencial. - `guest` — Convidado `ipt_…` (`POST /api/guest`, em `X-Guest-Token` ou Bearer) ou, no navegador, a sessão da conta — com sessão, o dono é a conta, e escrever exige a mesma origem e o `X-CSRF-Token` de `/api/auth/bootstrap` (403 `invalid_origin`/`invalid_csrf`). Cookie da conta com a sessão vencida: 401 `session_ended`, nunca o convidado. Chave `iptk_…` retirada: 410 `api_key_retired`. - `session` — Conta: cookie HttpOnly `__Host-mm-auth`, gravado ao entrar pela modal da conta ou em `/conta/global`; a escrita exige a mesma origem e `X-CSRF-Token` de `/api/auth/bootstrap`. Não há bearer para pessoas. - `token` — Token de operador: `METRICS_TOKEN` nas métricas e relatos crus; `CATALOGO_TOKEN` nas rotas `/api/admin/catalogo/*` da recarga (credencial exclusiva de operação). ## Endpoints ## Descoberta ### `GET /okf/:arquivo` Bundle OKF (Open Knowledge Format v0.1): markdown com frontmatter para o agente ler o produto inteiro sem parsear HTML. - **URL:** `https://staging.gradetv.net/okf/:arquivo` - **Auth:** `none` — Público, sem credencial. **Parâmetros de caminho** - `arquivo` (string, obrigatório) — `index.md`, `sobre.md`, `api.md` ou `faq.md`. Ex.: `index.md`. **Resposta `200`** `text/markdown`. Comece por `/okf/index.md`, que lista o bundle. **Erros** - `404` — Arquivo fora do bundle. **Exemplo** ```sh curl -s https://staging.gradetv.net/okf/index.md ``` ### `GET /.well-known/:arquivo` Descoberta de máquina antes da home: `api-catalog` (RFC 9727, linkset com a API e o MCP), `security.txt` (RFC 9116), `x402` (manifesto de pagamento: rede, carteira e rotas que cobram), `agent-card.json` (identidade do agente: ferramentas MCP e portas de descoberta; também em `/agent.json`) e `mcp-registry-auth` (chave do registro oficial de MCP). - **URL:** `https://staging.gradetv.net/.well-known/:arquivo` - **Auth:** `none` — Público, sem credencial. **Parâmetros de caminho** - `arquivo` (string, obrigatório) — `api-catalog`, `security.txt`, `x402`, `agent-card.json`, `mcp-registry-auth` ou `apis.json`. Ex.: `api-catalog`. **Resposta `200`** `application/linkset+json` no api-catalog; `application/json` no x402, no agent-card.json e no apis.json; `text/plain` nos outros dois. **Erros** - `404` — Nome fora dos seis publicados. **Exemplo** ```sh curl -s https://staging.gradetv.net/.well-known/api-catalog ``` ### `GET /apis.json` APIs.json (apisjson.org, 0.19): o índice que o APIs.io colhe — a API, o MCP, OpenAPI, guia e bundle OKF num arquivo só. Também em `/.well-known/apis.json`. - **URL:** `https://staging.gradetv.net/apis.json` - **Auth:** `none` — Público, sem credencial. **Resposta `200`** `application/json` no formato APIs.json 0.19: `apis[]` com `baseURL`, `humanURL` e `properties[]`. **Exemplo** ```sh curl -s https://staging.gradetv.net/apis.json ``` ### `GET /agent.json` Cartão do agente: identidade, quem opera, documentação, o endpoint MCP e as ferramentas que ele serve. Mesmo documento de `/.well-known/agent-card.json`. - **URL:** `https://staging.gradetv.net/agent.json` - **Auth:** `none` — Público, sem credencial. **Resposta `200`** `application/json`: `name`, `provider`, `protocol` (`mcp`), `interfaces[]` e `skills[]`. **Exemplo** ```sh curl -s https://staging.gradetv.net/agent.json ``` ### `GET /api/` Índice auto-descrito da API inteira, com os idiomas e as páginas HTML de cada um. - **URL:** `https://staging.gradetv.net/api/` - **Auth:** `none` — Público, sem credencial. **Resposta `200`** - `name` (string) — Nome do produto. - `description` (string) — O que o produto faz, em uma frase. - `locales` (object) — Idiomas atendidos e o caminho de cada página em cada um. - `auth` (object) — Cada modo de autenticação e como obtê-lo. - `docs` (object) — Links para llms.txt, llms-full.txt, openapi.json, MCP e a UI. - `endpoints` (object[]) — Todo endpoint com método, caminho, auth, URL absoluta e o que devolve. - `quota` (object) — O que é grátis, o que custa e como pagar — antes de você gastar chamada. - `mcp` (object) — Endereço e transporte do servidor MCP. - `quickstart` (string[]) — As quatro chamadas que levam do zero à biblioteca. ### `GET /api/health` Liveness e o commit publicado agora — é como o smoke espera o próprio deploy. - **URL:** `https://staging.gradetv.net/api/health` - **Auth:** `none` — Público, sem credencial. **Resposta `200`** Estrutura: `Saude`. - `ok` (bool) — Sempre `true` quando o Worker responde. - `app` (string) — Nome do produto. - `build` (string) — Commit publicado; o CI passa o SHA curto no deploy. - `ts` (string) — Momento da resposta (UTC, ISO-8601). - `catalog` (FrescorCatalogo) — Idade do catálogo: `synced_at` da última recarga e se passou do limite de 2 dias (o smoke reprova). → ver `FrescorCatalogo` em **Estruturas**. - `sources` (object) — Uma entrada por referência do catálogo: `fetched_at`, `sha256`, `itens`, `age_hours`, `limit_days`, `stale` (passou do limite ou a recarga usou o snapshot anterior), `ausente` (saiu sem a fonte). Só o tronco velho reprova o smoke. ### `POST /mcp` Servidor MCP por HTTP (Streamable HTTP, JSON-RPC 2.0) — pluga no cliente sem instalar nada. As tools são as operações deste mesmo catálogo; o MCP não tem backend próprio. `GET /mcp` devolve o cartão do servidor. - **URL:** `https://staging.gradetv.net/mcp` - **Auth:** `none` — Público, sem credencial. - Credencial vai nos headers de sempre (X-Guest-Token, Authorization, X-PAYMENT) e é repassada à API. - Cota estourada chega como 402 com accepts[] dentro do resultado da tool — pague e repita. **Resposta `200`** Resposta JSON-RPC 2.0 (`initialize`, `tools/list` ou `tools/call`). **Exemplo** ```sh curl -s -XPOST https://staging.gradetv.net/mcp -H 'content-type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' ``` ### `GET /api/pricing` Preços vigentes e franquias gratuitas. - **URL:** `https://staging.gradetv.net/api/pricing` - **Auth:** `none` — Público, sem credencial. **Resposta `200`** - `product` (string) — Product name. - `quota` (PaymentQuota) — Public allowances and current list prices; not personal usage. → ver `PaymentQuota` em **Estruturas**. - `pricing` (string) — Absolute URL of the current price list. - `billing` (string) — Absolute URL of payment discovery or the existing billing summary. - `api_index` (string) — Absolute URL of the API catalog. **Erros** - `405` — Use GET ou HEAD. **Exemplo** ```sh curl -s https://staging.gradetv.net/api/pricing ``` ## Catálogo ### `GET /api/channels` Busca paginada do catálogo público, com as facetas de categoria da busca atual. É a porta de entrada do produto. A resposta varia por navegador, sistema e país de quem pede — cada canal traz `social.your_fails`, o recorte do SEU ambiente — por isso ela é `Cache-Control: private`. - **URL:** `https://staging.gradetv.net/api/channels` - **Auth:** `none` — Público, sem credencial. **Query** - `q` (string) — Texto livre no nome e nos apelidos do canal (busca full-text). Ex.: `globo`. - `country` (string) — País do canal, ISO 3166-1 alpha-2. Ex.: `BR`. - `category` (string) — ID de categoria do catálogo. Ex.: `news`. - `language` (string) — Idioma do canal, ISO 639-3. Ex.: `por`. - `network` (string) — Nome exato da rede/emissora. Ex.: `Globo`. - `quality` (string) — Qualidade exata do stream. Ex.: `1080p`. - `guide` (bool) — `1` traz só canal com grade de programação (EPG). Padrão: `0`. Valores: `0`, `1`. - `subdivision` (string) — Estado/província, código do catálogo. Ex.: `BR-SP`. - `city` (string) — Cidade, código do catálogo. - `playable` (bool) — `0` inclui canal sem stream utilizável conhecido. Padrão: `1`. Valores: `0`, `1`. - `sort` (string) — `score` ordena pela saúde medida por terceiro, melhor primeiro; canal não medido vai para o fim. `votes` ordena pelos votos registrados para a estação (rádio). `name` é a ordem alfabética. Padrão: `name`. Valores: `name`, `score`, `votes`. - `online` (bool) — `1` traz só canal visto online pela fonte nas 48 h anteriores à última recarga do catálogo (`health_ext.online`); com o catálogo parado há mais de 48 h o filtro não devolve ninguém. Padrão: `0`. Valores: `0`, `1`. - `kind` (string) — `tv` (padrão) é o catálogo de TV; `radio` são as estações de rádio; `all` junta os dois. Sem `kind`, rádio nunca aparece. Padrão: `tv`. Valores: `tv`, `radio`, `all`. - `tag` (string) — Tag da estação de rádio (vocabulário livre, ex. `mpb`, `news`); veja `GET /api/tags`. Ex.: `mpb`. - `limit` (int) — Itens por página. Acima de 50 é silenciosamente reduzido a 50. Padrão: `20`. - `offset` (int) — Quantos itens pular. Use `next_offset` da resposta anterior. Padrão: `0`. **Resposta `200`** Estrutura: `PaginaDeCanais`. - `items` (Canal[]) — Os canais desta página, na ordem pedida (`sort`): nome, ou melhor saúde medida primeiro. → ver `Canal` em **Estruturas**. - `total` (int) — Canais que casam com o filtro, ignorando a paginação. - `limit` (int) — Tamanho de página aplicado (teto de 50). - `offset` (int) — Deslocamento aplicado nesta página. - `next_offset` (int, pode ser null) — Offset da próxima página; `null` quando acabou. - `facets` (FacetasCanal) — Contagem por categoria DENTRO do filtro atual — serve para montar o menu lateral. → ver `FacetasCanal` em **Estruturas**. - `filters` (FiltrosCanal) — Os filtros como o servidor os entendeu, já normalizados. → ver `FiltrosCanal` em **Estruturas**. **Exemplo** ```sh curl -s 'https://staging.gradetv.net/api/channels?country=BR&language=por&playable=1&limit=5' ``` ### `GET /api/channels/:id` Ficha completa de um canal, com os streams já apontando para o nosso hop. - **URL:** `https://staging.gradetv.net/api/channels/:id` - **Auth:** `none` — Público, sem credencial. **Parâmetros de caminho** - `id` (string, obrigatório) — ID do canal no catálogo, ex. `GloboNews.br`. Ex.: `GloboNews.br`. **Resposta `200`** Estrutura: `CanalCompleto`. - `id` (string) — ID estável do catálogo, ex. `GloboNews.br`. É a chave em toda a API. - `name` (string) — Nome de exibição do canal. - `alt_names` (string[]) — Outros nomes pelos quais o canal é conhecido. - `country` (string, pode ser null) — País de origem, ISO 3166-1 alpha-2. - `categories` (string[]) — IDs de categoria do catálogo, ex. `news`, `sports`. - `category_labels` (string[]) — Os mesmos IDs já traduzidos para exibição. - `languages` (string[]) — Idiomas do canal, ISO 639-3. - `language_labels` (string[]) — Nomes dos idiomas acima, quando conhecidos. - `logo_url` (string, pode ser null) — Logo servido por nós (variante ≤256px), não a origem. - `website` (string, pode ser null) — Site oficial do canal. - `playable_hint` (bool, opcional) — Se a última verificação achou stream utilizável. - `slug` (string, pode ser null) — Identificador legível; é id de API, não URL pública. - `network` (string, pode ser null) — Rede/emissora a que o canal pertence. - `owners` (string[]) — Quem opera o canal, conforme o cadastro. - `launched` (string, pode ser null) — Data de lançamento (AAAA-MM-DD). - `replaced_by` (string, pode ser null) — ID do canal que substituiu este, se foi descontinuado. - `feed_name` (string, pode ser null) — Nome do feed quando o canal tem mais de um. - `feed_format` (string, pode ser null) — Formato do feed declarado pela fonte. - `timezones` (string[]) — Fusos em que o canal transmite. - `broadcast_area` (string[]) — Área de cobertura, em códigos do catálogo. - `quality` (string, opcional) — Melhor qualidade conhecida, ex. `1080p`. - `has_guide` (bool) — Se existe grade de programação (EPG) para este canal. - `guide_site` (string, pode ser null) — Site de onde a grade vem. - `guide_lang` (string, pode ser null) — Idioma da grade de programação. - `subdivision` (string, pode ser null) — Estado/província, código do catálogo. - `city` (string, pode ser null) — Cidade, código do catálogo. - `kind` (string) — `tv` ou `radio` (estação de rádio). - `radio` (Radio, opcional) — Só em estação de rádio: tags, votos, cliques, codec, bitrate e localização. → ver `Radio` em **Estruturas**. - `origem` (string) — Identificador de procedência; créditos e licenças em `/sobre`. - `guide_now` (GuiaAgora, opcional, pode ser null) — Só na ficha: agora e a seguir na programação de hoje, grabada por nós; `null` sem guia fresca. → ver `GuiaAgora` em **Estruturas**. - `health_ext` (SaudeMedida, pode ser null) — Saúde medida por terceiro; `null` quando o canal não foi medido. → ver `SaudeMedida` em **Estruturas**. - `social` (Social, opcional) — Contadores da comunidade; ausente nas páginas HTML de SEO. → ver `Social` em **Estruturas**. - `api` (string) — URL absoluta da ficha deste canal. - `streams` (Stream[]) — Transmissões conhecidas, com a URL já apontando para o nosso hop. → ver `Stream` em **Estruturas**. - `_links` (LinksCanal) — Esta ficha, a mesma coisa na interface humana e o índice da API. → ver `LinksCanal` em **Estruturas**. **Erros** - `404` — Canal não disponível no catálogo. **Exemplo** ```sh curl -s https://staging.gradetv.net/api/channels/GloboNews.br ``` ### `GET /api/channels/:id/health` Por que o canal falha, para quem e onde — inclui geo-bloqueio, latência por região e o veredito de quem está chamando. É o que separa 'o canal está fora do ar' de 'o canal está bloqueado no seu país'. `geo` diz se é restrição regional ou falha geral (com os países), `regions[]` traz sucesso/falha por país, `latency[]` a velocidade de abertura medida no hop por país, e `pra_voce` resume tudo para o país de quem chama. Sem relato da comunidade (`POST /api/play-report`) o painel fica vazio. - **URL:** `https://staging.gradetv.net/api/channels/:id/health` - **Auth:** `none` — Público, sem credencial. **Parâmetros de caminho** - `id` (string, obrigatório) — ID do canal no catálogo. Ex.: `GloboNews.br`. **Resposta `200`** Estrutura: `SaudeCanal`. - `channel_id` (string) — Canal a que esta saúde se refere. - `plays` (int) — Relatos de sucesso, no mundo todo. - `fails` (int) — Relatos de falha, no mundo todo. - `favorites` (int) — Quantas pessoas favoritaram o canal. - `comments` (int) — Comentários públicos no canal. - `health` (int, pode ser null) — Percentual de sucesso; `null` com menos de `min_relatos`. - `last_ok_at` (string, pode ser null) — Último relato de sucesso (UTC). - `last_fail_at` (string, pode ser null) — Último relato de falha (UTC). - `last_fail_code` (string, pode ser null) — Código da falha mais recente. - `min_relatos` (int) — Quantos relatos são necessários antes de calcular `health`. - `reasons` (MotivoFalha[]) — Por que falhou, do motivo mais comum para o menos. → ver `MotivoFalha` em **Estruturas**. - `environments` (Ambiente[]) — O mesmo canal por navegador, sistema e país. → ver `Ambiente` em **Estruturas**. - `your_environment` (Ambiente) — O recorte de QUEM ESTÁ CHAMANDO, deduzido do User-Agent e da borda. → ver `Ambiente` em **Estruturas**. - `regions` (RegiaoSaude[]) — O mesmo canal agregado por PAÍS — onde falha e onde funciona. → ver `RegiaoSaude` em **Estruturas**. - `geo` (GeoCanal) — Veredito do bloqueio: geo-restrito (falha numas regiões, funciona noutras) ou fora do ar (falha em todas). → ver `GeoCanal` em **Estruturas**. - `latency` (LatenciaPais[]) — Quão rápido a playlist abre, por país — medido no hop `/api/s/:id`. → ver `LatenciaPais` em **Estruturas**. - `your_country` (RegiaoSaude) — O recorte do PAÍS de quem está chamando, com a latência da borda dele. → ver `RegiaoSaude` em **Estruturas**. - `pra_voce` (string) — Veredito final para quem está chamando: `geo_bloqueado`, `lenta`, `instavel`, `boa` ou `sem_dado`. - `codes` (string[]) — Todos os códigos de falha que o produto reconhece. - `_links` (LinksSaude) — Esta saúde, o canal e onde relatar. → ver `LinksSaude` em **Estruturas**. **Erros** - `404` — Canal não existe no catálogo. **Exemplo** ```sh curl -s https://staging.gradetv.net/api/channels/GloboNews.br/health ``` ### `GET /api/channels/:id/guia` Programação de hoje do canal, grabada por nós: o que está no ar agora e o que vem a seguir. Disponível nos canais com guia (`guide=1`), com validade de até dois dias. Confira a data da resposta; a ficha traz o resumo em `guide_now`. - **URL:** `https://staging.gradetv.net/api/channels/:id/guia` - **Auth:** `none` — Público, sem credencial. **Parâmetros de caminho** - `id` (string, obrigatório) — ID do canal no catálogo. Ex.: `RecordNews.br`. **Resposta `200`** Estrutura: `GuiaDoDia`. - `channel_id` (string) — ID do canal no catálogo. - `day` (string) — Dia grabado, YYYY-MM-DD. - `site` (string, pode ser null) — Site de programação de origem. - `agora` (Programa, pode ser null) — O programa no ar neste instante. → ver `Programa` em **Estruturas**. - `a_seguir` (Programa, pode ser null) — O próximo programa. → ver `Programa` em **Estruturas**. - `programas` (Programa[]) — Todos os programas do dia, em ordem (até 200). → ver `Programa` em **Estruturas**. **Erros** - `404` — Canal sem guia do dia (ou com guia velha). **Exemplo** ```sh curl -s https://staging.gradetv.net/api/channels/RecordNews.br/guia ``` ### `GET /api/geo` País e idioma sugeridos para quem está chamando. - **URL:** `https://staging.gradetv.net/api/geo` - **Auth:** `none` — Público, sem credencial. **Resposta `200`** Estrutura: `Geo`. - `country` (string) — País a usar; cai em `BR` quando a borda não informa. - `detected` (string, pode ser null) — O que a borda realmente detectou; `null` se nada. - `language` (string) — Idioma a usar; cai em `por` sem detecção. - `language_detected` (string, pode ser null) — Idioma realmente detectado. - `source` (string) — `cf` quando veio da borda, `fallback` quando é o padrão. - `api` (string) — URL absoluta desta rota. **Exemplo** ```sh curl -s https://staging.gradetv.net/api/geo ``` ## Facetas ### `GET /api/countries` Países que têm canal tocável, com a contagem e a bandeira de cada um. - **URL:** `https://staging.gradetv.net/api/countries` - **Auth:** `none` — Público, sem credencial. **Query** - `kind` (string) — `tv` (padrão) conta canais de TV; `radio` conta estações de rádio; `all` junta os dois. Padrão: `tv`. Valores: `tv`, `radio`, `all`. **Resposta `200`** Estrutura: `Lista`. - `items` (Pais[]) — Todos os itens; estas rotas não paginam. → ver `Pais` em **Estruturas**. **Exemplo** ```sh curl -s 'https://staging.gradetv.net/api/countries?kind=radio' ``` ### `GET /api/tags` Tags das estações de rádio tocáveis (vocabulário livre), com a contagem de cada uma. - **URL:** `https://staging.gradetv.net/api/tags` - **Auth:** `none` — Público, sem credencial. **Query** - `country` (string) — Restringe às estações de um país, ISO 3166-1 alpha-2. Ex.: `BR`. - `limit` (int) — Quantas tags devolver (teto 100). Padrão: `40`. **Resposta `200`** Estrutura: `Lista`. - `items` (Tag[]) — Todos os itens; estas rotas não paginam. → ver `Tag` em **Estruturas**. **Exemplo** ```sh curl -s 'https://staging.gradetv.net/api/tags?country=BR&limit=20' ``` ### `GET /api/categories` Vocabulário de categorias do catálogo, com ícone para a interface. Note que `POST /api/categories` é outra coisa: cria pasta na biblioteca do dono. - **URL:** `https://staging.gradetv.net/api/categories` - **Auth:** `none` — Público, sem credencial. **Resposta `200`** Estrutura: `Lista`. - `items` (Categoria[]) — Todos os itens; estas rotas não paginam. → ver `Categoria` em **Estruturas**. ### `GET /api/languages` Idiomas que têm canal tocável, com a contagem de cada um. - **URL:** `https://staging.gradetv.net/api/languages` - **Auth:** `none` — Público, sem credencial. **Query** - `kind` (string) — `tv` (padrão) conta canais de TV; `radio` conta estações de rádio; `all` junta os dois. Padrão: `tv`. Valores: `tv`, `radio`, `all`. **Resposta `200`** Estrutura: `Lista`. - `items` (Idioma[]) — Todos os itens; estas rotas não paginam. → ver `Idioma` em **Estruturas**. **Exemplo** ```sh curl -s 'https://staging.gradetv.net/api/languages?kind=tv' ``` ### `GET /api/networks` Redes e emissoras que têm canal tocável, com a contagem. - **URL:** `https://staging.gradetv.net/api/networks` - **Auth:** `none` — Público, sem credencial. **Query** - `kind` (string) — `tv` (padrão) conta canais de TV; `radio` conta estações de rádio; `all` junta os dois. Padrão: `tv`. Valores: `tv`, `radio`, `all`. **Resposta `200`** Estrutura: `Lista`. - `items` (Rede[]) — Todos os itens; estas rotas não paginam. → ver `Rede` em **Estruturas**. **Exemplo** ```sh curl -s 'https://staging.gradetv.net/api/networks?kind=tv' ``` ### `GET /api/qualities` Qualidades distintas encontradas nos streams do catálogo (em rádio, codec e bitrate). - **URL:** `https://staging.gradetv.net/api/qualities` - **Auth:** `none` — Público, sem credencial. **Query** - `kind` (string) — `tv` (padrão) conta canais de TV; `radio` conta estações de rádio; `all` junta os dois. Padrão: `tv`. Valores: `tv`, `radio`, `all`. **Resposta `200`** Estrutura: `Lista`. - `items` (Qualidade[]) — Todos os itens; estas rotas não paginam. → ver `Qualidade` em **Estruturas**. **Exemplo** ```sh curl -s 'https://staging.gradetv.net/api/qualities?kind=radio' ``` ### `GET /api/subdivisions` Estados e províncias que têm canal tocável. - **URL:** `https://staging.gradetv.net/api/subdivisions` - **Auth:** `none` — Público, sem credencial. **Query** - `country` (string) — Restringe a um país, ISO 3166-1 alpha-2. Ex.: `BR`. - `kind` (string) — `tv` (padrão) conta canais de TV; `radio` conta estações de rádio; `all` junta os dois. Padrão: `tv`. Valores: `tv`, `radio`, `all`. **Resposta `200`** Estrutura: `Lista`. - `items` (Subdivisao[]) — Todos os itens; estas rotas não paginam. → ver `Subdivisao` em **Estruturas**. **Exemplo** ```sh curl -s 'https://staging.gradetv.net/api/subdivisions?country=BR' ``` ### `GET /api/cities` Cidades que têm canal tocável, filtráveis por país e por estado. - **URL:** `https://staging.gradetv.net/api/cities` - **Auth:** `none` — Público, sem credencial. **Query** - `country` (string) — Restringe a um país, ISO 3166-1 alpha-2. Ex.: `BR`. - `subdivision` (string) — Restringe a um estado/província. Ex.: `BR-SP`. - `kind` (string) — `tv` (padrão) conta canais de TV; `radio` conta estações de rádio; `all` junta os dois. Padrão: `tv`. Valores: `tv`, `radio`, `all`. **Resposta `200`** Estrutura: `Lista`. - `items` (Cidade[]) — Todos os itens; estas rotas não paginam. → ver `Cidade` em **Estruturas**. **Exemplo** ```sh curl -s 'https://staging.gradetv.net/api/cities?country=BR&subdivision=BR-SP' ``` ## Produtores ### `GET /api/producers` Oferta sob consulta para produtores com conteúdo autorizado e canais de contato. Somente informações e captação de interesse. Não provisiona, não cobra e não ativa transmissão. Use contact.form_url no browser, contact.email por e-mail ou POST /api/contact para apresentar o projeto. O envio por API mantém o gate x402 ou crédito pré-pago; o formulário humano usa Turnstile. - **URL:** `https://staging.gradetv.net/api/producers` - **Auth:** `none` — Público, sem credencial. **Query** - `lang` (string) — Idioma da oferta: pt, en, es, fr ou de; ausente ou desconhecido volta a pt. Padrão: `pt`. Valores: `pt`, `en`, `es`, `fr`, `de`. **Resposta `200`** - `status` (string) — `sob_consulta`: proposta sujeita a avaliação individual. - `activation_available` (bool) — Sempre false: não há ativação de transmissão nesta superfície. - `title` (string) — Nome da oferta no idioma solicitado. - `description` (string) — Apresentação do serviço sob consulta. - `audience` (string) — Perfil de produtores e organizações atendidos pela proposta. - `services` (string[]) — Capacidades a avaliar no projeto, sem compromisso de disponibilidade. - `requirements` (string) — Necessidade de autorização para sinal e obras, território e prazo. - `availability` (string) — Condição de avaliação antes de confirmar início e escopo. - `pricing` (string) — Orçamento sob consulta; enviar interesse não contrata o serviço. - `contact` (object) — email, form_url e api_url absolutos, message_template e instructions para apresentar canal/evento, direitos, audiência, duração e data. - `_links` (object) — self (esta API com idioma) e page (página da oferta): URLs absolutas. **Erros** - `405` — A oferta só aceita GET; não existe provisionamento por POST. **Exemplo** ```sh curl -s "https://staging.gradetv.net/api/producers?lang=pt" ``` ## Mídia ### `GET /logos/:id` Logo do canal servido por nós, na variante de card (≤256px). Nunca faz proxy na hora: ou o arquivo está no R2, ou responde 404. É o que impede a página de card virar um proxy de imagem de terceiro. - **URL:** `https://staging.gradetv.net/logos/:id` - **Auth:** `none` — Público, sem credencial. **Parâmetros de caminho** - `id` (string, obrigatório) — ID do logo, que vem em `Canal.logo_url`. Ex.: `GloboNews.br`. **Resposta `200`** `image/webp` ou `image/png` — os bytes do logo. **Erros** - `404` — Não há variante em cache para este canal. ### `GET /api/s/:id` Hop do stream: a URL pública aponta para https://m3m8.gradetv.net; o apex ainda 302 para compatibilidade. Mídia continua direta. Conta o play uma vez por pessoa, canal e dia. É o endereço que aparece no M3U e na API: https://m3m8.gradetv.net/api/s/:id, com CORS * e cache de 1 s na CDN (browser continua no-store). Links antigos em gradetv.net/api/s/ ainda 302 no-store para o mesmo path. O Grade NÃO retransmite vídeo: os segmentos e chaves vêm da origem, no browser e no VLC. Playlists internas usam o mesmo hop com `p` assinado; copie a URL completa, sem montar o ticket. Cada busca tem teto de 512 KiB, 8 s e 3 redirects revalidados; até 128 referências internas por manifesto e 4 níveis. Refresh interno não conta outro play. Falha de playlist no o2 é 422 JSON (a CDN não reescreve 4xx); o Worker de fallback ainda usa 502. Sem redirecionamento para HTTP. Headers expostos por CORS: X-Grade-Upstream-Status (status observado na origem), X-Grade-Source (URL original da transmissão para copiar), X-Grade-Website (site oficial do canal, vazio quando ausente), X-Grade-Legacy (ação HTTP pública) e X-Grade-Media-HTTP (1 se este manifesto referencia mídia/chave HTTP, 0 caso contrário). Mídia HTTP oferece player legado isolado; 401/403 da origem oferece abrir o site do canal e copiar a playlist original para outro player. CORS ainda pode impedir reprodução. Áudio/vídeo contínuo e YouTube recebem 302 para a origem. IDs novos não dependem da ordem da recarga. Um ID posicional antigo ausente recupera um stream do mesmo canal, feed e fonte, preservando os bloqueios. - **URL:** `https://staging.gradetv.net/api/s/:id` - **Auth:** `none` — Público, sem credencial. **Parâmetros de caminho** - `id` (string, obrigatório) — ID do stream, que vem em `Stream.id`. Ex.: `1001Noites.br:SD:2168`. **Query** - `p` (string) — Ticket de playlist interna emitido pelo hop, até 6000 caracteres, vinculado ao stream e à origem. Permanece válido enquanto origem/bloqueio permitirem. **Resposta `200`** A playlist é `application/vnd.apple.mpegurl` em m3m8.gradetv.net, com playlists internas pelo hop e mídia com URI absoluta da origem. Pedido legado no apex pode 302 para esse host. Stream contínuo/YouTube recebe 302 para o provedor. **Erros** - `404` — Stream bloqueado/ausente, ticket `p` inválido/expirado ou origem alterada. - `422` — `playlist_acesso`/`playlist_origem`/`playlist_limite` no hop do o2 (a CDN não reescreve 4xx). - `502` — `playlist_acesso` para 401/403 da origem (`upstream_status` no corpo); `playlist_origem` para outras falhas; `playlist_limite` para limite excedido. - `503` — Origem de playlists indisponível ou limite operacional atingido; `playlist_config` indica chave de assinatura indisponível. **Exemplo** ```sh curl -Ls 'https://staging.gradetv.net/api/s/STREAM_ID' ``` ### `GET /api/legacy/:id` Metadados públicos para o player legado HTTP, sem sessão. O botão legacy_url abre http://legacy.gradetv.net:8080/legacy?stream=ID. A página isolada aceita lang=pt/en/es/fr/de e theme=light/dark. Playlists continuam pelo hop HTTPS e mídia direto da origem. Não remove recusa de acesso nem exigência CORS. Uma leitura indexada; não busca origem nem grava no D1. - **URL:** `https://staging.gradetv.net/api/legacy/:id` - **Auth:** `none` — Público, sem credencial. **Parâmetros de caminho** - `id` (string, obrigatório) — ID público do stream, incluindo compatibilidade de IDs antigos. Ex.: `1001Noites.br:SD:2168`. **Resposta `200`** - `id` (string) — ID resolvido do stream. - `name` (string) — Nome do canal. - `kind` (string) — Tipo do stream para o player. - `radio` (bool) — Se o canal é rádio. - `url` (string) — URL pública do hop HTTPS de playlist. - `provider_url` (string) — URL direta da transmissão no provedor. - `website` (string, pode ser null) — Site oficial do canal; null quando ausente ou inválido. Destino do botão de abrir site, separado da URL da transmissão. - `legacy_url` (string) — Player HTTP isolado, sem credencial. **Erros** - `404` — Stream indisponível ou origem incompatível. **Exemplo** ```sh curl -s 'https://staging.gradetv.net/api/legacy/STREAM_ID' ``` ### `GET /api/m/:ticket` Desligado: era o pass-through de vídeo. Responde 410 sempre. Até 05/09/2026 encaminhava os bytes da origem com CORS nosso. Relay de stream de terceiro não é serviço do Grade; a rota fica documentada para quem ainda tiver o endereço numa playlist antiga. - **URL:** `https://staging.gradetv.net/api/m/:ticket` - **Auth:** `none` — Público, sem credencial. **Parâmetros de caminho** - `ticket` (string, obrigatório) — Ticket legado ignorado; nenhum valor reativa a retransmissão. Ex.: `tk_legado`. **Resposta `200`** 410 `relay_desligado`, sempre. **Erros** - `404` — Caminho inexistente fora da família retirada. - `410` — Sempre: o Grade não retransmite vídeo. ## Feeds ### `GET /f/:token/library.:formato` Feed da biblioteca inteira do dono, no formato pedido pela extensão. É a URL que a pessoa cola no VLC. Não pede credencial: o token no caminho É a credencial, e quem tem o link tem o conteúdo — trate como segredo. As URLs saem prontas em `Biblioteca.feeds`, e cada pasta e sub-aba tem a sua. Itens com IDs posicionais antigos se recuperam na leitura, enquanto o mesmo canal, feed e fonte continuarem disponíveis; não é preciso salvar novamente. - **URL:** `https://staging.gradetv.net/f/:token/library.:formato` - **Auth:** `none` — Público, sem credencial. - Item do YouTube fica fora do M3U e do XSPF (o VLC não toca) e vem no JSON com `kind: "youtube"`. **Parâmetros de caminho** - `token` (string, obrigatório) — Token de feed do dono; vem em `Biblioteca.feeds` e não é o guest token. Ex.: `k7m2p9r4t6v8w1y3z5b7c9d1`. - `formato` (string, obrigatório) — Extensão que escolhe o formato de saída. Valores: `m3u`, `m3u8`, `json`, `xspf`. Ex.: `m3u`. **Resposta `200`** `audio/x-mpegurl` (m3u/m3u8), `application/json` ou `application/xspf+xml`. **Erros** - `404` — Token de feed desconhecido. **Exemplo** ```sh curl -s https://staging.gradetv.net/f/FEED_TOKEN/library.m3u ``` ### `GET /f/:token/c/:categoria.:formato` Feed de uma pasta da biblioteca, para assinar só aquele recorte. - **URL:** `https://staging.gradetv.net/f/:token/c/:categoria.:formato` - **Auth:** `none` — Público, sem credencial. - Item do YouTube fica fora do M3U e do XSPF (o VLC não toca) e vem no JSON com `kind: "youtube"`. **Parâmetros de caminho** - `token` (string, obrigatório) — Token de feed do dono, vindo de `Biblioteca.feeds`. Ex.: `k7m2p9r4t6v8w1y3z5b7c9d1`. - `categoria` (string, obrigatório) — Slug da pasta, que vem em `PastaBiblioteca.slug`. Ex.: `jornalismo`. - `formato` (string, obrigatório) — Extensão que escolhe o formato de saída. Valores: `m3u`, `m3u8`, `json`, `xspf`. Ex.: `m3u`. **Resposta `200`** `audio/x-mpegurl` (m3u/m3u8), `application/json` ou `application/xspf+xml`. **Erros** - `404` — Token de feed ou pasta desconhecidos. **Exemplo** ```sh curl -s https://staging.gradetv.net/f/FEED_TOKEN/c/noticias.m3u ``` ### `GET /f/:token/c/:categoria/g/:grupo.:formato` Feed de uma sub-aba — o recorte mais fino que a galeria oferece. - **URL:** `https://staging.gradetv.net/f/:token/c/:categoria/g/:grupo.:formato` - **Auth:** `none` — Público, sem credencial. - Item do YouTube fica fora do M3U e do XSPF (o VLC não toca) e vem no JSON com `kind: "youtube"`. **Parâmetros de caminho** - `token` (string, obrigatório) — Token de feed do dono, vindo de `Biblioteca.feeds`. Ex.: `k7m2p9r4t6v8w1y3z5b7c9d1`. - `categoria` (string, obrigatório) — Slug da pasta que contém a sub-aba. - `grupo` (string, obrigatório) — Slug da sub-aba, que vem em `SubAba.slug`. - `formato` (string, obrigatório) — Extensão que escolhe o formato de saída. Valores: `m3u`, `m3u8`, `json`, `xspf`. Ex.: `m3u`. **Resposta `200`** `audio/x-mpegurl` (m3u/m3u8), `application/json` ou `application/xspf+xml`. **Erros** - `404` — Token de feed, pasta ou sub-aba desconhecidos. **Exemplo** ```sh curl -s https://staging.gradetv.net/f/FEED_TOKEN/c/noticias/g/manchete.json ``` ## Identidade ### `POST /api/guest` Cria um convidado `ipt_…` — é a identidade que guarda galeria, histórico e favoritos sem conta. Não pede e-mail nem nada. Guarde o token: perdeu o token, perdeu a biblioteca — a não ser que a conta já o tenha reivindicado (`POST /api/auth/claim`), e aí o que ele guardou está na conta. - **URL:** `https://staging.gradetv.net/api/guest` - **Auth:** `none` — Público, sem credencial. **Resposta `200`** - `token` (string) — O convidado, prefixo `ipt_`. Mande em `X-Guest-Token` ou como Bearer. **Exemplo** ```sh curl -s -XPOST https://staging.gradetv.net/api/guest ``` ### `POST /api/keys` Retirada: criava chave `iptk_…`. Responde 410. Conta e pagamento são globais desde 21/09/2026 e o SDK não tem chave de API por conta. Identifique-se com `X-Guest-Token: ipt_…` (`POST /api/guest`) ou pela sessão da conta; chamada paga usa o crédito global (`Authorization: Bearer cred_…`) ou x402. - **URL:** `https://staging.gradetv.net/api/keys` - **Auth:** `none` — Público, sem credencial. **Resposta `200`** 410 `api_keys_retired`, sempre. **Erros** - `410` — Sempre: as chaves de API próprias foram retiradas. **Exemplo** ```sh curl -s -XPOST https://staging.gradetv.net/api/keys ``` ### `GET /api/keys` Retirada: listava as chaves `iptk_…`. Responde 410. Conta e pagamento são globais desde 21/09/2026 e o SDK não tem chave de API por conta. Identifique-se com `X-Guest-Token: ipt_…` (`POST /api/guest`) ou pela sessão da conta; chamada paga usa o crédito global (`Authorization: Bearer cred_…`) ou x402. - **URL:** `https://staging.gradetv.net/api/keys` - **Auth:** `none` — Público, sem credencial. **Resposta `200`** 410 `api_keys_retired`, sempre. **Erros** - `410` — Sempre: as chaves de API próprias foram retiradas. **Exemplo** ```sh curl -s https://staging.gradetv.net/api/keys ``` ### `DELETE /api/keys/:id` Retirada: revogava uma chave `iptk_…`. Responde 410. Conta e pagamento são globais desde 21/09/2026 e o SDK não tem chave de API por conta. Identifique-se com `X-Guest-Token: ipt_…` (`POST /api/guest`) ou pela sessão da conta; chamada paga usa o crédito global (`Authorization: Bearer cred_…`) ou x402. - **URL:** `https://staging.gradetv.net/api/keys/:id` - **Auth:** `none` — Público, sem credencial. **Parâmetros de caminho** - `id` (string, obrigatório) — ID de chave antiga; ignorado. Ex.: `key_9f3c2b1d7a`. **Resposta `200`** 410 `api_keys_retired`, sempre. **Erros** - `404` — Caminho fora da família retirada — todo `/api/keys/…` responde 410. - `410` — Sempre: as chaves de API próprias foram retiradas. **Exemplo** ```sh curl -s -XDELETE https://staging.gradetv.net/api/keys/KEY_ID ``` ## Galeria ### `GET /api/library` A galeria inteira do dono: pastas, sub-abas, canais e as URLs de feed de cada nível. - **URL:** `https://staging.gradetv.net/api/library` - **Auth:** `guest` — Convidado `ipt_…` (`POST /api/guest`, em `X-Guest-Token` ou Bearer) ou, no navegador, a sessão da conta — com sessão, o dono é a conta, e escrever exige a mesma origem e o `X-CSRF-Token` de `/api/auth/bootstrap` (403 `invalid_origin`/`invalid_csrf`). Cookie da conta com a sessão vencida: 401 `session_ended`, nunca o convidado. Chave `iptk_…` retirada: 410 `api_key_retired`. **Resposta `200`** Estrutura: `Biblioteca`. - `owner` (string) — Rótulo derivado do dono desta árvore — estável enquanto o feed for o mesmo, e não reversível. NÃO é o token de convidado nem o id da conta: o token é a credencial mestra, sem revogação, e o rótulo sai derivado dele com o token do feed. - `feeds` (Feeds) — Feeds da biblioteca inteira, em quatro formatos. → ver `Feeds` em **Estruturas**. - `categories` (PastaBiblioteca[]) — As pastas do dono, na ordem que ele arrumou. → ver `PastaBiblioteca` em **Estruturas**. **Erros** - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. **Exemplo** ```sh curl -s https://staging.gradetv.net/api/library -H "X-Guest-Token: $IPT" ``` ### `POST /api/categories` Cria uma pasta na galeria, já com a sub-aba Geral dentro dela. Teto de 8 pastas por dono. A resposta traz a biblioteca inteira já atualizada — não precisa recarregar `GET /api/library` depois. - **URL:** `https://staging.gradetv.net/api/categories` - **Auth:** `guest` — Convidado `ipt_…` (`POST /api/guest`, em `X-Guest-Token` ou Bearer) ou, no navegador, a sessão da conta — com sessão, o dono é a conta, e escrever exige a mesma origem e o `X-CSRF-Token` de `/api/auth/bootstrap` (403 `invalid_origin`/`invalid_csrf`). Cookie da conta com a sessão vencida: 401 `session_ended`, nunca o convidado. Chave `iptk_…` retirada: 410 `api_key_retired`. **Corpo** (`application/json`) - `name` (string, obrigatório) — Nome da pasta, até 40 caracteres. **Exemplo de corpo** ```json { "name": "Notícias" } ``` **Resposta `200`** Estrutura: `BibliotecaComPasta`. - `owner` (string) — Rótulo derivado do dono desta árvore — estável enquanto o feed for o mesmo, e não reversível. NÃO é o token de convidado nem o id da conta: o token é a credencial mestra, sem revogação, e o rótulo sai derivado dele com o token do feed. - `feeds` (Feeds) — Feeds da biblioteca inteira, em quatro formatos. → ver `Feeds` em **Estruturas**. - `categories` (PastaBiblioteca[]) — As pastas do dono, na ordem que ele arrumou. → ver `PastaBiblioteca` em **Estruturas**. - `id` (string) — ID da pasta criada, `cat_…`. - `name` (string) — Nome da pasta criada. - `slug` (string) — Slug da pasta — é o que entra na URL do feed dela. - `group_id` (string) — ID da sub-aba Geral, criada junto com a pasta. **Erros** - `400` — Nome vazio, longo demais, ou o teto de 8 pastas foi atingido. - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. **Exemplo** ```sh curl -s -XPOST https://staging.gradetv.net/api/categories -H "X-Guest-Token: $IPT" -H 'content-type: application/json' -d '{"name":"Notícias"}' ``` ### `PATCH /api/categories/:id` Renomeia uma pasta. O slug do feed acompanha o nome novo. Atenção: mudar o nome muda o slug, e portanto muda a URL do feed daquela pasta. Quem já tinha colado o link antigo no player precisa colar o novo. - **URL:** `https://staging.gradetv.net/api/categories/:id` - **Auth:** `guest` — Convidado `ipt_…` (`POST /api/guest`, em `X-Guest-Token` ou Bearer) ou, no navegador, a sessão da conta — com sessão, o dono é a conta, e escrever exige a mesma origem e o `X-CSRF-Token` de `/api/auth/bootstrap` (403 `invalid_origin`/`invalid_csrf`). Cookie da conta com a sessão vencida: 401 `session_ended`, nunca o convidado. Chave `iptk_…` retirada: 410 `api_key_retired`. **Parâmetros de caminho** - `id` (string, obrigatório) — ID da pasta, `cat_…`. **Corpo** (`application/json`) - `name` (string, obrigatório) — Novo nome da pasta, até 40 caracteres. **Exemplo de corpo** ```json { "name": "Jornalismo" } ``` **Resposta `200`** Estrutura: `Biblioteca`. - `owner` (string) — Rótulo derivado do dono desta árvore — estável enquanto o feed for o mesmo, e não reversível. NÃO é o token de convidado nem o id da conta: o token é a credencial mestra, sem revogação, e o rótulo sai derivado dele com o token do feed. - `feeds` (Feeds) — Feeds da biblioteca inteira, em quatro formatos. → ver `Feeds` em **Estruturas**. - `categories` (PastaBiblioteca[]) — As pastas do dono, na ordem que ele arrumou. → ver `PastaBiblioteca` em **Estruturas**. **Erros** - `400` — Nome vazio ou longo demais. - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. - `404` — Recurso não existe (ou não é seu — a API não distingue os dois de propósito). **Exemplo** ```sh curl -s -XPATCH https://staging.gradetv.net/api/categories/cat_123 -H "X-Guest-Token: $IPT" -H 'content-type: application/json' -d '{"name":"Jornalismo"}' ``` ### `DELETE /api/categories/:id` Apaga a pasta e tudo que está dentro dela: sub-abas e canais. - **URL:** `https://staging.gradetv.net/api/categories/:id` - **Auth:** `guest` — Convidado `ipt_…` (`POST /api/guest`, em `X-Guest-Token` ou Bearer) ou, no navegador, a sessão da conta — com sessão, o dono é a conta, e escrever exige a mesma origem e o `X-CSRF-Token` de `/api/auth/bootstrap` (403 `invalid_origin`/`invalid_csrf`). Cookie da conta com a sessão vencida: 401 `session_ended`, nunca o convidado. Chave `iptk_…` retirada: 410 `api_key_retired`. **Parâmetros de caminho** - `id` (string, obrigatório) — ID da pasta, `cat_…`. **Resposta `200`** Estrutura: `Biblioteca`. - `owner` (string) — Rótulo derivado do dono desta árvore — estável enquanto o feed for o mesmo, e não reversível. NÃO é o token de convidado nem o id da conta: o token é a credencial mestra, sem revogação, e o rótulo sai derivado dele com o token do feed. - `feeds` (Feeds) — Feeds da biblioteca inteira, em quatro formatos. → ver `Feeds` em **Estruturas**. - `categories` (PastaBiblioteca[]) — As pastas do dono, na ordem que ele arrumou. → ver `PastaBiblioteca` em **Estruturas**. **Erros** - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. - `404` — Recurso não existe (ou não é seu — a API não distingue os dois de propósito). **Exemplo** ```sh curl -s -XDELETE https://staging.gradetv.net/api/categories/cat_123 -H "X-Guest-Token: $IPT" ``` ### `POST /api/groups` Cria uma sub-aba dentro de uma pasta. Teto de 12 sub-abas por pasta. A resposta traz a biblioteca inteira já atualizada — não precisa recarregar `GET /api/library` depois. - **URL:** `https://staging.gradetv.net/api/groups` - **Auth:** `guest` — Convidado `ipt_…` (`POST /api/guest`, em `X-Guest-Token` ou Bearer) ou, no navegador, a sessão da conta — com sessão, o dono é a conta, e escrever exige a mesma origem e o `X-CSRF-Token` de `/api/auth/bootstrap` (403 `invalid_origin`/`invalid_csrf`). Cookie da conta com a sessão vencida: 401 `session_ended`, nunca o convidado. Chave `iptk_…` retirada: 410 `api_key_retired`. **Corpo** (`application/json`) - `category_id` (string, obrigatório) — Pasta que vai receber a sub-aba, `cat_…`. - `name` (string, obrigatório) — Nome da sub-aba, até 40 caracteres. **Exemplo de corpo** ```json { "category_id": "cat_…", "name": "Manchete" } ``` **Resposta `200`** Estrutura: `BibliotecaComSubAba`. - `owner` (string) — Rótulo derivado do dono desta árvore — estável enquanto o feed for o mesmo, e não reversível. NÃO é o token de convidado nem o id da conta: o token é a credencial mestra, sem revogação, e o rótulo sai derivado dele com o token do feed. - `feeds` (Feeds) — Feeds da biblioteca inteira, em quatro formatos. → ver `Feeds` em **Estruturas**. - `categories` (PastaBiblioteca[]) — As pastas do dono, na ordem que ele arrumou. → ver `PastaBiblioteca` em **Estruturas**. - `id` (string) — ID da sub-aba criada, `grp_…`. - `name` (string) — Nome da sub-aba criada. - `slug` (string) — Slug da sub-aba — entra na URL do feed dela. - `category_id` (string) — Pasta que recebeu a sub-aba. **Erros** - `400` — Nome inválido ou teto de 12 sub-abas atingido. - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. - `404` — A pasta não é sua ou não existe. **Exemplo** ```sh curl -s -XPOST https://staging.gradetv.net/api/groups -H "X-Guest-Token: $IPT" -H 'content-type: application/json' -d '{"category_id":"cat_123","name":"Manchete"}' ``` ### `DELETE /api/groups/:id` Apaga uma sub-aba e os canais que estavam nela. - **URL:** `https://staging.gradetv.net/api/groups/:id` - **Auth:** `guest` — Convidado `ipt_…` (`POST /api/guest`, em `X-Guest-Token` ou Bearer) ou, no navegador, a sessão da conta — com sessão, o dono é a conta, e escrever exige a mesma origem e o `X-CSRF-Token` de `/api/auth/bootstrap` (403 `invalid_origin`/`invalid_csrf`). Cookie da conta com a sessão vencida: 401 `session_ended`, nunca o convidado. Chave `iptk_…` retirada: 410 `api_key_retired`. **Parâmetros de caminho** - `id` (string, obrigatório) — ID da sub-aba, `grp_…`. **Resposta `200`** Estrutura: `Biblioteca`. - `owner` (string) — Rótulo derivado do dono desta árvore — estável enquanto o feed for o mesmo, e não reversível. NÃO é o token de convidado nem o id da conta: o token é a credencial mestra, sem revogação, e o rótulo sai derivado dele com o token do feed. - `feeds` (Feeds) — Feeds da biblioteca inteira, em quatro formatos. → ver `Feeds` em **Estruturas**. - `categories` (PastaBiblioteca[]) — As pastas do dono, na ordem que ele arrumou. → ver `PastaBiblioteca` em **Estruturas**. **Erros** - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. - `404` — Recurso não existe (ou não é seu — a API não distingue os dois de propósito). **Exemplo** ```sh curl -s -XDELETE https://staging.gradetv.net/api/groups/grp_123 -H "X-Guest-Token: $IPT" ``` ### `POST /api/items` Põe um canal do catálogo numa sub-aba da galeria. Teto de 40 canais por sub-aba. A resposta diz em que pasta e sub-aba o canal caiu, para a tela abrir no lugar certo. A resposta traz a biblioteca inteira já atualizada — não precisa recarregar `GET /api/library` depois. - **URL:** `https://staging.gradetv.net/api/items` - **Auth:** `guest` — Convidado `ipt_…` (`POST /api/guest`, em `X-Guest-Token` ou Bearer) ou, no navegador, a sessão da conta — com sessão, o dono é a conta, e escrever exige a mesma origem e o `X-CSRF-Token` de `/api/auth/bootstrap` (403 `invalid_origin`/`invalid_csrf`). Cookie da conta com a sessão vencida: 401 `session_ended`, nunca o convidado. Chave `iptk_…` retirada: 410 `api_key_retired`. **Corpo** (`application/json`) - `group_id` (string, obrigatório) — Sub-aba que recebe o canal, `grp_…`. - `channel_id` (string, obrigatório) — ID do canal no catálogo, ex. `GloboNews.br`. - `stream_id` (string) — Stream específico; sem ele o servidor escolhe o melhor. **Exemplo de corpo** ```json { "group_id": "grp_…", "channel_id": "GloboNews.br" } ``` **Resposta `200`** Estrutura: `BibliotecaComItem`. - `owner` (string) — Rótulo derivado do dono desta árvore — estável enquanto o feed for o mesmo, e não reversível. NÃO é o token de convidado nem o id da conta: o token é a credencial mestra, sem revogação, e o rótulo sai derivado dele com o token do feed. - `feeds` (Feeds) — Feeds da biblioteca inteira, em quatro formatos. → ver `Feeds` em **Estruturas**. - `categories` (PastaBiblioteca[]) — As pastas do dono, na ordem que ele arrumou. → ver `PastaBiblioteca` em **Estruturas**. - `id` (string) — ID do item criado, `itm_…`. - `group_id` (string) — Sub-aba que recebeu o canal. - `category_id` (string) — Pasta a que essa sub-aba pertence. **Erros** - `400` — Teto de 40 canais na sub-aba atingido. - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. - `404` — Sub-aba ou canal não encontrados. **Exemplo** ```sh curl -s -XPOST https://staging.gradetv.net/api/items -H "X-Guest-Token: $IPT" -H 'content-type: application/json' -d '{"group_id":"grp_123","channel_id":"GloboNews.br"}' ``` ### `DELETE /api/items/:id` Tira um canal da sub-aba. O canal continua no catálogo público, claro. - **URL:** `https://staging.gradetv.net/api/items/:id` - **Auth:** `guest` — Convidado `ipt_…` (`POST /api/guest`, em `X-Guest-Token` ou Bearer) ou, no navegador, a sessão da conta — com sessão, o dono é a conta, e escrever exige a mesma origem e o `X-CSRF-Token` de `/api/auth/bootstrap` (403 `invalid_origin`/`invalid_csrf`). Cookie da conta com a sessão vencida: 401 `session_ended`, nunca o convidado. Chave `iptk_…` retirada: 410 `api_key_retired`. **Parâmetros de caminho** - `id` (string, obrigatório) — ID do item na galeria, `itm_…`. **Resposta `200`** Estrutura: `Biblioteca`. - `owner` (string) — Rótulo derivado do dono desta árvore — estável enquanto o feed for o mesmo, e não reversível. NÃO é o token de convidado nem o id da conta: o token é a credencial mestra, sem revogação, e o rótulo sai derivado dele com o token do feed. - `feeds` (Feeds) — Feeds da biblioteca inteira, em quatro formatos. → ver `Feeds` em **Estruturas**. - `categories` (PastaBiblioteca[]) — As pastas do dono, na ordem que ele arrumou. → ver `PastaBiblioteca` em **Estruturas**. **Erros** - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. - `404` — Recurso não existe (ou não é seu — a API não distingue os dois de propósito). **Exemplo** ```sh curl -s -XDELETE https://staging.gradetv.net/api/items/itm_123 -H "X-Guest-Token: $IPT" ``` ### `POST /api/items/:id/play` Marca este canal como o último tocado da sub-aba — é o que devolve a pessoa onde parou. Não conta play público nem entra no histórico; para isso são `POST /api/history` e `POST /api/play-report`. - **URL:** `https://staging.gradetv.net/api/items/:id/play` - **Auth:** `guest` — Convidado `ipt_…` (`POST /api/guest`, em `X-Guest-Token` ou Bearer) ou, no navegador, a sessão da conta — com sessão, o dono é a conta, e escrever exige a mesma origem e o `X-CSRF-Token` de `/api/auth/bootstrap` (403 `invalid_origin`/`invalid_csrf`). Cookie da conta com a sessão vencida: 401 `session_ended`, nunca o convidado. Chave `iptk_…` retirada: 410 `api_key_retired`. **Parâmetros de caminho** - `id` (string, obrigatório) — ID do item na galeria, `itm_…`. **Resposta `200`** Estrutura: `Biblioteca`. - `owner` (string) — Rótulo derivado do dono desta árvore — estável enquanto o feed for o mesmo, e não reversível. NÃO é o token de convidado nem o id da conta: o token é a credencial mestra, sem revogação, e o rótulo sai derivado dele com o token do feed. - `feeds` (Feeds) — Feeds da biblioteca inteira, em quatro formatos. → ver `Feeds` em **Estruturas**. - `categories` (PastaBiblioteca[]) — As pastas do dono, na ordem que ele arrumou. → ver `PastaBiblioteca` em **Estruturas**. **Erros** - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. - `404` — Recurso não existe (ou não é seu — a API não distingue os dois de propósito). **Exemplo** ```sh curl -s -XPOST https://staging.gradetv.net/api/items/itm_123/play -H "X-Guest-Token: $IPT" ``` ## Histórico ### `GET /api/history` Canais que o dono assistiu, do mais recente para o mais antigo. Exibe somente canais disponíveis no catálogo. - **URL:** `https://staging.gradetv.net/api/history` - **Auth:** `guest` — Convidado `ipt_…` (`POST /api/guest`, em `X-Guest-Token` ou Bearer) ou, no navegador, a sessão da conta — com sessão, o dono é a conta, e escrever exige a mesma origem e o `X-CSRF-Token` de `/api/auth/bootstrap` (403 `invalid_origin`/`invalid_csrf`). Cookie da conta com a sessão vencida: 401 `session_ended`, nunca o convidado. Chave `iptk_…` retirada: 410 `api_key_retired`. **Query** - `limit` (int) — Itens por página. Acima de 50 é silenciosamente reduzido a 50. Padrão: `20`. - `offset` (int) — Quantos itens pular. Use `next_offset` da resposta anterior. Padrão: `0`. **Resposta `200`** Estrutura: `PaginaDeHistorico`. - `items` (Visita[]) — Os itens desta página, na ordem que a rota define. → ver `Visita` em **Estruturas**. - `total` (int) — Quantos itens casam com o filtro, ignorando a paginação. - `limit` (int) — Tamanho de página efetivamente aplicado (pode ser menor que o pedido). - `offset` (int) — Deslocamento aplicado nesta página. - `next_offset` (int, pode ser null) — Offset da próxima página; `null` quando esta é a última. - `next` (string, pode ser null) — URL absoluta da próxima página, com os mesmos filtros — siga até vir `null` para varrer tudo, sem remontar a querystring. - `api` (string) — URL absoluta desta própria listagem. - `max` (int) — Quantos canais o histórico guarda no máximo. **Erros** - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. **Exemplo** ```sh curl -s 'https://staging.gradetv.net/api/history?limit=10' -H "X-Guest-Token: $IPT" ``` ### `POST /api/history` Registra que o dono assistiu um canal. Repetir soma em `plays` e sobe a linha. - **URL:** `https://staging.gradetv.net/api/history` - **Auth:** `guest` — Convidado `ipt_…` (`POST /api/guest`, em `X-Guest-Token` ou Bearer) ou, no navegador, a sessão da conta — com sessão, o dono é a conta, e escrever exige a mesma origem e o `X-CSRF-Token` de `/api/auth/bootstrap` (403 `invalid_origin`/`invalid_csrf`). Cookie da conta com a sessão vencida: 401 `session_ended`, nunca o convidado. Chave `iptk_…` retirada: 410 `api_key_retired`. **Corpo** (`application/json`) - `channel_id` (string, obrigatório) — Canal assistido, ex. `GloboNews.br`. **Exemplo de corpo** ```json { "channel_id": "GloboNews.br" } ``` **Resposta `200`** - `channel_id` (string) — O canal registrado. - `plays` (int) — Quantas vezes o dono já assistiu este canal. - `last_at` (string, pode ser null) — Momento deste registro (UTC). - `api` (string) — URL absoluta do histórico. **Erros** - `400` — `channel_id` ausente ou JSON inválido. - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. **Exemplo** ```sh curl -s -XPOST https://staging.gradetv.net/api/history -H "X-Guest-Token: $IPT" -H 'content-type: application/json' -d '{"channel_id":"GloboNews.br"}' ``` ### `DELETE /api/history/:channel_id` Tira um canal do histórico do dono. - **URL:** `https://staging.gradetv.net/api/history/:channel_id` - **Auth:** `guest` — Convidado `ipt_…` (`POST /api/guest`, em `X-Guest-Token` ou Bearer) ou, no navegador, a sessão da conta — com sessão, o dono é a conta, e escrever exige a mesma origem e o `X-CSRF-Token` de `/api/auth/bootstrap` (403 `invalid_origin`/`invalid_csrf`). Cookie da conta com a sessão vencida: 401 `session_ended`, nunca o convidado. Chave `iptk_…` retirada: 410 `api_key_retired`. **Parâmetros de caminho** - `channel_id` (string, obrigatório) — Canal a remover do histórico, ex. `GloboNews.br`. **Resposta `200`** - `ok` (bool) — Sempre `true` quando removeu. - `removed` (string) — O `channel_id` que saiu. **Erros** - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. - `404` — Recurso não existe (ou não é seu — a API não distingue os dois de propósito). **Exemplo** ```sh curl -s -XDELETE https://staging.gradetv.net/api/history/GloboNews.br -H "X-Guest-Token: $IPT" ``` ### `DELETE /api/history` Limpa o histórico inteiro do dono, de uma vez. - **URL:** `https://staging.gradetv.net/api/history` - **Auth:** `guest` — Convidado `ipt_…` (`POST /api/guest`, em `X-Guest-Token` ou Bearer) ou, no navegador, a sessão da conta — com sessão, o dono é a conta, e escrever exige a mesma origem e o `X-CSRF-Token` de `/api/auth/bootstrap` (403 `invalid_origin`/`invalid_csrf`). Cookie da conta com a sessão vencida: 401 `session_ended`, nunca o convidado. Chave `iptk_…` retirada: 410 `api_key_retired`. **Resposta `200`** - `ok` (bool) — Sempre `true`. - `cleared` (bool) — Sempre `true` — o histórico foi zerado. - `total` (int) — Quantos restaram: zero. **Erros** - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. **Exemplo** ```sh curl -s -XDELETE https://staging.gradetv.net/api/history -H "X-Guest-Token: $IPT" ``` ## Saúde ### `POST /api/play-report` Relata se o canal tocou ou falhou — é o que alimenta a saúde pública do catálogo. Conta uma vez por dono, por canal, por dia e por resultado; relatar de novo devolve 200 com `reason: ja_relatado_hoje`, e não é erro. Navegador e sistema saem do User-Agent e o país da borda — mandar isso no corpo não muda nada. Sem relato, o catálogo não aprende: é assim que `GET /api/channels/:id/health` sabe distinguir canal fora do ar de canal bloqueado para você. - **URL:** `https://staging.gradetv.net/api/play-report` - **Auth:** `guest` — Convidado `ipt_…` (`POST /api/guest`, em `X-Guest-Token` ou Bearer) ou, no navegador, a sessão da conta — com sessão, o dono é a conta, e escrever exige a mesma origem e o `X-CSRF-Token` de `/api/auth/bootstrap` (403 `invalid_origin`/`invalid_csrf`). Cookie da conta com a sessão vencida: 401 `session_ended`, nunca o convidado. Chave `iptk_…` retirada: 410 `api_key_retired`. **Corpo** (`application/json`) - `channel_id` (string, obrigatório) — Canal que você tentou assistir. - `ok` (bool, obrigatório) — `true` se tocou, `false` se falhou. - `code` (string) — Por que falhou; só quando `ok` é `false`. Valores: `cors`, `geo`, `sumiu`, `codec`, `playlist`, `sem_resposta`, `protocolo`, `sem_stream`, `outro`. **Exemplo de corpo** ```json { "channel_id": "GloboNews.br", "ok": false, "code": "cors" } ``` **Resposta `200`** - `ok` (bool) — Sempre `true` — o relato foi aceito. - `counted` (bool) — `false` quando você já tinha relatado o mesmo hoje. - `reason` (string, opcional) — Por que não contou; só aparece quando `counted` é `false`. - `channel_id` (string) — O canal relatado. - `stats` (Social) — Os contadores do canal já com este relato dentro. → ver `Social` em **Estruturas**. - `health` (string) — URL do painel de saúde completo deste canal. **Erros** - `400` — `channel_id` ausente, `ok` faltando ou `code` fora da lista. - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. **Exemplo** ```sh curl -s -XPOST https://staging.gradetv.net/api/play-report -H "X-Guest-Token: $IPT" -H 'content-type: application/json' -d '{"channel_id":"GloboNews.br","ok":false,"code":"geo"}' ``` ### `GET /api/play-reports` Relatos crus, com endereço IP, para investigar um canal — só operador. Existe separado do painel público justamente porque traz endereço. A linha some depois do prazo em `retention_days`, e `/api/channels/:id/health` nunca devolve IP. - **URL:** `https://staging.gradetv.net/api/play-reports` - **Auth:** `token` — Token de operador: `METRICS_TOKEN` nas métricas e relatos crus; `CATALOGO_TOKEN` nas rotas `/api/admin/catalogo/*` da recarga (credencial exclusiva de operação). **Query** - `channel_id` (string) — Restringe a um canal. Ex.: `GloboNews.br`. - `ok` (bool) — `0` traz só as falhas — é o recorte que interessa numa investigação. Valores: `0`. - `limit` (int) — Itens por página. Acima de 50 é silenciosamente reduzido a 50. Padrão: `20`. - `offset` (int) — Quantos itens pular. Use `next_offset` da resposta anterior. Padrão: `0`. **Resposta `200`** Estrutura: `PaginaDeRelatos`. - `items` (Relato[]) — Os relatos, do mais recente para o mais antigo. → ver `Relato` em **Estruturas**. - `limit` (int) — Tamanho de página aplicado. - `offset` (int) — Deslocamento aplicado. - `next_offset` (int, pode ser null) — Offset da próxima página; `null` quando acabou. - `retention_days` (int) — Depois de quantos dias a linha é apagada. - `filters` (FiltrosRelato) — Os filtros como o servidor os entendeu. → ver `FiltrosRelato` em **Estruturas**. - `api` (string) — URL absoluta desta listagem. **Erros** - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. **Exemplo** ```sh curl -s 'https://staging.gradetv.net/api/play-reports?channel_id=GloboNews.br&ok=0' -H "Authorization: Bearer $METRICS_TOKEN" ``` ## Favoritos ### `GET /api/favorites` Canais favoritados pelo dono, do mais recente para o mais antigo. Exibe somente canais disponíveis no catálogo. - **URL:** `https://staging.gradetv.net/api/favorites` - **Auth:** `guest` — Convidado `ipt_…` (`POST /api/guest`, em `X-Guest-Token` ou Bearer) ou, no navegador, a sessão da conta — com sessão, o dono é a conta, e escrever exige a mesma origem e o `X-CSRF-Token` de `/api/auth/bootstrap` (403 `invalid_origin`/`invalid_csrf`). Cookie da conta com a sessão vencida: 401 `session_ended`, nunca o convidado. Chave `iptk_…` retirada: 410 `api_key_retired`. **Query** - `limit` (int) — Itens por página. Acima de 50 é silenciosamente reduzido a 50. Padrão: `20`. - `offset` (int) — Quantos itens pular. Use `next_offset` da resposta anterior. Padrão: `0`. **Resposta `200`** Estrutura: `PaginaDeFavoritos`. - `items` (Favorito[]) — Os itens desta página, na ordem que a rota define. → ver `Favorito` em **Estruturas**. - `total` (int) — Quantos itens casam com o filtro, ignorando a paginação. - `limit` (int) — Tamanho de página efetivamente aplicado (pode ser menor que o pedido). - `offset` (int) — Deslocamento aplicado nesta página. - `next_offset` (int, pode ser null) — Offset da próxima página; `null` quando esta é a última. - `next` (string, pode ser null) — URL absoluta da próxima página, com os mesmos filtros — siga até vir `null` para varrer tudo, sem remontar a querystring. - `api` (string) — URL absoluta desta própria listagem. - `max` (int) — Quantos favoritos o dono pode ter. **Erros** - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. **Exemplo** ```sh curl -s 'https://staging.gradetv.net/api/favorites?limit=10' -H "X-Guest-Token: $IPT" ``` ### `POST /api/favorites` Favorita um canal. Repetir não soma: o contador público conta pessoas, não cliques. - **URL:** `https://staging.gradetv.net/api/favorites` - **Auth:** `guest` — Convidado `ipt_…` (`POST /api/guest`, em `X-Guest-Token` ou Bearer) ou, no navegador, a sessão da conta — com sessão, o dono é a conta, e escrever exige a mesma origem e o `X-CSRF-Token` de `/api/auth/bootstrap` (403 `invalid_origin`/`invalid_csrf`). Cookie da conta com a sessão vencida: 401 `session_ended`, nunca o convidado. Chave `iptk_…` retirada: 410 `api_key_retired`. **Corpo** (`application/json`) - `channel_id` (string, obrigatório) — Canal a favoritar, ex. `GloboNews.br`. **Exemplo de corpo** ```json { "channel_id": "GloboNews.br" } ``` **Resposta `200`** - `ok` (bool) — Sempre `true`. - `favorited` (bool) — Sempre `true` ao fim desta chamada. - `created` (bool) — `true` se foi agora; `false` se já era favorito. - `channel_id` (string) — O canal favoritado. - `favorites` (int) — Total de pessoas que favoritaram este canal. **Erros** - `400` — `channel_id` ausente ou teto de favoritos atingido. - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. **Exemplo** ```sh curl -s -XPOST https://staging.gradetv.net/api/favorites -H "X-Guest-Token: $IPT" -H 'content-type: application/json' -d '{"channel_id":"GloboNews.br"}' ``` ### `DELETE /api/favorites/:channel_id` Desfavorita o canal e devolve o ponto ao contador público. - **URL:** `https://staging.gradetv.net/api/favorites/:channel_id` - **Auth:** `guest` — Convidado `ipt_…` (`POST /api/guest`, em `X-Guest-Token` ou Bearer) ou, no navegador, a sessão da conta — com sessão, o dono é a conta, e escrever exige a mesma origem e o `X-CSRF-Token` de `/api/auth/bootstrap` (403 `invalid_origin`/`invalid_csrf`). Cookie da conta com a sessão vencida: 401 `session_ended`, nunca o convidado. Chave `iptk_…` retirada: 410 `api_key_retired`. **Parâmetros de caminho** - `channel_id` (string, obrigatório) — Canal a desfavoritar, ex. `GloboNews.br`. **Resposta `200`** - `ok` (bool) — Sempre `true`. - `favorited` (bool) — Sempre `false` ao fim desta chamada. - `channel_id` (string) — O canal que saiu dos favoritos. - `favorites` (int) — Total de pessoas que ainda favoritam este canal. **Erros** - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. - `404` — O canal não estava nos seus favoritos. **Exemplo** ```sh curl -s -XDELETE https://staging.gradetv.net/api/favorites/GloboNews.br -H "X-Guest-Token: $IPT" ``` ## Comentários ### `GET /api/channels/:id/comments` Comentários públicos de um canal, do mais novo para o mais antigo. Com credencial na chamada, cada comentário seu vem com `mine: true` — é assim que a interface sabe o que dá para apagar. - **URL:** `https://staging.gradetv.net/api/channels/:id/comments` - **Auth:** `none` — Público, sem credencial. **Parâmetros de caminho** - `id` (string, obrigatório) — ID do canal no catálogo. Ex.: `GloboNews.br`. **Query** - `limit` (int) — Itens por página. Acima de 50 é silenciosamente reduzido a 50. Padrão: `20`. - `offset` (int) — Quantos itens pular. Use `next_offset` da resposta anterior. Padrão: `0`. **Resposta `200`** Estrutura: `PaginaDeComentarios`. - `items` (Comentario[]) — Os itens desta página, na ordem que a rota define. → ver `Comentario` em **Estruturas**. - `total` (int) — Quantos itens casam com o filtro, ignorando a paginação. - `limit` (int) — Tamanho de página efetivamente aplicado (pode ser menor que o pedido). - `offset` (int) — Deslocamento aplicado nesta página. - `next_offset` (int, pode ser null) — Offset da próxima página; `null` quando esta é a última. - `next` (string, pode ser null) — URL absoluta da próxima página, com os mesmos filtros — siga até vir `null` para varrer tudo, sem remontar a querystring. - `api` (string) — URL absoluta desta própria listagem. - `channel_id` (string) — Canal a que os comentários pertencem. - `max_length` (int) — Tamanho máximo de um comentário novo. **Erros** - `404` — Canal não existe no catálogo. **Exemplo** ```sh curl -s 'https://staging.gradetv.net/api/channels/GloboNews.br/comments?limit=10' ``` ### `POST /api/channels/:id/comments` Escreve um comentário no canal. Teto de 20 por hora por dono. Sem `author`, o apelido é gerado e fica estável para o mesmo dono — a pessoa não vira um nome diferente a cada mensagem. - **URL:** `https://staging.gradetv.net/api/channels/:id/comments` - **Auth:** `guest` — Convidado `ipt_…` (`POST /api/guest`, em `X-Guest-Token` ou Bearer) ou, no navegador, a sessão da conta — com sessão, o dono é a conta, e escrever exige a mesma origem e o `X-CSRF-Token` de `/api/auth/bootstrap` (403 `invalid_origin`/`invalid_csrf`). Cookie da conta com a sessão vencida: 401 `session_ended`, nunca o convidado. Chave `iptk_…` retirada: 410 `api_key_retired`. **Parâmetros de caminho** - `id` (string, obrigatório) — ID do canal no catálogo. Ex.: `GloboNews.br`. **Corpo** (`application/json`) - `body` (string, obrigatório) — O texto do comentário; o teto vem em `max_length` da listagem. - `author` (string) — Apelido a usar; sem ele o servidor gera um estável. **Exemplo de corpo** ```json { "body": "Só abre no formato 720p.", "author": "Wendel" } ``` **Resposta `200`** - `ok` (bool) — Sempre `true` quando o comentário entrou. - `comment` (Comentario) — O comentário criado, do jeito que ele aparece na listagem. → ver `Comentario` em **Estruturas**. **Erros** - `400` — Texto vazio ou acima de `max_length`. - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. - `404` — Canal não existe. - `429` — Passou de 20 comentários na hora. **Exemplo** ```sh curl -s -XPOST https://staging.gradetv.net/api/channels/GloboNews.br/comments -H "X-Guest-Token: $IPT" -H 'content-type: application/json' -d '{"body":"Só abre em 720p."}' ``` ### `DELETE /api/comments/:id` Apaga um comentário seu. Comentário alheio responde 404, não 403. O 404 é de propósito: a API não confirma que existe um comentário com aquele id se ele não é seu. - **URL:** `https://staging.gradetv.net/api/comments/:id` - **Auth:** `guest` — Convidado `ipt_…` (`POST /api/guest`, em `X-Guest-Token` ou Bearer) ou, no navegador, a sessão da conta — com sessão, o dono é a conta, e escrever exige a mesma origem e o `X-CSRF-Token` de `/api/auth/bootstrap` (403 `invalid_origin`/`invalid_csrf`). Cookie da conta com a sessão vencida: 401 `session_ended`, nunca o convidado. Chave `iptk_…` retirada: 410 `api_key_retired`. **Parâmetros de caminho** - `id` (string, obrigatório) — ID do comentário, vindo de `Comentario.id`. Ex.: `cm_9f3c2b1d7a4e58b0c2`. **Resposta `200`** - `ok` (bool) — Sempre `true`. - `removed` (string) — O id que saiu. - `channel_id` (string) — Canal de onde o comentário saiu. **Erros** - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. - `404` — Recurso não existe (ou não é seu — a API não distingue os dois de propósito). **Exemplo** ```sh curl -s -XDELETE https://staging.gradetv.net/api/comments/CMT_ID -H "X-Guest-Token: $IPT" ``` ## Chat ### `GET /api/chat/:channel_id/mensagens` Últimas mensagens da sala do canal, mais o endereço do WebSocket para acompanhar ao vivo. - **URL:** `https://staging.gradetv.net/api/chat/:channel_id/mensagens` - **Auth:** `none` — Público, sem credencial. **Parâmetros de caminho** - `channel_id` (string, obrigatório) — ID do canal no catálogo. Ex.: `GloboNews.br`. **Resposta `200`** - `channel_id` (string) — Canal a que a sala pertence. - `items` (MensagemChat[]) — As últimas mensagens, da mais antiga para a mais nova. → ver `MensagemChat` em **Estruturas**. - `watching` (int) — Quantas pessoas estão com a sala aberta agora. - `max_length` (int) — Tamanho máximo de uma mensagem. - `_links` (LinksChat) — Esta listagem, o WebSocket e a ficha do canal. → ver `LinksChat` em **Estruturas**. **Erros** - `404` — Canal não existe no catálogo. **Exemplo** ```sh curl -s https://staging.gradetv.net/api/chat/GloboNews.br/mensagens ``` ### `POST /api/chat/:channel_id/mensagens` Manda mensagem na sala sem abrir WebSocket. Exige o passe mensal do chat. Ler é grátis; escrever custa $0.10 por 30 dias. Sem passe válido a resposta é 402 com `accepts[]` — pague e repita a mesma chamada. Quem fala é o convidado (`X-Guest-Token`), o mesmo que o WebSocket reconhece; sem convidado, a sessão da conta. - **URL:** `https://staging.gradetv.net/api/chat/:channel_id/mensagens` - **Auth:** `guest` — Convidado `ipt_…` (`POST /api/guest`, em `X-Guest-Token` ou Bearer) ou, no navegador, a sessão da conta — com sessão, o dono é a conta, e escrever exige a mesma origem e o `X-CSRF-Token` de `/api/auth/bootstrap` (403 `invalid_origin`/`invalid_csrf`). Cookie da conta com a sessão vencida: 401 `session_ended`, nunca o convidado. Chave `iptk_…` retirada: 410 `api_key_retired`. **Parâmetros de caminho** - `channel_id` (string, obrigatório) — ID do canal no catálogo. Ex.: `GloboNews.br`. **Corpo** (`application/json`) - `body` (string, obrigatório) — O texto da mensagem, dentro de `max_length`. - `author` (string) — Apelido a usar; sem ele o servidor gera um estável. **Exemplo de corpo** ```json { "body": "alguém aí?", "author": "Wendel" } ``` **Resposta `200`** - `ok` (bool) — Sempre `true` quando a mensagem entrou. - `message` (MensagemChat) — A mensagem publicada na sala. → ver `MensagemChat` em **Estruturas**. **Erros** - `400` — Texto vazio ou longo demais. - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. - `402` — Cota estourada. A resposta traz `accepts[]` (x402, USDC na Base): pague e repita a mesma chamada com `X-PAYMENT`. - `404` — Canal não existe. **Exemplo** ```sh curl -s -XPOST https://staging.gradetv.net/api/chat/GloboNews.br/mensagens -H "X-Guest-Token: $IPT" -H "X-PAYMENT: $PAGAMENTO" -H 'content-type: application/json' -d '{"body":"alguém aí?"}' ``` ### `POST /api/chat/pass` Compra ou confirma o passe mensal do chat: $0.10 por 30 dias, via x402 ou crédito. Passe já válido devolve 200 sem cobrar de novo — dá para chamar antes de escrever, sem risco de pagar duas vezes. O passe é de quem fala no chat: o convidado de `X-Guest-Token` (é ele que o WebSocket reconhece no `hello`); sem convidado, a sessão da conta, que fala só por HTTP. Fica no registro global de compras (`direito`). - **URL:** `https://staging.gradetv.net/api/chat/pass` - **Auth:** `guest` — Convidado `ipt_…` (`POST /api/guest`, em `X-Guest-Token` ou Bearer) ou, no navegador, a sessão da conta — com sessão, o dono é a conta, e escrever exige a mesma origem e o `X-CSRF-Token` de `/api/auth/bootstrap` (403 `invalid_origin`/`invalid_csrf`). Cookie da conta com a sessão vencida: 401 `session_ended`, nunca o convidado. Chave `iptk_…` retirada: 410 `api_key_retired`. **Resposta `200`** - `ok` (bool) — Sempre `true` quando o passe está valendo ao fim da chamada. - `charged` (bool) — `true` se esta chamada cobrou; `false` se o passe já valia. - `until` (string, pode ser null) — Até quando o passe vale (UTC); `null` com o chat aberto de graça (`X402_GRATIS`). **Erros** - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. - `402` — Cota estourada. A resposta traz `accepts[]` (x402, USDC na Base): pague e repita a mesma chamada com `X-PAYMENT`. - `410` — Chave `iptk_…` (retirada): use o convidado. - `503` — `pass_unavailable` (sem registro de compras, nada cobrado) ou `pass_not_recorded` (pago, não gravado: traz o recibo). **Exemplo** ```sh curl -s -XPOST https://staging.gradetv.net/api/chat/pass -H "X-Guest-Token: $IPT" -H "X-PAYMENT: $PAGAMENTO" ``` ### `GET /api/chat/:channel_id/ws` WebSocket da sala do canal — o caminho ao vivo, com presença. Exige `Upgrade: websocket`; sem isso responde 426. O socket entra MUDO: mande `{t:'hello',token:'ipt_…'}` e depois `{t:'msg',body:'…'}`. Você recebe `{t:'pronto',autor,items[],watching}` na entrada e `{t:'msg'|'presenca',…}` durante a sessão; erro chega como `{t:'erro',code:'auth'|'vazio'|'pago'}`. - **URL:** `https://staging.gradetv.net/api/chat/:channel_id/ws` - **Auth:** `none` — Público, sem credencial. **Parâmetros de caminho** - `channel_id` (string, obrigatório) — ID do canal no catálogo. Ex.: `GloboNews.br`. **Resposta `200`** `101 Switching Protocols` e a conexão WebSocket; `426` sem o header de upgrade. **Erros** - `404` — Canal não existe no catálogo. ## Conta ### `GET /api/auth/bootstrap` Prepara o navegador para entrar na conta global. Define cookie HttpOnly restrito ao host. CSRF vinculado à sessão atual. Sem CORS. - **URL:** `https://staging.gradetv.net/api/auth/bootstrap` - **Auth:** `none` — Público, sem credencial. **Resposta `200`** - `csrf` (string) — X-CSRF-Token - `context` (string) — Opaque view context, also in X-MM-Context; not a credential / contexto opaco da vista, não é credencial. **Erros** - `400` — invalid_request - `403` — invalid_origin / invalid_csrf - `503` — auth_unavailable: a sessão anterior é preservada / the previous session is preserved ### `GET /api/account/profile` Consulta seu perfil global. Lê preferências atuais da conta. Altere-as na página da conta; produtos não mantêm perfil autoritativo separado. - **URL:** `https://staging.gradetv.net/api/account/profile` - **Auth:** `session` — Conta: cookie HttpOnly `__Host-mm-auth`, gravado ao entrar pela modal da conta ou em `/conta/global`; a escrita exige a mesma origem e `X-CSRF-Token` de `/api/auth/bootstrap`. Não há bearer para pessoas. **Resposta `200`** {profile:{name,locale,timeZone,theme,revision}} **Erros** - `401` — invalid_session - `503` — auth_unavailable **Exemplo** ```js await fetch("https://staging.gradetv.net/api/account/profile", {credentials: "same-origin"}).then(r => r.json()); ``` ### `GET /api/account/avatar` Consulta sua foto de perfil global. WebP privado de até 64 KiB, sem cache. Altere-o na conta. Não aceita ID de usuário ou URL de objeto. - **URL:** `https://staging.gradetv.net/api/account/avatar` - **Auth:** `session` — Conta: cookie HttpOnly `__Host-mm-auth`, gravado ao entrar pela modal da conta ou em `/conta/global`; a escrita exige a mesma origem e `X-CSRF-Token` de `/api/auth/bootstrap`. Não há bearer para pessoas. **Resposta `200`** image/webp; Cache-Control: no-store **Erros** - `401` — invalid_session - `404` — not_found: no photo / sem foto - `503` — auth_unavailable **Exemplo** ```js await fetch("https://staging.gradetv.net/api/account/avatar", {credentials: "same-origin"}).then(r => {if (!r.ok) throw new Error("HTTP " + r.status); return r.blob();}); ``` ### `POST /api/auth/logout` Revoga esta sessão do produto. Exige bootstrap/CSRF deste navegador e sessão. As sessões de outros produtos permanecem ativas. - **URL:** `https://staging.gradetv.net/api/auth/logout` - **Auth:** `session` — Conta: cookie HttpOnly `__Host-mm-auth`, gravado ao entrar pela modal da conta ou em `/conta/global`; a escrita exige a mesma origem e `X-CSRF-Token` de `/api/auth/bootstrap`. Não há bearer para pessoas. **Resposta `200`** - `ok` (bool) — true **Erros** - `400` — invalid_request - `403` — invalid_origin / invalid_csrf - `503` — auth_unavailable: a sessão anterior é preservada / the previous session is preserved **Exemplo** ```js // Execute no console da página do produto / Run in the product page console. (async () => { const origin = "https://staging.gradetv.net"; const {csrf} = await fetch(origin + "/api/auth/bootstrap").then(r => r.json()); const r = await fetch(origin + "/api/auth/logout", { method: "POST", credentials: "same-origin", headers: {"Content-Type": "application/json", "X-CSRF-Token": csrf}, body: JSON.stringify({}) }); if (!r.ok) throw new Error("Auth HTTP " + r.status); return r.json(); })(); ``` ### `GET /api/account/keys` Lista suas chaves de API neste produto. Nunca devolve a chave: nome, 4 últimos caracteres, organização, criação, último uso (por hora) e se ainda vale. - **URL:** `https://staging.gradetv.net/api/account/keys` - **Auth:** `session` — Conta: cookie HttpOnly `__Host-mm-auth`, gravado ao entrar pela modal da conta ou em `/conta/global`; a escrita exige a mesma origem e `X-CSRF-Token` de `/api/auth/bootstrap`. Não há bearer para pessoas. **Resposta `200`** - `keys` (object[]) — `id`, `name`, `organizationId`, `last4`, `createdAt`, `lastUsedAt`, `revokedAt`, `active` (false quando revogada ou parada por troca de senha / encerrar todos os acessos). **Erros** - `401` — invalid_session - `503` — auth_unavailable **Exemplo** ```js await fetch("https://staging.gradetv.net/api/account/keys", {credentials: "same-origin"}).then(r => r.json()); ``` ### `POST /api/account/keys/create` Cria uma chave de API para agentes e scripts. Exige entrada nos últimos 5 minutos; a de organização também exige segundo fator na sessão e o papel de dona/administradora com o produto ligado. No máximo 10 chaves vivas por conta e produto. A chave (`secret`) volta UMA vez. - **URL:** `https://staging.gradetv.net/api/account/keys/create` - **Auth:** `session` — Conta: cookie HttpOnly `__Host-mm-auth`, gravado ao entrar pela modal da conta ou em `/conta/global`; a escrita exige a mesma origem e `X-CSRF-Token` de `/api/auth/bootstrap`. Não há bearer para pessoas. **Corpo** (`application/json`) - `name` (string, obrigatório) — Até 60 caracteres. - `organizationId` (string, obrigatório) — `null` para chave da conta. **Exemplo de corpo** ```json { "name": "agent", "organizationId": null } ``` **Resposta `200`** - `key` (object) — `id`, `name`, `organizationId`, `last4`, `createdAt`. - `secret` (string) — `mmk_…`, mostrada uma vez. **Erros** - `400` — invalid_key_name / invalid_organization - `401` — invalid_session / reauth_required - `403` — invalid_origin / invalid_csrf / organization_forbidden / organization_mfa_required - `409` — key_limit_reached - `503` — auth_unavailable **Exemplo** ```js (async () => { const {csrf} = await fetch("https://staging.gradetv.net/api/auth/bootstrap").then(r => r.json()); const r = await fetch("https://staging.gradetv.net/api/account/keys/create", {method: "POST", credentials: "same-origin", headers: {"Content-Type": "application/json", "X-CSRF-Token": csrf}, body: JSON.stringify({name: "agent", organizationId: null})}); return r.json(); })(); ``` ### `POST /api/account/keys/revoke` Revoga uma das suas chaves de API. Para a chave na hora. Repetir não faz mal. - **URL:** `https://staging.gradetv.net/api/account/keys/revoke` - **Auth:** `session` — Conta: cookie HttpOnly `__Host-mm-auth`, gravado ao entrar pela modal da conta ou em `/conta/global`; a escrita exige a mesma origem e `X-CSRF-Token` de `/api/auth/bootstrap`. Não há bearer para pessoas. **Corpo** (`application/json`) - `id` (string, obrigatório) — O `id` da chave. **Exemplo de corpo** ```json { "id": "…" } ``` **Resposta `200`** - `ok` (bool) — true **Erros** - `400` — invalid_key_id - `401` — invalid_session - `403` — invalid_origin / invalid_csrf - `404` — key_not_found - `503` — auth_unavailable **Exemplo** ```js (async () => { const {csrf} = await fetch("https://staging.gradetv.net/api/auth/bootstrap").then(r => r.json()); const r = await fetch("https://staging.gradetv.net/api/account/keys/revoke", {method: "POST", credentials: "same-origin", headers: {"Content-Type": "application/json", "X-CSRF-Token": csrf}, body: JSON.stringify({id: "…"})}); return r.json(); })(); ``` ### `POST /api/auth/claim` Passa para a conta o que o convidado criou: pastas (com as sub-abas e os canais), favoritos, histórico, comentários e o link do feed. Exige bootstrap/CSRF deste navegador; a página faz isso logo depois de entrar. Só passa o que o convidado ainda tem, numa transação, e o que colide com o que a conta já tem fica com o convidado. O que o convidado comprou passa junto. Token antigo, sem assinatura, que não é dono de nada aqui é recusado. Repetir não faz mal (move zero). - **URL:** `https://staging.gradetv.net/api/auth/claim` - **Auth:** `session` — Conta: cookie HttpOnly `__Host-mm-auth`, gravado ao entrar pela modal da conta ou em `/conta/global`; a escrita exige a mesma origem e `X-CSRF-Token` de `/api/auth/bootstrap`. Não há bearer para pessoas. **Corpo** (`application/json`) - `guest_token` (string, obrigatório) — Convidado `ipt_…` deste navegador. **Exemplo de corpo** ```json { "guest_token": "ipt_…" } ``` **Resposta `200`** - `ok` (bool) — Se o convidado foi reconhecido e passou. - `claimed` (object) — `product.movidos` (linhas que mudaram de dono, por tabela), `product.apagados` (duplicatas do convidado descartadas) e `product.direitos` (compras que passaram). **Erros** - `400` — invalid_product_claim / invalid_body - `401` — invalid_session - `403` — invalid_origin / invalid_csrf - `409` — unknown_guest (em `claimed.reason` / in `claimed.reason`) - `503` — product_claim_pending / auth_unavailable **Exemplo** ```js (async () => { const {csrf} = await fetch("https://staging.gradetv.net/api/auth/bootstrap").then(r => r.json()); const r = await fetch("https://staging.gradetv.net/api/auth/claim", {method: "POST", credentials: "same-origin", headers: {"Content-Type": "application/json", "X-CSRF-Token": csrf}, body: JSON.stringify({guest_token: localStorage.getItem("ipt_guest")})}); return r.json(); })(); ``` ### `GET /api/me` A conta da sessão: e-mail e o tamanho da biblioteca dela. A conta é a da biblioteca de conta; `user.id` é o id da conta, e é ele o dono da galeria com sessão. - **URL:** `https://staging.gradetv.net/api/me` - **Auth:** `session` — Conta: cookie HttpOnly `__Host-mm-auth`, gravado ao entrar pela modal da conta ou em `/conta/global`; a escrita exige a mesma origem e `X-CSRF-Token` de `/api/auth/bootstrap`. Não há bearer para pessoas. **Resposta `200`** - `user` (Conta) — A pessoa dona da sessão. → ver `Conta` em **Estruturas**. - `profile` (object) — Perfil global: `name`, `locale`, `timeZone`, `theme`, `revision`. - `app` (string) — Nome do produto. - `resources` (Recursos) — Quantas pastas, favoritos e canais no histórico a conta tem. → ver `Recursos` em **Estruturas**. **Erros** - `401` — invalid_session - `503` — auth_unavailable **Exemplo** ```js await fetch("https://staging.gradetv.net/api/me", {credentials: "same-origin"}).then(r => r.json()); ``` ## Cobrança ### `GET /api/billing` Preços em vigor, tetos da galeria e a configuração x402 completa. É o número EM VIGOR: leia daqui antes de gastar chamada, em vez de assumir o preço da documentação. Com credencial, também diz se o passe de chat de quem fala no chat (o convidado; sem ele, a conta) está ativo. `prices.abuso_24h_usd` é o preço da porta de UA vazio/curl, hoje desligada. - **URL:** `https://staging.gradetv.net/api/billing` - **Auth:** `none` — Público, sem credencial. **Resposta `200`** Estrutura: `Billing`. - `provider` (string) — Sempre `x402` — é o único protocolo de cobrança aceito. - `mode` (string) — Modo do vendedor: `live` cobra de verdade, `dev` libera sem pagar. - `network` (string) — Rede da USDC: `base` em produção, `base-sepolia` em homologação. - `chain_id` (int) — Chain ID EVM da rede acima, para a carteira assinar na cadeia certa. - `pay_to` (string, pode ser null) — Endereço que recebe o pagamento. - `homolog` (bool) — Seam de homologação ligado: dá para fechar o loop sem gastar USDC. - `dev` (bool) — Modo de desenvolvimento: o 402 é simulado. - `dev_gate` (bool) — Há credencial de homolog configurada; não concede acesso. - `gratis` (string[], opcional) — SKUs temporariamente gratuitos. - `facilitator` (string) — URL do facilitador que verifica e liquida o pagamento. - `asset` (string) — Moeda aceita — sempre `USDC`. - `asset_address` (string) — Contrato da USDC na rede acima. - `faucet` (string, pode ser null) — Torneira de USDC de teste; só em base-sepolia. - `wallets` (object) — Links de carteiras que falam x402 (metamask, coinbase, base_app). - `product` (string) — Nome do produto que está cobrando. - `prices` (Precos) — Quanto custa cada ação paga, em USD. → ver `Precos` em **Estruturas**. - `chat` (Passe) — Seu passe de chat; sem credencial vem inativo. → ver `Passe` em **Estruturas**. - `limits` (TetosGaleria) — Quantas pastas, sub-abas e canais cabem. → ver `TetosGaleria` em **Estruturas**. **Exemplo** ```sh curl -s https://staging.gradetv.net/api/billing -H "X-Guest-Token: $IPT" ``` ### `POST /api/contact` Contato e projetos de produtores: humano usa Turnstile; agente paga $0.10 por x402 ou crédito. Projetos de produtores: consulte GET /api/producers e use contact.message_template na mensagem. O primeiro envio de agente sai sem espera, após pagamento; depois o backoff é 60s dobrando até 1 hora (`Retry-After`). O valor paga somente o envio do contato, não o serviço de transmissão. - **URL:** `https://staging.gradetv.net/api/contact` - **Auth:** `none` — Público, sem credencial. **Corpo** (`application/json`) - `name` (string, obrigatório) — Como chamar quem escreveu. - `email` (string, obrigatório) — Para onde responder. - `message` (string, obrigatório) — O que você quer dizer. - `form_ts` (int, obrigatório) — Início da composição, em milissegundos Unix: entre 2 segundos e 12 horas atrás, obrigatório também para agentes. - `cf_turnstile_response` (string) — Token Turnstile do formulário humano; ausente segue pelo pagamento de agente. - `tipo` (string) — Proposta: `patrocinio`, `parceria` ou `anuncio`. Liga os campos abaixo. - `empresa` (string) — Quem propõe, quando é empresa. - `site` (string) — Site de quem propõe. - `orcamento` (string) — `ate_100`, `100_500`, `500_2000`, `2000_mais` ou `a_combinar`. - `espaco` (string[]) — Ids de placement de `GET /api/partners`, até 6. - `duracao` (string) — Dias de exposição: `30`, `90` ou `365`. - `pagamento` (string) — `usdc`, `deposito` ou `a_combinar`. **Exemplo de corpo** ```json { "name": "…", "email": "a@example.com", "message": "…" } ``` **Resposta `200`** Estrutura: `Ok`. - `ok` (bool) — Sempre `true` — a falha vem como status 4xx/5xx, não como `ok:false`. **Erros** - `400` — Campo obrigatório faltando. - `402` — Cota estourada. A resposta traz `accepts[]` (x402, USDC na Base): pague e repita a mesma chamada com `X-PAYMENT`. - `403` — Verificação Turnstile inválida. - `429` — Backoff de agente: espere o `Retry-After`. - `502` — O provedor não aceitou o envio do e-mail; tente novamente mais tarde. - `503` — Envio indisponível por configuração de e-mail incompleta. **Exemplo** ```sh curl -s -XPOST https://staging.gradetv.net/api/contact -H "X-PAYMENT: $PAGAMENTO" -H 'content-type: application/json' -d '{"name":"Agente","email":"a@example.com","message":"[Produtores] Quero apresentar meu projeto"}' ``` ### `POST /api/visit` Ping da interface que incrementa a visita do dia. Agente não precisa chamar. Smoke não conta: `X-MM-Smoke`, User-Agent `mm-smoke` ou `smoke: true` no corpo entram como `counted: false`. - **URL:** `https://staging.gradetv.net/api/visit` - **Auth:** `none` — Público, sem credencial. **Corpo** (`application/json`) - `smoke` (bool) — `true` marca a chamada como teste e ela não entra na contagem. **Exemplo de corpo** ```json { "smoke": false } ``` **Resposta `200`** - `ok` (bool) — Sempre `true`. - `counted` (bool) — Se a visita entrou na contagem do dia. - `reason` (string, opcional) — Por que não contou, quando `counted` é `false`. **Exemplo** ```sh curl -s -XPOST https://staging.gradetv.net/api/visit -H 'content-type: application/json' -d '{"smoke":true}' ``` ### `GET /api/metrics` Métricas dos últimos 7 dias. Com o token do operador, inclui os pagamentos. Sem credencial devolve visitas e uso (`accounts` vem vazio: convidado e conta são do SDK, sem contagem por produto). Com `METRICS_TOKEN` em Bearer acrescenta `payments` — e só em Base mainnet, porque número de homologação em painel financeiro engana. - **URL:** `https://staging.gradetv.net/api/metrics` - **Auth:** `none` — Público, sem credencial. **Headers** - `Authorization` (string) — `Bearer ` para incluir o bloco financeiro. **Resposta `200`** Estrutura: `Metricas`. - `app` (string) — Nome do produto. - `today` (string) — Dia de referência (UTC, AAAA-MM-DD). - `today_visits` (int) — Visitas contadas hoje. - `today_contacts` (int, opcional) — Contatos de hoje; só com `METRICS_TOKEN`. - `today_plays` (int) — Plays contados hoje. - `today_feeds` (int) — Feeds servidos hoje. - `days` (object[]) — Um registro por dia da janela, com as contagens de cada métrica. - `usage` (object) — Uso por recurso do produto — aqui, itens na galeria. - `accounts` (object) — Vazio: convidado e conta são do SDK, sem tabela nem contagem por produto aqui. - `top_plays` (object[]) — Canais mais tocados hoje: `channel_id`, `name` e `count`. - `financeiro` (object, opcional) — `hoje_usd`, `hoje_count`, `rede`; só com `METRICS_TOKEN`. - `payments` (object, opcional) — Resumo financeiro; só com METRICS_TOKEN. **Exemplo** ```sh curl -s https://staging.gradetv.net/api/metrics -H "Authorization: Bearer $METRICS_TOKEN" ``` ## Operação ### `POST /api/erro-cliente` Relato de erro do navegador, enviado pela própria interface. Agente não precisa chamar. A interface relata sozinha erro de JS, promessa rejeitada, script/CSS que não carregou e bloqueio de CSP — uma vez por sessão — e o app relata falha tratada por `window.mmErro.relata`. O servidor valida o envelope, redige credencial, e-mail e telefone, junta repetições da mesma falha por minuto e registra um evento operacional; nada é gravado em banco. Não guarda IP, cookie, query nem o User-Agent inteiro. Responde 204 sempre, inclusive para relato inválido. - **URL:** `https://staging.gradetv.net/api/erro-cliente` - **Auth:** `none` — Público, sem credencial. **Corpo** (`application/json`) - `code` (string, obrigatório) — Código da falha, `UI-` + letras/dígitos (`UI-JS-001` erro global, `UI-PROMESSA-001`, `UI-RECURSO-001`, `UI-CSP-001`, `UI-APP-001` relato do app). - `phase` (string, obrigatório) — Fase em que quebrou, minúsculas: `global`, `promessa`, `script`, `carregar_lista`… - `path` (string) — Caminho da página aberta, sem query. - `message` (string) — Mensagem do erro, até 2000 caracteres. - `stack` (string) — Stack trace, até 12000 caracteres. - `source` (string) — Script de origem; só o caminho é guardado. - `line` (int) — Linha no script de origem. - `column` (int) — Coluna no script de origem. - `visivel` (bool) — Se a aba estava visível quando quebrou. **Exemplo de corpo** ```json { "code": "UI-APP-001", "phase": "carregar_lista", "path": "/", "message": "lista 500" } ``` **Resposta `200`** 204 sem corpo, sempre — relato inválido, repetido ou acima do teto também recebe 204. **Exemplo** ```sh curl -s -XPOST https://staging.gradetv.net/api/erro-cliente -H 'content-type: application/json' -d '{"code":"UI-APP-001","phase":"carregar_lista","path":"/","message":"lista 500"}' ``` ### `POST /api/pagamento/aberto` A interface relata que exibiu uma cobrança. Agentes não devem chamar. Relato sem corpo, da mesma origem, enviado automaticamente quando uma cobrança fica visível. Não inicia pagamento, não concede acesso e não recebe identidade ou credencial. Não grava banco por relato. Conta eventos, não pessoas únicas. O painel privado do operador separa pedidos de pagamento da API e aberturas da interface por dia UTC; os dois números podem se sobrepor. - **URL:** `https://staging.gradetv.net/api/pagamento/aberto` - **Auth:** `none` — Público, sem credencial. **Headers** - `Origin` (string, obrigatório) — A origem da página, idêntica à desta rota. - `Sec-Fetch-Site` (string, obrigatório) — `same-origin`, definido pelo navegador. - `X-MM-Payment-View` (string, obrigatório) — `1`, definido pelo componente comum. **Resposta `202`** 202 sem corpo se aceito; 204 se ignorado. Sempre no-store. ### `GET /api/admin/catalogo/estado` Contagens do catálogo no ar e do staging, mais o carimbo da última recarga. Credencial `CATALOGO_TOKEN`. Diagnóstico do estado publicado. - **URL:** `https://staging.gradetv.net/api/admin/catalogo/estado` - **Auth:** `token` — Token de operador: `METRICS_TOKEN` nas métricas e relatos crus; `CATALOGO_TOKEN` nas rotas `/api/admin/catalogo/*` da recarga (credencial exclusiva de operação). **Resposta `200`** Estrutura: `EstadoCatalogo`. - `live` (ContagemCatalogo) — O catálogo que está servindo agora. → ver `ContagemCatalogo` em **Estruturas**. - `staging` (ContagemCatalogo, pode ser null) — As tabelas `*_novo` da recarga em curso; `null` quando não há staging. → ver `ContagemCatalogo` em **Estruturas**. - `meta` (MetaCatalogo) — Carimbos da última recarga e do staging aberto. → ver `MetaCatalogo` em **Estruturas**. **Erros** - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. - `503` — `CATALOGO_TOKEN` não configurado no Worker: a recarga está desligada. **Exemplo** ```sh curl -s https://staging.gradetv.net/api/admin/catalogo/estado -H "Authorization: Bearer $CATALOGO_TOKEN" ``` ### `GET /api/admin/catalogo/slugs` Slug publicado de cada canal, paginado por id — a recarga herda para não trocar URL indexada. Keyset por `id`: repita com `apos` = `next_after` até vir `null`. - **URL:** `https://staging.gradetv.net/api/admin/catalogo/slugs` - **Auth:** `token` — Token de operador: `METRICS_TOKEN` nas métricas e relatos crus; `CATALOGO_TOKEN` nas rotas `/api/admin/catalogo/*` da recarga (credencial exclusiva de operação). **Query** - `apos` (string) — Cursor: devolve só ids maiores que este (o `next_after` da página anterior). Ex.: `GloboRJ.br`. - `limit` (int) — Tamanho da página; teto de 5000. Ex.: `5000`. **Resposta `200`** Estrutura: `SlugsPublicados`. - `items` (SlugPublicado[]) — Os pares desta página. → ver `SlugPublicado` em **Estruturas**. - `next_after` (string, pode ser null) — Passe em `apos` para a próxima página; `null` quando acabou. **Erros** - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. - `503` — Recarga desligada (sem `CATALOGO_TOKEN`). **Exemplo** ```sh curl -s "https://staging.gradetv.net/api/admin/catalogo/slugs?limit=5000" -H "Authorization: Bearer $CATALOGO_TOKEN" ``` ### `POST /api/admin/catalogo/inicio` A abertura de staging foi retirada e responde 410. Nenhum corpo reativa staging, apaga dados ou recria índices. A carga aceita somente diferença com orçamento; estado perdido exige restauração explícita. - **URL:** `https://staging.gradetv.net/api/admin/catalogo/inicio` - **Auth:** `token` — Token de operador: `METRICS_TOKEN` nas métricas e relatos crus; `CATALOGO_TOKEN` nas rotas `/api/admin/catalogo/*` da recarga (credencial exclusiva de operação). **Resposta `200`** 410 `recarga_completa_bloqueada`, após autenticação. **Erros** - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. - `410` — Sempre: protocolo retirado. **Exemplo** ```sh curl -s -XPOST https://staging.gradetv.net/api/admin/catalogo/inicio -H "Authorization: Bearer $CATALOGO_TOKEN" ``` ### `POST /api/admin/catalogo/lote` O envio de lote para staging foi retirado e responde 410. Nenhum corpo reativa staging, apaga dados ou recria índices. A carga aceita somente diferença com orçamento; estado perdido exige restauração explícita. - **URL:** `https://staging.gradetv.net/api/admin/catalogo/lote` - **Auth:** `token` — Token de operador: `METRICS_TOKEN` nas métricas e relatos crus; `CATALOGO_TOKEN` nas rotas `/api/admin/catalogo/*` da recarga (credencial exclusiva de operação). **Resposta `200`** 410 `recarga_completa_bloqueada`, após autenticação. **Erros** - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. - `410` — Sempre: protocolo retirado. **Exemplo** ```sh curl -s -XPOST https://staging.gradetv.net/api/admin/catalogo/lote -H "Authorization: Bearer $CATALOGO_TOKEN" ``` ### `POST /api/admin/guia/lote` Grava o dia de programação de até 500 canais em `guia_dia` (INSERT OR REPLACE). Publicação restrita à credencial de operação. Um canal por linha; programas fora de ordem são ordenados, sem título ou invertidos caem fora; JSON do canal acima de 64 KB recusa o pedido com o índice. - **URL:** `https://staging.gradetv.net/api/admin/guia/lote` - **Auth:** `token` — Token de operador: `METRICS_TOKEN` nas métricas e relatos crus; `CATALOGO_TOKEN` nas rotas `/api/admin/catalogo/*` da recarga (credencial exclusiva de operação). **Corpo** (`application/json`) - `day` (string, obrigatório) — Dia grabado, `YYYY-MM-DD`. - `canais` (object[], obrigatório) — `{ channel_id, site, programas: [{ inicio, fim, titulo, desc?, categoria? }] }`, instantes ISO 8601. Teto de 500 por pedido. **Exemplo de corpo** ```json { "day": "2026-09-04", "canais": [ { "channel_id": "RecordNews.br", "site": "programacao.example", "programas": [ { "inicio": "2026-09-04T09:00:00Z", "fim": "2026-09-04T10:00:00Z", "titulo": "Jornal da Record" } ] } ] } ``` **Resposta `200`** Estrutura: `GuiaLoteGravado`. - `ok` (bool) — Sempre `true`; lote recusado vem como 4xx. - `day` (string) — O dia gravado. - `gravados` (int) — Canais que entraram (INSERT OR REPLACE: reenviar não duplica). **Erros** - `400` — `day` torto, `canais` vazia ou canal inválido (o índice e o motivo vêm na mensagem). - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. - `413` — Mais de 500 canais num pedido. **Exemplo** ```sh curl -s -XPOST https://staging.gradetv.net/api/admin/guia/lote -H "Authorization: Bearer $CATALOGO_TOKEN" -H 'content-type: application/json' -d '{"day":"2026-09-04","canais":[{"channel_id":"RecordNews.br","site":"programacao.example","programas":[{"inicio":"2026-09-04T09:00:00Z","fim":"2026-09-04T10:00:00Z","titulo":"Jornal da Record"}]}]}' ``` ### `POST /api/admin/guia/fim` Registra a fonte `guia` em `catalog_meta.fontes`, ao lado das fontes do catálogo. Fecha a rodada do grabber: `fetched_at`, `itens` (canais, sites, programas…), `stale`, `ausente`. É o que `GET /api/health` mostra em `sources.guia` (limite de 2 dias) e o que o smoke cobra quando a fonte está fresca. - **URL:** `https://staging.gradetv.net/api/admin/guia/fim` - **Auth:** `token` — Token de operador: `METRICS_TOKEN` nas métricas e relatos crus; `CATALOGO_TOKEN` nas rotas `/api/admin/catalogo/*` da recarga (credencial exclusiva de operação). **Corpo** (`application/json`) - `registro` (object, obrigatório) — O mesmo formato de `fontes` da troca: `fetched_at`, `sha256` (pode ser nulo), `itens`, `stale`, `ausente`, `motivo`. **Exemplo de corpo** ```json { "registro": { "fetched_at": "2026-09-04T03:20:00Z", "sha256": null, "itens": { "canais": 64, "sites": 2 }, "stale": false, "ausente": false } } ``` **Resposta `200`** Estrutura: `GuiaRegistrada`. - `ok` (bool) — Sempre `true`. - `guia` (object) — O registro normalizado: `fetched_at`, `itens`, `stale`, `ausente`… **Erros** - `400` — Registro com campo de tipo errado. - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. **Exemplo** ```sh curl -s -XPOST https://staging.gradetv.net/api/admin/guia/fim -H "Authorization: Bearer $CATALOGO_TOKEN" -H 'content-type: application/json' -d '{"registro":{"fetched_at":"2026-09-04T03:20:00Z","itens":{"canais":64,"sites":2},"stale":false,"ausente":false}}' ``` ### `GET /api/admin/logos/mortas` Canais de TV cuja origem de logo morreu (o cron já falhou ao buscá-la) e ainda não têm override. Consulta restrita à operação, para identificar logos indisponíveis. - **URL:** `https://staging.gradetv.net/api/admin/logos/mortas` - **Auth:** `token` — Token de operador: `METRICS_TOKEN` nas métricas e relatos crus; `CATALOGO_TOKEN` nas rotas `/api/admin/catalogo/*` da recarga (credencial exclusiva de operação). **Query** - `limit` (int) — Quantos canais devolver (teto 5000). Padrão: `2000`. **Resposta `200`** Estrutura: `LogosMortas`. - `items` (object[]) — `{ id, name, alt_names, country, logo_url, fail_count, last_error }` por canal. - `limit` (int) — O teto aplicado. **Erros** - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. **Exemplo** ```sh curl -s "https://staging.gradetv.net/api/admin/logos/mortas?limit=50" -H "Authorization: Bearer $CATALOGO_TOKEN" ``` ### `POST /api/admin/logos/overrides` Atualiza o logo exibido na ficha do canal. Só https. Sobrevive à recarga (a tabela fica fora da troca). O crédito da fonte sai em `/sobre`. - **URL:** `https://staging.gradetv.net/api/admin/logos/overrides` - **Auth:** `token` — Token de operador: `METRICS_TOKEN` nas métricas e relatos crus; `CATALOGO_TOKEN` nas rotas `/api/admin/catalogo/*` da recarga (credencial exclusiva de operação). **Corpo** (`application/json`) - `fonte` (string, obrigatório) — Identificador para atribuição. - `itens` (object[], obrigatório) — `{ channel_id, url }`; teto de 500 por pedido. **Exemplo de corpo** ```json { "fonte": "licenciante", "itens": [ { "channel_id": "BandNews.br", "url": "https://logos.example/canal.png" } ] } ``` **Resposta `200`** Estrutura: `OverridesGravados`. - `ok` (bool) — Sempre `true`; pedido recusado vem como 4xx. - `fonte` (string) — A fonte gravada em cada linha. - `gravados` (int) — Overrides que entraram (INSERT OR REPLACE). **Erros** - `400` — `fonte` torta, lista vazia ou item sem `channel_id`/https (o índice vem na mensagem). - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. - `413` — Mais de 500 itens. **Exemplo** ```sh curl -s -XPOST https://staging.gradetv.net/api/admin/logos/overrides -H "Authorization: Bearer $CATALOGO_TOKEN" -H 'content-type: application/json' -d '{"fonte":"licenciante","itens":[{"channel_id":"BandNews.br","url":"https://logos.example/canal.png"}]}' ``` ### `POST /api/admin/catalogo/troca` A troca integral do catálogo foi retirada e responde 410. Nenhum corpo reativa staging, apaga dados ou recria índices. A carga aceita somente diferença com orçamento; estado perdido exige restauração explícita. - **URL:** `https://staging.gradetv.net/api/admin/catalogo/troca` - **Auth:** `token` — Token de operador: `METRICS_TOKEN` nas métricas e relatos crus; `CATALOGO_TOKEN` nas rotas `/api/admin/catalogo/*` da recarga (credencial exclusiva de operação). **Resposta `200`** 410 `recarga_completa_bloqueada`, após autenticação. **Erros** - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. - `410` — Sempre: protocolo retirado. **Exemplo** ```sh curl -s -XPOST https://staging.gradetv.net/api/admin/catalogo/troca -H "Authorization: Bearer $CATALOGO_TOKEN" ``` ### `POST /api/admin/catalogo/delta/inicio` Abre a recarga por diferença: coleira contra o catálogo no ar e a marca da execução. A mesma coleira da troca, antes de tocar em qualquer linha: zero canal, zero stream, zero tocável ou menos da metade do que está no ar recusa (409, `delta_recusado`). Sobra de staging recusa e preserva os dados. `recarga_id` fica em `catalog_meta.staging_run`. Início, lotes e fim compartilham teto de 100 mil linhas escritas/dia UTC, incluindo índices e controle; CHECK atômico recusa antes do efeito. - **URL:** `https://staging.gradetv.net/api/admin/catalogo/delta/inicio` - **Auth:** `token` — Token de operador: `METRICS_TOKEN` nas métricas e relatos crus; `CATALOGO_TOKEN` nas rotas `/api/admin/catalogo/*` da recarga (credencial exclusiva de operação). **Corpo** (`application/json`) - `recarga_id` (string, obrigatório) — Identificador desta execução (4–64 de `[A-Za-z0-9._-]`); os pedidos seguintes só valem para a recarga que abriu. - `esperado` (object, obrigatório) — Tabela → quantas linhas o catálogo INTEIRO terá depois da diferença (`channels_fts` conta ids distintos), mais `playable` (canais de TV tocáveis). É o que o fim confere contra o ar. **Exemplo de corpo** ```json { "recarga_id": "2026-09-05T18-00-00Z", "esperado": { "channels": 93857, "channels_fts": 93857, "streams": 80734, "playable": 9400 } } ``` **Resposta `200`** Estrutura: `DeltaAberto`. - `ok` (bool) — Sempre `true`; recusa vem como 409 com `problemas[]`. - `recarga_id` (string) — A execução aberta — os pedidos seguintes repetem. - `live` (ContagemCatalogo) — O catálogo no ar, antes da diferença. → ver `ContagemCatalogo` em **Estruturas**. **Erros** - `400` — `recarga_id` fora do formato ou `esperado` ausente. - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. - `409` — `delta_recusado`: a resposta lista `problemas[]` e o catálogo no ar não mudou. - `429` — `orcamento`: saldo diário insuficiente; preservar a diferença pendente e retomar em outra rodada. **Exemplo** ```sh curl -s -XPOST https://staging.gradetv.net/api/admin/catalogo/delta/inicio -H "Authorization: Bearer $CATALOGO_TOKEN" -H 'content-type: application/json' -d '{"recarga_id":"2026-09-05T18-00-00Z","esperado":{"channels":93857,"channels_fts":93857,"streams":80734,"playable":9400}}' ``` ### `POST /api/admin/catalogo/delta` Aplica até 50 linhas de UMA tabela: `upsert` para entradas, `alterar` para colunas modificadas e `remover` por chave. `INSERT … ON CONFLICT DO UPDATE` coluna a coluna — menos a chave e o `slug`, que é do produto (URL indexada não muda). `alterar` leva somente colunas modificadas, sem chave/slug ou FTS. Valores iguais não são regravados; FTS só apaga/reinsere texto alterado. Guarde o recibo integral para acompanhar o progresso. Reserva inclui índices e D1 `rows_written` devolve apenas o excedente medido. Ordem é do cliente: `channels` antes de `streams` nas entradas, o inverso nas remoções (FK). - **URL:** `https://staging.gradetv.net/api/admin/catalogo/delta` - **Auth:** `token` — Token de operador: `METRICS_TOKEN` nas métricas e relatos crus; `CATALOGO_TOKEN` nas rotas `/api/admin/catalogo/*` da recarga (credencial exclusiva de operação). **Corpo** (`application/json`) - `recarga_id` (string, obrigatório) — Identificador desta execução (4–64 de `[A-Za-z0-9._-]`); os pedidos seguintes só valem para a recarga que abriu. - `tabela` (string, obrigatório) — Uma de `channels`, `channels_fts`, `streams`, `blocklist`, `facet_countries`, `facet_categories`, `facet_languages`, `facet_subdivisions`, `facet_cities`. - `upsert` (object[]) — Linhas com as colunas da tabela; coluna faltando entra com o DEFAULT do schema. Pode ser vazia. - `alterar` (object[]) — `{ chave, valores: { coluna: valor } }`: somente colunas alteradas, com null, string ou número finito. Não aceita chave, slug ou FTS. - `remover` (string[]) — Chaves (`id`, `code` ou `channel_id`, conforme a tabela) a apagar. Pode ser vazia. **Exemplo de corpo** ```json { "recarga_id": "2026-09-05T18-00-00Z", "tabela": "facet_countries", "upsert": [ { "code": "BR", "name": "Brazil" } ], "remover": [ "XX" ] } ``` **Resposta `200`** Estrutura: `DeltaAplicado`. - `ok` (bool) — Sempre `true`; pedido recusado vem como 4xx. - `tabela` (string) — A tabela tocada. - `recebidas` (int) — Soma das linhas de `upsert` e `alterar` aceitas. - `removidas_pedidas` (int) — Chaves de remoção aceitas, inclusive as já ausentes. - `gravadas` (int) — Linhas que entraram ou foram atualizadas (na FTS, contando os ids apagados). - `removidas` (int) — Linhas apagadas por `remover`. **Erros** - `400` — Tabela desconhecida, listas ausentes ou as duas vazias, linha sem chave, chave inválida. - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. - `409` — `recarga_divergente`: a execução aberta é outra; chame `/delta/inicio`. - `413` — Mais de 50 linhas (`upsert` + `alterar` + `remover`) ou campo textual FTS acima de 4 KiB. - `429` — `orcamento`: saldo diário insuficiente; preservar a diferença pendente e retomar em outra rodada. **Exemplo** ```sh curl -s -XPOST https://staging.gradetv.net/api/admin/catalogo/delta -H "Authorization: Bearer $CATALOGO_TOKEN" -H 'content-type: application/json' -d '{"recarga_id":"2026-09-05T18-00-00Z","tabela":"facet_countries","upsert":[{"code":"BR","name":"Brazil"}],"remover":["XX"]}' ``` ### `POST /api/admin/catalogo/delta/fim` Confere o catálogo inteiro contra `esperado` e grava o carimbo (`synced_at`) — só se bateu. Contagem por tabela igual a `esperado`, um id na FTS por canal, nenhum stream órfão, nenhum canal sem slug. Diferente disso é 409 (`delta_invalido`) sem carimbo: os lotes aceitos continuam confirmados e a recarga completa permanece bloqueada. O registro por fonte (`fontes`) e o `dump_sha256` ficam em `catalog_meta`, como na troca. - **URL:** `https://staging.gradetv.net/api/admin/catalogo/delta/fim` - **Auth:** `token` — Token de operador: `METRICS_TOKEN` nas métricas e relatos crus; `CATALOGO_TOKEN` nas rotas `/api/admin/catalogo/*` da recarga (credencial exclusiva de operação). **Corpo** (`application/json`) - `recarga_id` (string, obrigatório) — Identificador desta execução (4–64 de `[A-Za-z0-9._-]`); os pedidos seguintes só valem para a recarga que abriu. - `esperado` (object, obrigatório) — Tabela → quantas linhas o catálogo INTEIRO terá depois da diferença (`channels_fts` conta ids distintos), mais `playable` (canais de TV tocáveis). É o que o fim confere contra o ar. - `dump_sha256` (string) — SHA-256 do conjunto de dados que gerou este catálogo. - `origem` (string) — Quem recarregou (ex.: `operacao`), para o relatório guardado. - `resumo` (object) — Contagens da diferença (`upsert`, `remover`, `refresh`, `iguais`), só para o relatório. **Exemplo de corpo** ```json { "recarga_id": "2026-09-05T18-00-00Z", "esperado": { "channels": 93857, "channels_fts": 93857, "streams": 80734, "playable": 9400 }, "dump_sha256": "…", "origem": "operacao", "resumo": { "upsert": 41200, "remover": 310, "refresh": 7000, "iguais": 190000 } } ``` **Resposta `200`** Estrutura: `RelatorioDelta`. - `ok` (bool) — Sempre `true`; conferência que falha vem como 409 com `problemas[]`. - `recarga_id` (string) — A recarga que entrou no ar. - `depois` (ContagemCatalogo) — O catálogo que está servindo agora. → ver `ContagemCatalogo` em **Estruturas**. - `synced_at` (string) — O `synced_at` gravado no mesmo batch do fechamento. **Erros** - `400` — `recarga_id`, `esperado` ou `fontes` inválidos. - `401` — Sem credencial ou credencial inválida. Veja a auth deste endpoint. - `409` — `recarga_divergente` (outra execução) ou `delta_invalido` — a resposta lista `problemas[]` e o carimbo não foi gravado. - `429` — `orcamento`: saldo diário insuficiente; preservar a diferença pendente e retomar em outra rodada. **Exemplo** ```sh curl -s -XPOST https://staging.gradetv.net/api/admin/catalogo/delta/fim -H "Authorization: Bearer $CATALOGO_TOKEN" -H 'content-type: application/json' -d '{"recarga_id":"2026-09-05T18-00-00Z","esperado":{"channels":2,"channels_fts":2,"streams":2,"playable":2},"origem":"manual"}' ``` ## Números públicos ### `GET /api/vitrine` Os números públicos do produto: tráfego, agentes, uso e confiabilidade, sem dinheiro. Projeção publicada de hora em hora pelo coletor da casa, arredondada a dois dígitos significativos; `null` é medição ausente, nunca zero. Cache de 15 minutos com ETag (`If-None-Match` → 304). Não há como enviar números por esta rota: a publicação é do coletor, com token próprio. - **URL:** `https://staging.gradetv.net/api/vitrine` - **Auth:** `none` — Público, sem credencial. **Resposta `200`** - `v` (int) — Versão do contrato (1). - `produto` (string) — Id do produto. - `publicado` (bool) — `false` antes da primeira publicação do coletor; aí só estas cinco chaves vêm. - `atualizado_em` (string, pode ser null) — Quando o coletor publicou (ISO 8601). - `stale` (bool) — `true` quando a projeção tem mais de 26 h. - `nome` (string, opcional) — Nome do produto. - `desde` (string, opcional, pode ser null) — Dia a partir do qual a série vale. - `fuso` (string, opcional) — Fuso dos dias (`UTC`). - `hoje` (object, opcional) — O dia de hoje: páginas por classe (pessoa, IA, bot), chamadas de API por classe, leituras das superfícies de máquina e uso do produto. - `dias` (object[], opcional) — Até 31 dias, o mais antigo primeiro: `dia`, `paginas`, `api`, `api_ia`, `maquina`, `visitantes`, `uso`. - `janelas` (object, opcional) — Somas de 7 e 30 dias (`d7`, `d30`). - `visitantes` (object, opcional) — Visitantes únicos na borda em 7 dias. - `pessoas` (object, opcional, pode ser null) — GA4 quando há: usuários, sessões, países, aparelhos e quem chegou de IA. - `agentes` (object, opcional) — Os agentes de IA e os bots que mais leem, 7 dias. - `superficies` (object, opcional) — Leituras de OKF, llms, well-known, OpenAPI e MCP em 7 dias. - `mcp` (object, opcional) — Chamadas MCP em 7 dias. - `uso` (object, opcional) — Uso real do produto por recurso: rótulo, hoje, 7 e 30 dias. - `contas` (object, opcional, pode ser null) — Usuários e convidados. - `confiabilidade` (object, opcional) — Percentual de pedidos sem 5xx em 7 dias e o build no ar. - `catalogo` (object, opcional, pode ser null) — Tamanho do acervo, quando o produto tem um. - `apoio` (object, opcional) — Impressões e cliques por patrocinador, quando houver. **Exemplo** ```sh curl -s https://staging.gradetv.net/api/vitrine ``` ### `GET /api/vitrine/operador` O documento completo do produto no painel do operador — só com o token do operador. - **URL:** `https://staging.gradetv.net/api/vitrine/operador` - **Auth:** `none` — Público, sem credencial. **Headers** - `Authorization` (string, obrigatório) — `Bearer ` — a classe operador. **Resposta `200`** - `produto` (string) — Id do produto. - `atualizado_em` (string, pode ser null) — Quando o coletor publicou. - `operador` (object, pode ser null) — O documento completo do coletor, com o que a projeção pública não carrega. **Erros** - `401` — Sem token, token errado ou token de outra classe. - `503` — Worker sem `METRICS_TOKEN` ou sem o control plane. **Exemplo** ```sh curl -s https://staging.gradetv.net/api/vitrine/operador -H "Authorization: Bearer $METRICS_TOKEN" ``` ### `GET /api/vitrine/painel` O painel da casa inteira, na forma que o gm lê — só com o token do operador. - **URL:** `https://staging.gradetv.net/api/vitrine/painel` - **Auth:** `none` — Público, sem credencial. **Headers** - `Authorization` (string, obrigatório) — `Bearer ` — a classe operador. **Resposta `200`** - `apps` (object[]) — Um documento do operador por produto, em ordem de id. - `updated` (string, opcional) — Quando o coletor fechou a rodada. - `totals` (object, opcional) — Os totais da casa. **Erros** - `401` — Sem token, token errado ou token de outra classe. - `503` — Worker sem `METRICS_TOKEN` ou sem o control plane. **Exemplo** ```sh curl -s https://staging.gradetv.net/api/vitrine/painel -H "Authorization: Bearer $METRICS_TOKEN" ``` ### `GET /api/vitrine/cursores` O cursor de erro resolvido por produto (`borda`, `cli`) — só com o token do operador. - **URL:** `https://staging.gradetv.net/api/vitrine/cursores` - **Auth:** `none` — Público, sem credencial. **Headers** - `Authorization` (string, obrigatório) — `Bearer ` — a classe operador. **Resposta `200`** JSON: `{ [produto]: { borda?: ISO, cli?: ISO } }`; vazio é `{}`. **Erros** - `401` — Sem token, token errado ou token de outra classe. - `503` — Worker sem `METRICS_TOKEN` ou sem o control plane. **Exemplo** ```sh curl -s https://staging.gradetv.net/api/vitrine/cursores -H "Authorization: Bearer $METRICS_TOKEN" ``` ## Parceria ### `GET /api/partners` Parceria, patrocínio e anúncio: os espaços do produto com preço sugerido, os números públicos ao lado e como propor. Informação sob consulta, sem ativação: espaços do catálogo da casa com preço em USD por 30 dias (90 e 365 dias com desconto), patrocinadores em vigor, recorte de `/api/vitrine`, carteira da casa (USDC na Base) e o caminho de contato — depósito, PIX ou fatura são combinados na resposta. Cache de 1 hora. - **URL:** `https://staging.gradetv.net/api/partners` - **Auth:** `none` — Público, sem credencial. **Resposta `200`** - `status` (string) — `sob_consulta`: informação e proposta, sem ativação nem cobrança. - `produto` (string) — Nome do produto. - `idioma` (string) — Idioma dos textos (o do produto). - `titulo` (string) — Título da oferta. - `descricao` (string) — Uma frase sobre a oferta. - `publico` (string) — Quem usa o produto — o público que o patrocinador alcança. - `modalidades` (object[]) — `{ id, nome }`: patrocinio, parceria, anuncio. - `placements` (object[]) — Os espaços do produto: `id`, `nome`, `onde`, `formato`, `exclusivo`, `medicao`, `price_usd_30d` (sugestão; `null` é sob consulta), `exposure[{ dias, price_usd }]` para 30, 90 e 365 dias, `disponivel`. - `house_bundle` (object) — O pacote da casa: rodapé e menção para agentes nos dez produtos, com desconto. - `parcerias` (string[]) — Ideias de parceria que o produto aceita discutir. - `current_sponsors` (object[]) — Patrocinadores em vigor: `id`, `nome`, `url`, `frase`, `espacos`, `ate`. - `stats` (object) — Recorte dos números públicos (`hoje`, `janelas`, `agentes`, `confiabilidade`) e o `link` para `/api/vitrine`; `publicado: false` antes da primeira publicação. - `payment` (object) — Como pagar: `rede`, `chain_id`, `ativo`, `pay_to`, `eip681` (a carteira da casa, quando declarada), `alternativas` e a `nota` — depósito, PIX ou fatura pela resposta. - `contact` (object) — `email`, `form_url`, `api_url` (`POST /api/contact` onde há handler), `campos` (os obrigatórios), `campos_proposta` (os opcionais da proposta, com os valores aceitos de cada um), `price_agent_usd`, `message_template`, `instructions`. - `politica` (object) — Rótulo do espaço, setores recusados, pagamento adiantado, prazos. - `_links` (object) — `self`, `stats`, `page` (`null` até a página existir), `contact`, `casa` (o mesmo caminho nos dez produtos). **Exemplo** ```sh curl -s https://staging.gradetv.net/api/partners ``` ## Crédito ### `POST /api/credito` Recarrega crédito pré-pago: paga uma vez com x402 e recebe o token que desconta em qualquer API da casa. - **URL:** `https://staging.gradetv.net/api/credito` - **Auth:** `none` — Público, sem credencial. **Query** - `usd` (int, obrigatório) — Pacote: 1, 5, 10 ou 25 dólares. **Resposta `200`** - `token` (string) — Token portador do saldo (`cred_…`). Mostrado UMA vez — não há como recuperá-lo. - `saldo_usd` (string) — Saldo creditado. - `guarde` (string) — Aviso de que o token é o portador do crédito. - `usar` (string) — Como apresentar o token nas rotas pagas. - `saldo_em` (string) — Onde consultar saldo e extrato. **Erros** - `400` — Pacote fora da lista (1, 5, 10 ou 25). - `402` — Sem pagamento — o corpo traz `accepts[]` do x402. **Exemplo** ```sh curl -s -XPOST 'https://staging.gradetv.net/api/credito?usd=10' ``` ### `GET /api/credito` Saldo e extrato do crédito — as últimas movimentações, sem devolver o token. - **URL:** `https://staging.gradetv.net/api/credito` - **Auth:** `credito` — Token de crédito em `Authorization: Bearer cred_…` (ou header `X-Credito`). Não é conta: é portador de saldo. **Resposta `200`** - `saldo_micros` (int) — Saldo em micro-dólares (1e-6 USD). - `saldo_usd` (string) — Saldo formatado. - `criado_em` (string) — Quando o crédito foi aberto. - `movimentos` (object[]) — Entradas e saídas recentes, com produto e recurso. **Erros** - `401` — Sem token ou token desconhecido. **Exemplo** ```sh curl -s https://staging.gradetv.net/api/credito -H 'Authorization: Bearer cred_…' ``` ## Estruturas ### `Saude` A resposta de `/api/health`: liveness, o commit publicado e a idade do catálogo. - `ok` (bool) — Sempre `true` quando o Worker responde. - `app` (string) — Nome do produto. - `build` (string) — Commit publicado; o CI passa o SHA curto no deploy. - `ts` (string) — Momento da resposta (UTC, ISO-8601). - `catalog` (FrescorCatalogo) — Idade do catálogo: `synced_at` da última recarga e se passou do limite de 2 dias (o smoke reprova). → ver `FrescorCatalogo` em **Estruturas**. - `sources` (object) — Uma entrada por referência do catálogo: `fetched_at`, `sha256`, `itens`, `age_hours`, `limit_days`, `stale` (passou do limite ou a recarga usou o snapshot anterior), `ausente` (saiu sem a fonte). Só o tronco velho reprova o smoke. ### `PaginaDeCanais` A resposta da busca de canais. Não usa o envelope `Pagina` porque troca `api` por `facets` e `filters`. - `items` (Canal[]) — Os canais desta página, na ordem pedida (`sort`): nome, ou melhor saúde medida primeiro. → ver `Canal` em **Estruturas**. - `total` (int) — Canais que casam com o filtro, ignorando a paginação. - `limit` (int) — Tamanho de página aplicado (teto de 50). - `offset` (int) — Deslocamento aplicado nesta página. - `next_offset` (int, pode ser null) — Offset da próxima página; `null` quando acabou. - `facets` (FacetasCanal) — Contagem por categoria DENTRO do filtro atual — serve para montar o menu lateral. → ver `FacetasCanal` em **Estruturas**. - `filters` (FiltrosCanal) — Os filtros como o servidor os entendeu, já normalizados. → ver `FiltrosCanal` em **Estruturas**. ### `CanalCompleto` A ficha de um canal: tudo que a busca traz, mais os streams e os links relacionados. - `id` (string) — ID estável do catálogo, ex. `GloboNews.br`. É a chave em toda a API. - `name` (string) — Nome de exibição do canal. - `alt_names` (string[]) — Outros nomes pelos quais o canal é conhecido. - `country` (string, pode ser null) — País de origem, ISO 3166-1 alpha-2. - `categories` (string[]) — IDs de categoria do catálogo, ex. `news`, `sports`. - `category_labels` (string[]) — Os mesmos IDs já traduzidos para exibição. - `languages` (string[]) — Idiomas do canal, ISO 639-3. - `language_labels` (string[]) — Nomes dos idiomas acima, quando conhecidos. - `logo_url` (string, pode ser null) — Logo servido por nós (variante ≤256px), não a origem. - `website` (string, pode ser null) — Site oficial do canal. - `playable_hint` (bool, opcional) — Se a última verificação achou stream utilizável. - `slug` (string, pode ser null) — Identificador legível; é id de API, não URL pública. - `network` (string, pode ser null) — Rede/emissora a que o canal pertence. - `owners` (string[]) — Quem opera o canal, conforme o cadastro. - `launched` (string, pode ser null) — Data de lançamento (AAAA-MM-DD). - `replaced_by` (string, pode ser null) — ID do canal que substituiu este, se foi descontinuado. - `feed_name` (string, pode ser null) — Nome do feed quando o canal tem mais de um. - `feed_format` (string, pode ser null) — Formato do feed declarado pela fonte. - `timezones` (string[]) — Fusos em que o canal transmite. - `broadcast_area` (string[]) — Área de cobertura, em códigos do catálogo. - `quality` (string, opcional) — Melhor qualidade conhecida, ex. `1080p`. - `has_guide` (bool) — Se existe grade de programação (EPG) para este canal. - `guide_site` (string, pode ser null) — Site de onde a grade vem. - `guide_lang` (string, pode ser null) — Idioma da grade de programação. - `subdivision` (string, pode ser null) — Estado/província, código do catálogo. - `city` (string, pode ser null) — Cidade, código do catálogo. - `kind` (string) — `tv` ou `radio` (estação de rádio). - `radio` (Radio, opcional) — Só em estação de rádio: tags, votos, cliques, codec, bitrate e localização. → ver `Radio` em **Estruturas**. - `origem` (string) — Identificador de procedência; créditos e licenças em `/sobre`. - `guide_now` (GuiaAgora, opcional, pode ser null) — Só na ficha: agora e a seguir na programação de hoje, grabada por nós; `null` sem guia fresca. → ver `GuiaAgora` em **Estruturas**. - `health_ext` (SaudeMedida, pode ser null) — Saúde medida por terceiro; `null` quando o canal não foi medido. → ver `SaudeMedida` em **Estruturas**. - `social` (Social, opcional) — Contadores da comunidade; ausente nas páginas HTML de SEO. → ver `Social` em **Estruturas**. - `api` (string) — URL absoluta da ficha deste canal. - `streams` (Stream[]) — Transmissões conhecidas, com a URL já apontando para o nosso hop. → ver `Stream` em **Estruturas**. - `_links` (LinksCanal) — Esta ficha, a mesma coisa na interface humana e o índice da API. → ver `LinksCanal` em **Estruturas**. ### `SaudeCanal` Por que um canal falha e para quem — o que separa 'está fora do ar' de 'está bloqueado no seu país'. - `channel_id` (string) — Canal a que esta saúde se refere. - `plays` (int) — Relatos de sucesso, no mundo todo. - `fails` (int) — Relatos de falha, no mundo todo. - `favorites` (int) — Quantas pessoas favoritaram o canal. - `comments` (int) — Comentários públicos no canal. - `health` (int, pode ser null) — Percentual de sucesso; `null` com menos de `min_relatos`. - `last_ok_at` (string, pode ser null) — Último relato de sucesso (UTC). - `last_fail_at` (string, pode ser null) — Último relato de falha (UTC). - `last_fail_code` (string, pode ser null) — Código da falha mais recente. - `min_relatos` (int) — Quantos relatos são necessários antes de calcular `health`. - `reasons` (MotivoFalha[]) — Por que falhou, do motivo mais comum para o menos. → ver `MotivoFalha` em **Estruturas**. - `environments` (Ambiente[]) — O mesmo canal por navegador, sistema e país. → ver `Ambiente` em **Estruturas**. - `your_environment` (Ambiente) — O recorte de QUEM ESTÁ CHAMANDO, deduzido do User-Agent e da borda. → ver `Ambiente` em **Estruturas**. - `regions` (RegiaoSaude[]) — O mesmo canal agregado por PAÍS — onde falha e onde funciona. → ver `RegiaoSaude` em **Estruturas**. - `geo` (GeoCanal) — Veredito do bloqueio: geo-restrito (falha numas regiões, funciona noutras) ou fora do ar (falha em todas). → ver `GeoCanal` em **Estruturas**. - `latency` (LatenciaPais[]) — Quão rápido a playlist abre, por país — medido no hop `/api/s/:id`. → ver `LatenciaPais` em **Estruturas**. - `your_country` (RegiaoSaude) — O recorte do PAÍS de quem está chamando, com a latência da borda dele. → ver `RegiaoSaude` em **Estruturas**. - `pra_voce` (string) — Veredito final para quem está chamando: `geo_bloqueado`, `lenta`, `instavel`, `boa` ou `sem_dado`. - `codes` (string[]) — Todos os códigos de falha que o produto reconhece. - `_links` (LinksSaude) — Esta saúde, o canal e onde relatar. → ver `LinksSaude` em **Estruturas**. ### `GuiaDoDia` A programação disponível para o dia de um canal. - `channel_id` (string) — ID do canal no catálogo. - `day` (string) — Dia grabado, YYYY-MM-DD. - `site` (string, pode ser null) — Site de programação de origem. - `agora` (Programa, pode ser null) — O programa no ar neste instante. → ver `Programa` em **Estruturas**. - `a_seguir` (Programa, pode ser null) — O próximo programa. → ver `Programa` em **Estruturas**. - `programas` (Programa[]) — Todos os programas do dia, em ordem (até 200). → ver `Programa` em **Estruturas**. ### `Geo` País e idioma sugeridos para quem está chamando. - `country` (string) — País a usar; cai em `BR` quando a borda não informa. - `detected` (string, pode ser null) — O que a borda realmente detectou; `null` se nada. - `language` (string) — Idioma a usar; cai em `por` sem detecção. - `language_detected` (string, pode ser null) — Idioma realmente detectado. - `source` (string) — `cf` quando veio da borda, `fallback` quando é o padrão. - `api` (string) — URL absoluta desta rota. ### `Lista` Coleção pequena e completa — faceta de catálogo, que cabe inteira numa resposta. - `items` (Pais[]) — Todos os itens; estas rotas não paginam. → ver `Pais` em **Estruturas**. ### `Pais` País com canal tocável no catálogo. - `code` (string) — ISO 3166-1 alpha-2, ex. `BR`. - `name` (string) — Nome do país. - `count` (int) — Canais tocáveis deste país. - `flag` (string) — URL do SVG da bandeira, servido por nós. ### `Lista` Coleção pequena e completa — faceta de catálogo, que cabe inteira numa resposta. - `items` (Tag[]) — Todos os itens; estas rotas não paginam. → ver `Tag` em **Estruturas**. ### `Tag` Tag de estação de rádio, vocabulário livre do catálogo, com a contagem no recorte. - `id` (string) — A tag como está na fonte, em minúsculas (ex. `mpb`). - `name` (string) — O mesmo texto, para exibir. - `count` (int) — Estações tocáveis com esta tag no recorte pedido. ### `Lista` Coleção pequena e completa — faceta de catálogo, que cabe inteira numa resposta. - `items` (Categoria[]) — Todos os itens; estas rotas não paginam. → ver `Categoria` em **Estruturas**. ### `Categoria` Categoria do vocabulário do catálogo, sem contagem. - `id` (string) — ID da categoria, ex. `movies`. - `name` (string) — Nome para exibição. - `description` (string, pode ser null) — O que a categoria abrange, segundo a fonte. - `icon` (string, pode ser null) — Nome do ícone usado na interface. ### `Lista` Coleção pequena e completa — faceta de catálogo, que cabe inteira numa resposta. - `items` (Idioma[]) — Todos os itens; estas rotas não paginam. → ver `Idioma` em **Estruturas**. ### `Idioma` Idioma com canal tocável. - `code` (string) — ISO 639-3, ex. `por`. - `name` (string) — Nome do idioma. - `count` (int) — Canais tocáveis neste idioma. ### `Lista` Coleção pequena e completa — faceta de catálogo, que cabe inteira numa resposta. - `items` (Rede[]) — Todos os itens; estas rotas não paginam. → ver `Rede` em **Estruturas**. ### `Rede` Rede/emissora com canal tocável. - `name` (string) — Nome da rede, ex. `Globo`. - `count` (int) — Canais tocáveis desta rede. ### `Lista` Coleção pequena e completa — faceta de catálogo, que cabe inteira numa resposta. - `items` (Qualidade[]) — Todos os itens; estas rotas não paginam. → ver `Qualidade` em **Estruturas**. ### `Qualidade` Qualidade distinta encontrada nos streams. - `id` (string) — A qualidade em si, ex. `1080p`. - `name` (string) — Mesmo valor, para exibição. - `count` (int) — Streams nesta qualidade. ### `Lista` Coleção pequena e completa — faceta de catálogo, que cabe inteira numa resposta. - `items` (Subdivisao[]) — Todos os itens; estas rotas não paginam. → ver `Subdivisao` em **Estruturas**. ### `Subdivisao` Estado/província com canal tocável. - `code` (string) — Código do catálogo, ex. `BR-SP`. - `country` (string) — País a que pertence, alpha-2. - `name` (string) — Nome da subdivisão. - `count` (int) — Canais tocáveis nela. ### `Lista` Coleção pequena e completa — faceta de catálogo, que cabe inteira numa resposta. - `items` (Cidade[]) — Todos os itens; estas rotas não paginam. → ver `Cidade` em **Estruturas**. ### `Cidade` Cidade com canal tocável. - `code` (string) — Código do catálogo. - `country` (string) — País, alpha-2. - `subdivision` (string, pode ser null) — Subdivisão a que a cidade pertence. - `name` (string) — Nome da cidade. - `count` (int) — Canais tocáveis nela. ### `Biblioteca` A galeria inteira do dono: pastas, sub-abas, canais e as URLs de feed de cada nível. - `owner` (string) — Rótulo derivado do dono desta árvore — estável enquanto o feed for o mesmo, e não reversível. NÃO é o token de convidado nem o id da conta: o token é a credencial mestra, sem revogação, e o rótulo sai derivado dele com o token do feed. - `feeds` (Feeds) — Feeds da biblioteca inteira, em quatro formatos. → ver `Feeds` em **Estruturas**. - `categories` (PastaBiblioteca[]) — As pastas do dono, na ordem que ele arrumou. → ver `PastaBiblioteca` em **Estruturas**. ### `BibliotecaComPasta` A biblioteca inteira mais os ids da pasta que acabou de ser criada. - `owner` (string) — Rótulo derivado do dono desta árvore — estável enquanto o feed for o mesmo, e não reversível. NÃO é o token de convidado nem o id da conta: o token é a credencial mestra, sem revogação, e o rótulo sai derivado dele com o token do feed. - `feeds` (Feeds) — Feeds da biblioteca inteira, em quatro formatos. → ver `Feeds` em **Estruturas**. - `categories` (PastaBiblioteca[]) — As pastas do dono, na ordem que ele arrumou. → ver `PastaBiblioteca` em **Estruturas**. - `id` (string) — ID da pasta criada, `cat_…`. - `name` (string) — Nome da pasta criada. - `slug` (string) — Slug da pasta — é o que entra na URL do feed dela. - `group_id` (string) — ID da sub-aba Geral, criada junto com a pasta. ### `BibliotecaComSubAba` A biblioteca inteira mais os ids da sub-aba que acabou de ser criada. - `owner` (string) — Rótulo derivado do dono desta árvore — estável enquanto o feed for o mesmo, e não reversível. NÃO é o token de convidado nem o id da conta: o token é a credencial mestra, sem revogação, e o rótulo sai derivado dele com o token do feed. - `feeds` (Feeds) — Feeds da biblioteca inteira, em quatro formatos. → ver `Feeds` em **Estruturas**. - `categories` (PastaBiblioteca[]) — As pastas do dono, na ordem que ele arrumou. → ver `PastaBiblioteca` em **Estruturas**. - `id` (string) — ID da sub-aba criada, `grp_…`. - `name` (string) — Nome da sub-aba criada. - `slug` (string) — Slug da sub-aba — entra na URL do feed dela. - `category_id` (string) — Pasta que recebeu a sub-aba. ### `BibliotecaComItem` A biblioteca inteira mais onde o canal caiu — para a tela abrir na sub-aba certa. - `owner` (string) — Rótulo derivado do dono desta árvore — estável enquanto o feed for o mesmo, e não reversível. NÃO é o token de convidado nem o id da conta: o token é a credencial mestra, sem revogação, e o rótulo sai derivado dele com o token do feed. - `feeds` (Feeds) — Feeds da biblioteca inteira, em quatro formatos. → ver `Feeds` em **Estruturas**. - `categories` (PastaBiblioteca[]) — As pastas do dono, na ordem que ele arrumou. → ver `PastaBiblioteca` em **Estruturas**. - `id` (string) — ID do item criado, `itm_…`. - `group_id` (string) — Sub-aba que recebeu o canal. - `category_id` (string) — Pasta a que essa sub-aba pertence. ### `PaginaDeHistorico` Página do histórico do dono. `max` é o teto de linhas guardadas — passou disso, a mais antiga sai. - `items` (Visita[]) — Os itens desta página, na ordem que a rota define. → ver `Visita` em **Estruturas**. - `total` (int) — Quantos itens casam com o filtro, ignorando a paginação. - `limit` (int) — Tamanho de página efetivamente aplicado (pode ser menor que o pedido). - `offset` (int) — Deslocamento aplicado nesta página. - `next_offset` (int, pode ser null) — Offset da próxima página; `null` quando esta é a última. - `next` (string, pode ser null) — URL absoluta da próxima página, com os mesmos filtros — siga até vir `null` para varrer tudo, sem remontar a querystring. - `api` (string) — URL absoluta desta própria listagem. - `max` (int) — Quantos canais o histórico guarda no máximo. ### `Social` Contadores da comunidade sobre um canal, incluindo o recorte do ambiente e do país de quem pediu. - `plays` (int) — Relatos de que o canal tocou. - `fails` (int) — Relatos de que o canal falhou. - `favorites` (int) — Quantas pessoas favoritaram — conta pessoas, não cliques. - `comments` (int) — Comentários públicos no canal. - `last_fail_code` (string, pode ser null) — Código da falha mais recente relatada. - `health` (int, pode ser null) — Percentual de sucesso; `null` enquanto houver menos de 3 relatos. - `your_plays` (int) — Relatos de sucesso no SEU navegador, sistema e país. - `your_fails` (int) — Relatos de falha no seu ambiente — é o que distingue 'fora do ar' de 'bloqueado para você'. - `your_fail_code` (string, pode ser null) — Código da última falha no seu ambiente. - `your_country_ok` (int) — Relatos de sucesso no SEU país, sem quebrar por navegador/SO. - `your_country_fail` (int) — Relatos de falha no seu país. - `your_geo_ok` (bool, pode ser null) — `false` = todas as tentativas relatadas no seu país falharam (suspeita de geo-bloqueio); `null` sem relato suficiente. - `your_latency_ms` (int, pode ser null) — Latência média da playlist medida no hop, na borda do seu país; `null` sem medição. - `your_latency_grade` (string, pode ser null) — `otima`, `boa`, `lenta` ou `ruim`; `null` sem medição. ### `PaginaDeRelatos` Página de relatos crus. Não tem `total` de propósito: contar a tabela inteira a cada consulta de investigação sai caro e não muda a decisão de quem investiga. - `items` (Relato[]) — Os relatos, do mais recente para o mais antigo. → ver `Relato` em **Estruturas**. - `limit` (int) — Tamanho de página aplicado. - `offset` (int) — Deslocamento aplicado. - `next_offset` (int, pode ser null) — Offset da próxima página; `null` quando acabou. - `retention_days` (int) — Depois de quantos dias a linha é apagada. - `filters` (FiltrosRelato) — Os filtros como o servidor os entendeu. → ver `FiltrosRelato` em **Estruturas**. - `api` (string) — URL absoluta desta listagem. ### `PaginaDeFavoritos` Página dos favoritos do dono, com o teto de quantos cabem. - `items` (Favorito[]) — Os itens desta página, na ordem que a rota define. → ver `Favorito` em **Estruturas**. - `total` (int) — Quantos itens casam com o filtro, ignorando a paginação. - `limit` (int) — Tamanho de página efetivamente aplicado (pode ser menor que o pedido). - `offset` (int) — Deslocamento aplicado nesta página. - `next_offset` (int, pode ser null) — Offset da próxima página; `null` quando esta é a última. - `next` (string, pode ser null) — URL absoluta da próxima página, com os mesmos filtros — siga até vir `null` para varrer tudo, sem remontar a querystring. - `api` (string) — URL absoluta desta própria listagem. - `max` (int) — Quantos favoritos o dono pode ter. ### `PaginaDeComentarios` Página de comentários de um canal, com o teto de tamanho de quem for escrever a seguir. - `items` (Comentario[]) — Os itens desta página, na ordem que a rota define. → ver `Comentario` em **Estruturas**. - `total` (int) — Quantos itens casam com o filtro, ignorando a paginação. - `limit` (int) — Tamanho de página efetivamente aplicado (pode ser menor que o pedido). - `offset` (int) — Deslocamento aplicado nesta página. - `next_offset` (int, pode ser null) — Offset da próxima página; `null` quando esta é a última. - `next` (string, pode ser null) — URL absoluta da próxima página, com os mesmos filtros — siga até vir `null` para varrer tudo, sem remontar a querystring. - `api` (string) — URL absoluta desta própria listagem. - `channel_id` (string) — Canal a que os comentários pertencem. - `max_length` (int) — Tamanho máximo de um comentário novo. ### `Comentario` Comentário público num canal. - `id` (string) — ID do comentário, para apagar. - `channel_id` (string) — Canal em que o comentário foi escrito. - `author` (string) — Apelido de quem escreveu; `Visitante` quando não informado. - `body` (string) — O texto do comentário. - `created_at` (string) — Quando foi escrito (UTC, com milissegundos). - `mine` (bool) — `true` se é seu — só você pode apagar. - `api` (string) — URL absoluta deste comentário. ### `MensagemChat` Mensagem da sala de chat de um canal. - `id` (string) — ID da mensagem dentro da sala. - `autor` (string) — Apelido de quem mandou — estável por dono, gerado se não informado. - `body` (string) — O texto da mensagem. - `at` (string) — Quando foi mandada (UTC). ### `LinksChat` Endereços da sala — incluindo o WebSocket, que é o caminho ao vivo. - `self` (string) — Esta mesma listagem por HTTP. - `websocket` (string) — URL `wss://` da sala; mande `{t:'hello',token}` antes de falar. - `channel` (string) — Ficha do canal a que a sala pertence. ### `Conta` A pessoa por trás da sessão. - `id` (string) — ID da conta. - `email` (string) — E-mail confirmado por código. ### `Recursos` O tamanho da biblioteca do dono — para o agente saber o que vai encontrar antes de buscar. - `categories` (int) — Quantas pastas o dono tem. - `favorites` (int) — Quantos canais ele favoritou. - `history` (int) — Quantos canais estão no histórico. ### `Billing` Tudo que decide se uma chamada vai custar: a configuração x402, os preços do produto, o seu passe e os tetos da galeria. - `provider` (string) — Sempre `x402` — é o único protocolo de cobrança aceito. - `mode` (string) — Modo do vendedor: `live` cobra de verdade, `dev` libera sem pagar. - `network` (string) — Rede da USDC: `base` em produção, `base-sepolia` em homologação. - `chain_id` (int) — Chain ID EVM da rede acima, para a carteira assinar na cadeia certa. - `pay_to` (string, pode ser null) — Endereço que recebe o pagamento. - `homolog` (bool) — Seam de homologação ligado: dá para fechar o loop sem gastar USDC. - `dev` (bool) — Modo de desenvolvimento: o 402 é simulado. - `dev_gate` (bool) — Há credencial de homolog configurada; não concede acesso. - `gratis` (string[], opcional) — SKUs temporariamente gratuitos. - `facilitator` (string) — URL do facilitador que verifica e liquida o pagamento. - `asset` (string) — Moeda aceita — sempre `USDC`. - `asset_address` (string) — Contrato da USDC na rede acima. - `faucet` (string, pode ser null) — Torneira de USDC de teste; só em base-sepolia. - `wallets` (object) — Links de carteiras que falam x402 (metamask, coinbase, base_app). - `product` (string) — Nome do produto que está cobrando. - `prices` (Precos) — Quanto custa cada ação paga, em USD. → ver `Precos` em **Estruturas**. - `chat` (Passe) — Seu passe de chat; sem credencial vem inativo. → ver `Passe` em **Estruturas**. - `limits` (TetosGaleria) — Quantas pastas, sub-abas e canais cabem. → ver `TetosGaleria` em **Estruturas**. ### `Ok` Confirmação de escrita que não tem corpo próprio a devolver. - `ok` (bool) — Sempre `true` — a falha vem como status 4xx/5xx, não como `ok:false`. ### `Metricas` Painel de 7 dias. O bloco `payments` só aparece com o token do operador e só em Base mainnet. - `app` (string) — Nome do produto. - `today` (string) — Dia de referência (UTC, AAAA-MM-DD). - `today_visits` (int) — Visitas contadas hoje. - `today_contacts` (int, opcional) — Contatos de hoje; só com `METRICS_TOKEN`. - `today_plays` (int) — Plays contados hoje. - `today_feeds` (int) — Feeds servidos hoje. - `days` (object[]) — Um registro por dia da janela, com as contagens de cada métrica. - `usage` (object) — Uso por recurso do produto — aqui, itens na galeria. - `accounts` (object) — Vazio: convidado e conta são do SDK, sem tabela nem contagem por produto aqui. - `top_plays` (object[]) — Canais mais tocados hoje: `channel_id`, `name` e `count`. - `financeiro` (object, opcional) — `hoje_usd`, `hoje_count`, `rede`; só com `METRICS_TOKEN`. - `payments` (object, opcional) — Resumo financeiro; só com METRICS_TOKEN. ### `EstadoCatalogo` O retrato que o serviço de recarga lê antes de começar e depois de trocar. - `live` (ContagemCatalogo) — O catálogo que está servindo agora. → ver `ContagemCatalogo` em **Estruturas**. - `staging` (ContagemCatalogo, pode ser null) — As tabelas `*_novo` da recarga em curso; `null` quando não há staging. → ver `ContagemCatalogo` em **Estruturas**. - `meta` (MetaCatalogo) — Carimbos da última recarga e do staging aberto. → ver `MetaCatalogo` em **Estruturas**. ### `SlugsPublicados` Página de slugs publicados, em ordem de id. - `items` (SlugPublicado[]) — Os pares desta página. → ver `SlugPublicado` em **Estruturas**. - `next_after` (string, pode ser null) — Passe em `apos` para a próxima página; `null` quando acabou. ### `GuiaLoteGravado` Resultado de um lote da guia. - `ok` (bool) — Sempre `true`; lote recusado vem como 4xx. - `day` (string) — O dia gravado. - `gravados` (int) — Canais que entraram (INSERT OR REPLACE: reenviar não duplica). ### `GuiaRegistrada` A fonte `guia` como ficou em `catalog_meta.fontes`. - `ok` (bool) — Sempre `true`. - `guia` (object) — O registro normalizado: `fetched_at`, `itens`, `stale`, `ausente`… ### `LogosMortas` Canais com origem de logo morta, para o script de overrides casar. - `items` (object[]) — `{ id, name, alt_names, country, logo_url, fail_count, last_error }` por canal. - `limit` (int) — O teto aplicado. ### `OverridesGravados` Resultado da gravação de overrides de logo. - `ok` (bool) — Sempre `true`; pedido recusado vem como 4xx. - `fonte` (string) — A fonte gravada em cada linha. - `gravados` (int) — Overrides que entraram (INSERT OR REPLACE). ### `DeltaAberto` A recarga por diferença passou pela coleira e está marcada; nada mudou no ar ainda. - `ok` (bool) — Sempre `true`; recusa vem como 409 com `problemas[]`. - `recarga_id` (string) — A execução aberta — os pedidos seguintes repetem. - `live` (ContagemCatalogo) — O catálogo no ar, antes da diferença. → ver `ContagemCatalogo` em **Estruturas**. ### `DeltaAplicado` Um pedido da diferença entrou no catálogo vivo, num batch. - `ok` (bool) — Sempre `true`; pedido recusado vem como 4xx. - `tabela` (string) — A tabela tocada. - `recebidas` (int) — Soma das linhas de `upsert` e `alterar` aceitas. - `removidas_pedidas` (int) — Chaves de remoção aceitas, inclusive as já ausentes. - `gravadas` (int) — Linhas que entraram ou foram atualizadas (na FTS, contando os ids apagados). - `removidas` (int) — Linhas apagadas por `remover`. ### `RelatorioDelta` A diferença fechou: o catálogo inteiro bateu com `esperado` e o carimbo foi gravado. - `ok` (bool) — Sempre `true`; conferência que falha vem como 409 com `problemas[]`. - `recarga_id` (string) — A recarga que entrou no ar. - `depois` (ContagemCatalogo) — O catálogo que está servindo agora. → ver `ContagemCatalogo` em **Estruturas**. - `synced_at` (string) — O `synced_at` gravado no mesmo batch do fechamento. ### `PaymentQuota` - `free` (PaymentFree[]) — Free allowances and their windows. → ver `PaymentFree` em **Estruturas**. - `paid` (PaymentPrice[]) — List prices in USD. The operation's 402 is the payable quote. → ver `PaymentPrice` em **Estruturas**. - `how_to_pay` (string) — Payment instructions and availability restrictions. - `live` (string, pode ser null) — Authoritative product quota endpoint. - `free_now` (string[], opcional) — SKUs temporarily free despite their list price. - `trial` (PaymentTrial, opcional) — Registration trial, when offered. → ver `PaymentTrial` em **Estruturas**. ### `FrescorCatalogo` Idade do catálogo, como sai em `/api/health`. - `synced_at` (string, pode ser null) — Instante (ISO-8601) da última recarga que entrou no ar; `null` se nunca. - `age_hours` (int, pode ser null) — Horas inteiras desde `synced_at`; `null` sem carimbo. - `stale` (bool) — `true` quando passou de `limit_days` — o cron avisa por e-mail e o smoke reprova. - `limit_days` (int) — O limite em dias (2). ### `Canal` Um canal do catálogo público, do jeito que a busca e a ficha devolvem. - `id` (string) — ID estável do catálogo, ex. `GloboNews.br`. É a chave em toda a API. - `name` (string) — Nome de exibição do canal. - `alt_names` (string[]) — Outros nomes pelos quais o canal é conhecido. - `country` (string, pode ser null) — País de origem, ISO 3166-1 alpha-2. - `categories` (string[]) — IDs de categoria do catálogo, ex. `news`, `sports`. - `category_labels` (string[]) — Os mesmos IDs já traduzidos para exibição. - `languages` (string[]) — Idiomas do canal, ISO 639-3. - `language_labels` (string[]) — Nomes dos idiomas acima, quando conhecidos. - `logo_url` (string, pode ser null) — Logo servido por nós (variante ≤256px), não a origem. - `website` (string, pode ser null) — Site oficial do canal. - `playable_hint` (bool, opcional) — Se a última verificação achou stream utilizável. - `slug` (string, pode ser null) — Identificador legível; é id de API, não URL pública. - `network` (string, pode ser null) — Rede/emissora a que o canal pertence. - `owners` (string[]) — Quem opera o canal, conforme o cadastro. - `launched` (string, pode ser null) — Data de lançamento (AAAA-MM-DD). - `replaced_by` (string, pode ser null) — ID do canal que substituiu este, se foi descontinuado. - `feed_name` (string, pode ser null) — Nome do feed quando o canal tem mais de um. - `feed_format` (string, pode ser null) — Formato do feed declarado pela fonte. - `timezones` (string[]) — Fusos em que o canal transmite. - `broadcast_area` (string[]) — Área de cobertura, em códigos do catálogo. - `quality` (string, opcional) — Melhor qualidade conhecida, ex. `1080p`. - `has_guide` (bool) — Se existe grade de programação (EPG) para este canal. - `guide_site` (string, pode ser null) — Site de onde a grade vem. - `guide_lang` (string, pode ser null) — Idioma da grade de programação. - `subdivision` (string, pode ser null) — Estado/província, código do catálogo. - `city` (string, pode ser null) — Cidade, código do catálogo. - `kind` (string) — `tv` ou `radio` (estação de rádio). - `radio` (Radio, opcional) — Só em estação de rádio: tags, votos, cliques, codec, bitrate e localização. → ver `Radio` em **Estruturas**. - `origem` (string) — Identificador de procedência; créditos e licenças em `/sobre`. - `guide_now` (GuiaAgora, opcional, pode ser null) — Só na ficha: agora e a seguir na programação de hoje, grabada por nós; `null` sem guia fresca. → ver `GuiaAgora` em **Estruturas**. - `health_ext` (SaudeMedida, pode ser null) — Saúde medida por terceiro; `null` quando o canal não foi medido. → ver `SaudeMedida` em **Estruturas**. - `social` (Social, opcional) — Contadores da comunidade; ausente nas páginas HTML de SEO. → ver `Social` em **Estruturas**. - `api` (string) — URL absoluta da ficha deste canal. ### `FacetasCanal` Recortes da busca atual. Hoje só categoria; o formato aceita mais sem quebrar cliente. - `categories` (FacetaCategoria[]) — Categorias presentes no resultado, com a contagem de cada uma. → ver `FacetaCategoria` em **Estruturas**. ### `FiltrosCanal` O que o servidor entendeu do que você mandou — útil para saber por que um filtro não pegou. - `q` (string, pode ser null) — Busca textual aplicada. - `country` (string, pode ser null) — País aplicado, já em maiúsculas. - `category` (string, pode ser null) — Categoria aplicada. - `language` (string, pode ser null) — Idioma aplicado, já normalizado para ISO 639-3. - `network` (string, pode ser null) — Rede aplicada. - `quality` (string, pode ser null) — Qualidade aplicada. - `guide` (bool) — Se o filtro de grade de programação estava ligado. - `subdivision` (string, pode ser null) — Subdivisão aplicada. - `city` (string, pode ser null) — Cidade aplicada. - `playable` (bool) — Se só canal com stream utilizável entrou. - `sort` (string) — Ordem aplicada: `name`, `score` (saúde medida por terceiro) ou `votes` (rádio). - `online` (bool) — Se só canal visto online pela fonte nas 48 h anteriores à última recarga entrou. - `kind` (string) — `tv`, `radio` ou `all` — sem `kind` na chamada, é `tv`. - `tag` (string, pode ser null) — Tag de rádio aplicada. ### `Radio` Informações próprias de uma estação de rádio. Vem em `Canal.radio` quando `kind` é `radio`. - `tags` (string[]) — Tags da estação, vocabulário livre da fonte. - `votes` (int) — Votos registrados para a estação. - `clicks` (int) — Cliques registrados para a estação. - `codec` (string, pode ser null) — Codec do stream (`MP3`, `AAC+`…), quando a fonte sabe. - `bitrate` (int, pode ser null) — Bitrate em kbps, quando a fonte sabe. - `geo` (object, pode ser null) — `{ lat, lon }` da estação, quando a fonte tem; senão `null`. ### `GuiaAgora` Resumo da guia do dia que a ficha carrega: o programa de agora e o próximo. - `day` (string) — Dia grabado, YYYY-MM-DD (UTC do grabber). - `site` (string, pode ser null) — Referência da programação, quando disponível. - `agora` (Programa, pode ser null) — O programa no ar neste instante; `null` fora da grade. → ver `Programa` em **Estruturas**. - `a_seguir` (Programa, pode ser null) — O próximo programa; `null` no fim da grade. → ver `Programa` em **Estruturas**. ### `SaudeMedida` Saúde observada do canal. É o melhor stream medido; `null` quando ninguém mediu — não medido não é ruim. - `score` (int, pode ser null) — 0–100, média móvel das sondagens do melhor stream do canal. - `online` (bool) — Algum stream do canal respondeu `online` numa sondagem das 48 h anteriores à última recarga do catálogo. - `checked_at` (string, pode ser null) — Instante (ISO-8601) da sondagem mais recente gravada; a recarga só o regrava quando algo mais do canal mudou ou a cada 7 dias, então pode estar até uma semana atrás da sondagem real. ### `Stream` Uma das transmissões de um canal. A `url` já é o hop nosso, não a origem. - `id` (string) — ID do stream; é o `:id` de `GET /api/s/:id`. - `provider_url` (string, pode ser null) — URL direta da transmissão no provedor, para copiar e colar em outro player após falha de acesso; null para protocolo não HTTP(S). O site oficial está em Canal.website. - `legacy_url` (string, pode ser null) — Player legado HTTP isolado para mídia HTTP. Null para origem incompatível; não dispensa CORS nem acesso à origem. - `feed` (string, pode ser null) — Qual feed do canal este stream serve. - `title` (string, pode ser null) — Título do stream, quando a fonte declara. - `url` (string) — URL de reprodução no nosso hop — conta o play (uma vez por pessoa, canal e dia) e devolve a playlist. - `quality` (string, pode ser null) — Qualidade declarada deste stream. - `needs_headers` (bool) — Se a origem exige Referer/User-Agent — o hop cuida disso. - `label` (string, pode ser null) — Rótulo curto para escolher entre streams. - `scheme` (string) — Esquema da URL de origem: `http`, `https`, ou `youtube` (transmissão no YouTube, tocável só pelo player do site, nunca pelo M3U). - `kind` (string) — Como tocar: `hls`, `dash`, `ts`, `flv`, `audio` (rádio contínua), `youtube` (player oficial embutido) ou `externo` (RTMP/RTSP). - `youtube` (Youtube, opcional) — Só em stream do YouTube: ids e as URLs de embed e de assistir. → ver `Youtube` em **Estruturas**. - `playable_hint` (bool) — Se a última verificação achou este stream utilizável. - `origem` (string) — Identificador de procedência; créditos e licenças em `/sobre`. - `health_ext` (SaudeMedidaStream, pode ser null) — A verificação mais recente deste stream; `null` quando não foi medido. → ver `SaudeMedidaStream` em **Estruturas**. ### `LinksCanal` URLs relacionadas, para o agente não montar caminho na mão. - `self` (string) — Esta mesma ficha em JSON. - `app` (string) — A mesma coisa na interface humana. - `api_index` (string) — Índice auto-descrito da API. ### `MotivoFalha` Um motivo de falha agregado, já com o texto que a interface mostra. - `code` (string) — Código: `cors`, `geo`, `sumiu`, `codec`, `playlist`, `sem_resposta`, `protocolo`, `sem_stream`, `outro`. - `label` (string) — O motivo em uma frase curta. - `hint` (string) — O que a pessoa pode fazer a respeito. - `count` (int) — Quantos relatos trouxeram este código. - `last_at` (string, pode ser null) — Relato mais recente com este código (UTC). ### `Ambiente` O comportamento do canal num navegador, sistema e país específicos. - `browser` (string) — Navegador normalizado, ex. `Chrome`; `Outro` quando não dá para dizer. - `os` (string) — Sistema normalizado, ex. `Android`. - `country` (string) — País de quem relatou, alpha-2. - `label` (string) — Os três acima numa frase, para exibir. - `plays` (int) — Relatos de sucesso neste ambiente. - `fails` (int) — Relatos de falha neste ambiente. - `health` (int, pode ser null) — Percentual de sucesso aqui; `null` sem relato bastante. - `last_fail_code` (string, pode ser null) — Código da última falha neste ambiente. - `last_at` (string, pode ser null) — Relato mais recente neste ambiente (UTC). ### `RegiaoSaude` O comportamento do canal num país — sem quebrar por navegador/SO. - `country` (string) — País, alpha-2; `ZZ` quando a borda não disse. - `plays` (int) — Relatos de sucesso neste país. - `fails` (int) — Relatos de falha neste país. - `health` (int, pode ser null) — Percentual de sucesso aqui; `null` sem relato bastante. - `latency_ms` (int, pode ser null) — Latência média da playlist medida na borda deste país; só em `your_country`. - `latency_grade` (string, pode ser null) — Nota da latência: `otima`, `boa`, `lenta` ou `ruim`; só em `your_country`. ### `GeoCanal` O que separa 'está bloqueado onde eu moro' de 'saiu do ar para todo mundo'. - `tipo` (string) — `geo` (falha numa região, funciona noutra), `down` (falha em toda região medida), `ok` ou `unknown` (sem relato suficiente). - `bloqueado_em` (string[]) — Regiões onde só há relato de falha (teto de 8; `ZZ` = país desconhecido). - `funciona_em` (string[]) — Países com pelo menos um sucesso (teto de 8). - `label` (string) — O veredito em uma frase; vazio quando não há o que dizer. ### `LatenciaPais` Quanto tempo a playlist leva para abrir, medido na borda do país — a Grade busca a origem por você, então sabe. - `country` (string) — País onde a medição aconteceu, alpha-2. - `samples` (int) — Quantas aberturas entraram na média. - `avg_ms` (int) — Tempo médio até a playlist chegar, em milissegundos. - `grade` (string) — `otima` (<600ms), `boa` (<1500ms), `lenta` (<3500ms) ou `ruim`. - `grade_label` (string) — A nota em uma palavra, para exibir. - `last_at` (string, pode ser null) — Última medição (UTC). ### `LinksSaude` Endereços relacionados ao painel de saúde. - `self` (string) — Este mesmo painel. - `channel` (string) — Ficha do canal. - `report` (string) — Onde mandar um relato novo. ### `Programa` Um programa da grade do dia, com os instantes em ISO 8601 (UTC). - `inicio` (string) — Começo do programa, ISO 8601. - `fim` (string) — Fim do programa, ISO 8601. - `titulo` (string) — Título como o site da programação escreve. - `desc` (string, opcional) — Sinopse curta (até 300 caracteres). - `categoria` (string, opcional) — Categoria do site, quando ele dá. ### `Feeds` O mesmo conteúdo em quatro formatos. São URLs públicas, com o token do feed no caminho — quem tem o link tem o conteúdo. - `m3u` (string) — Playlist M3U — é o que se cola no VLC. - `m3u8` (string) — Mesma playlist, extensão `.m3u8` para players que só aceitam ela. - `json` (string) — Mesma lista em JSON, para quem programa em cima. - `xspf` (string) — Mesma lista em XSPF, para players que preferem XML. ### `PastaBiblioteca` Pasta (aba principal) da galeria do dono. - `id` (string) — ID da pasta, `cat_…`. - `name` (string) — Nome que o dono deu. - `slug` (string) — Versão do nome usada na URL do feed. - `sort` (int) — Posição na ordenação do dono. - `last_group_id` (string, pode ser null) — Sub-aba aberta por último — é o que devolve a pessoa ao lugar onde parou. - `api` (string) — URL absoluta desta pasta. - `feeds` (Feeds) — Feeds só desta pasta. → ver `Feeds` em **Estruturas**. - `groups` (SubAba[]) — Sub-abas dentro da pasta. → ver `SubAba` em **Estruturas**. ### `Visita` Um canal no histórico do dono, com quantas vezes ele assistiu. - `channel_id` (string) — ID do canal assistido. - `name` (string) — Nome do canal; cai no ID se ele saiu do catálogo. - `country` (string, pode ser null) — País do canal. - `logo_url` (string, pode ser null) — Logo servido por nós. - `playable_hint` (bool) — Se a última verificação achou stream utilizável. - `plays` (int) — Quantas vezes o dono assistiu este canal. - `first_at` (string) — Primeira vez que assistiu (UTC). - `last_at` (string) — Última vez que assistiu (UTC). - `stale` (bool) — `true` quando o canal não existe mais no catálogo. - `api` (string) — URL absoluta da ficha do canal. ### `Relato` Relato cru de reprodução, com endereço — por isso a rota é só de operador e a linha expira. - `channel_id` (string) — Canal que a pessoa tentou assistir. - `ok` (bool) — Se tocou (`true`) ou falhou (`false`). - `code` (string, pode ser null) — Código da falha, quando falhou. - `ip` (string, pode ser null) — Endereço de quem relatou. Nunca aparece no painel público. - `browser` (string, pode ser null) — Navegador deduzido do User-Agent. - `os` (string, pode ser null) — Sistema deduzido do User-Agent. - `country` (string, pode ser null) — País deduzido pela borda. - `day` (string) — Dia do relato (AAAA-MM-DD) — a contagem é uma por dono, canal e dia. - `at` (string) — Momento exato do relato (UTC). ### `FiltrosRelato` O que o servidor entendeu do filtro de relatos. - `channel_id` (string, pode ser null) — Canal filtrado, ou `null` para todos. - `only_failures` (bool) — Se só as falhas entraram. ### `Favorito` Um canal favoritado pelo dono. - `channel_id` (string) — ID do canal favoritado. - `name` (string) — Nome do canal; cai no ID se ele saiu do catálogo. - `country` (string, pode ser null) — País do canal. - `quality` (string, pode ser null) — Melhor qualidade conhecida. - `logo_url` (string, pode ser null) — Logo servido por nós. - `playable_hint` (bool) — Se a última verificação achou stream utilizável. - `favorites` (int) — Quantas pessoas favoritaram este canal ao todo. - `created_at` (string) — Quando o dono favoritou (UTC). - `stale` (bool) — `true` quando o canal não existe mais no catálogo. - `api` (string) — URL absoluta da ficha do canal. ### `Precos` Preços em vigor, em dólar. Leia daqui, não da documentação. - `contact_agent_usd` (number) — Custo de um contato de agente. - `chat_month_usd` (number) — Custo do passe de chat por 30 dias. - `abuso_24h_usd` (number) — Preço da porta de UA vazio ou curl (desligada). ### `Passe` Passe mensal do chat: o `direito` `chat_pass` de quem fala, no registro global de compras. - `active` (bool) — Se o passe vale neste momento. - `until` (string, pode ser null) — Até quando vale (UTC); `null` sem passe, ou com o chat aberto de graça (`X402_GRATIS`). ### `TetosGaleria` Os limites da galeria pessoal — existem para o catálogo público não ser despejado numa biblioteca. - `categories` (int) — Pastas por dono. - `groups_per_category` (int) — Sub-abas por pasta. - `items_per_group` (int) — Canais por sub-aba. ### `ContagemCatalogo` Quantas linhas cada tabela do catálogo tem — no ar ou no staging. - `channels` (int) — Canais (linhas de `channels`). - `channels_fts` (int) — Linhas do índice de busca (`channels_fts`). - `channels_fts_ids` (int) — Ids distintos no índice de busca; a troca exige que seja igual a `channels`. - `streams` (int) — Streams (linhas de `streams`). - `blocklist` (int) — Canais bloqueados (DMCA), que ficam fora do catálogo. - `facet_countries` (int) — Países na faceta. - `facet_categories` (int) — Categorias na faceta. - `facet_languages` (int) — Idiomas na faceta (ISO 639-3 inteiro). - `facet_subdivisions` (int) — Estados e subdivisões na faceta. - `facet_cities` (int) — Cidades na faceta. ### `MetaCatalogo` Os carimbos da recarga, lidos de `catalog_meta`. - `synced_at` (string, pode ser null) — Instante (ISO-8601) da última troca bem-sucedida; é o que `/api/health` compara com o limite de 2 dias. - `applied_at` (string, pode ser null) — Instante em que o catálogo novo entrou no ar. - `dump_sha256` (string, pode ser null) — SHA-256 do conjunto de dados que gerou o catálogo no ar. - `staging_run` (string, pode ser null) — `recarga_id` da execução com staging aberto agora; `null` sem staging. ### `SlugPublicado` O par que a recarga herda: id estável do canal → slug já publicado. - `id` (string) — Id do canal no catálogo (ex.: `GloboRJ.br`). - `slug` (string) — Slug publicado; a troca recusa staging em que este id venha com outro. ### `PaymentFree` - `o_que` (string) — Operation or allowance. - `limite` (string) — Allowance and eligibility. - `janela` (string, pode ser null) — Reset window, when applicable. ### `PaymentPrice` - `o_que` (string) — Operation and billing unit. - `price_usd` (number) — Current list price in USD. ### `PaymentTrial` - `days` (int) — Trial duration in days. - `how` (string) — Eligibility and activation steps. ### `FacetaCategoria` Categoria com a contagem dentro da busca que acabou de ser feita. - `id` (string) — ID da categoria no catálogo, ex. `news`. - `name` (string) — Nome da categoria para exibição. - `count` (int) — Canais desta categoria dentro do filtro atual. - `icon` (string, pode ser null) — Nome do ícone usado na interface. ### `Youtube` Um stream que é uma live do YouTube: o player embute o oficial; M3U/XSPF não o levam. - `video` (string, opcional) — Id do vídeo da live (11 caracteres), quando a fonte deu um vídeo. - `channel` (string, opcional) — Id do canal (`UC…`), quando a fonte deu o canal: o embed abre a live corrente. - `embed` (string) — URL do embed oficial sem cookies (`youtube-nocookie.com`). - `assistir` (string) — URL para abrir no YouTube (botão do player e destino do hop). ### `SaudeMedidaStream` A verificação mais recente deste stream; `null` quando o stream não foi medido. - `status` (string) — `online`, `offline`, `blocked` (geo), `timeout`, `error` ou `unknown`. - `score` (int, pode ser null) — 0–100, média móvel: uma falha só não derruba o stream. - `checked_at` (string, pode ser null) — Instante (ISO-8601) da sondagem gravada; regravado quando o status ou o score mudam, ou a cada 7 dias. - `resolution` (string, pode ser null) — Resolução observada, ex. `1080p`. - `bitrate` (int, pode ser null) — Bitrate em bits por segundo, quando medido. - `latency_ms` (int, pode ser null) — Tempo até o primeiro byte na sondagem, em ms. ### `SubAba` Sub-aba dentro de uma pasta; é o nível que agrupa os canais. - `id` (string) — ID da sub-aba, `grp_…`. - `name` (string) — Nome que o dono deu. - `slug` (string) — Versão do nome usada na URL do feed. - `sort` (int) — Posição na ordenação dentro da pasta. - `last_item_id` (string, pode ser null) — Último canal tocado nesta sub-aba. - `api` (string) — URL absoluta desta sub-aba. - `feeds` (Feeds) — Feeds só desta sub-aba. → ver `Feeds` em **Estruturas**. - `items` (ItemBiblioteca[]) — Canais colocados aqui, na ordem do dono. → ver `ItemBiblioteca` em **Estruturas**. ### `ItemBiblioteca` Um canal dentro de uma sub-aba, com o stream já escolhido. - `id` (string) — ID do item na biblioteca, `itm_…`. - `channel_id` (string) — ID do canal no catálogo, ex. `GloboNews.br`. - `stream_id` (string, pode ser null) — Stream escolhido para este item. - `name` (string) — Nome do canal; cai no `channel_id` se o canal sumiu do catálogo. - `logo_url` (string, pode ser null) — Logo servido por nós. - `url` (string, pode ser null) — URL de reprodução no hop; `null` quando o stream sumiu. - `quality` (string, pode ser null) — Qualidade do stream escolhido. - `kind` (string) — Tipo de mídia: `hls`, `mpd`, `mp4`… - `playable_hint` (bool) — Se a última verificação achou o stream utilizável. - `stale` (bool) — `true` quando o canal ou o stream sumiu da fonte — o item fica, sem tocar. - `sort` (int) — Posição dentro da sub-aba. ## Acervos públicos de dados Explore endereços e compras por lugar e abra os registros de que precisa. Até 20 itens por página, em formatos prontos para pessoas e agentes. Confira a cobertura e a data de referência antes de usar um resultado. Cada produto informa suas opções de acesso. - [CEPs e endereços](https://api.pontofato.com/enderecos/index.json): Encontre endereços por lugar, com coordenadas e referência de 2022. Não certifica CEP vigente. UF → município → bairro/localidade → rua → endereços. [HTML](https://api.pontofato.com/enderecos/) · [llms.txt](https://api.pontofato.com/enderecos/llms.txt) · [OKF](https://api.pontofato.com/enderecos/okf/index.md) - [Editais e compras públicas](https://api.editalmd.com/licitacoes/index.json): Encontre compras públicas por lugar e período. Consulte documentos e opções de leitura no EditalMD. Modalidade → UF → ano → mês → dia → município → compras. [HTML](https://api.editalmd.com/licitacoes/) · [llms.txt](https://api.editalmd.com/licitacoes/llms.txt) · [OKF](https://api.editalmd.com/licitacoes/okf/index.md) ## Cota - Grátis: catálogo, busca e facets (`GET /api/channels`) — sem cota. - Grátis: galeria pessoal, pastas e feeds M3U/JSON/XSPF — sem cota por convidado. - Grátis: ler chat, comentários e saúde de canal — sem cota. - Pago: escrever no chat (30 dias) — **$0.10** USDC via x402. - Pago: contato de agente — **$0.10** USDC via x402. Rota paga responde **402** com `accepts[]` (x402, USDC na Base). Pague e repita a mesma chamada com `X-PAYMENT`. Números em vigor: https://staging.gradetv.net/api/billing ## MCP - **Endpoint:** `POST https://staging.gradetv.net/mcp` — Streamable HTTP, JSON-RPC 2.0. - Cada tool é uma chamada nesta mesma API; a credencial vai no header e é repassada.