{"openapi":"3.1.0","info":{"title":"Argos — Monitoramento Inteligente de Câmeras","description":"**Argos** é a plataforma de monitoramento inteligente de câmeras da Automatizase.\nUm servidor central puxa streams RTSP ao vivo, detecta eventos por visão computacional\ne verificação por IA, e apresenta os resultados por organização num painel centralizado.\n\n## Papéis e autenticação\n\n| Papel | Descrição | Escopo |\n|-------|-----------|--------|\n| **administrador da conta** | Administra a organização: câmeras, usuários, notificações e calibração. | Própria organização |\n| **usuário** | Acesso somente leitura: visualiza câmeras, alertas e relatórios. | Própria organização |\n\n### Autenticação pelo Painel\n\nSessão baseada em **cookie HttpOnly** (`session`) ou **Bearer token opaco** obtido em\n`POST /api/v1/auth/login`.\n\n### Autenticação pelo MCP (agentes de IA)\n\n**OAuth 2.0 Authorization Code + PKCE** conforme RFC 7636.\nO fluxo: descoberta via `/.well-known/oauth-authorization-server` →\nDCR via `POST /oauth/register` → autorização em `/oauth/authorize` →\ntroca de código por JWT em `POST /oauth/token` → chamadas ao MCP em `/mcp`\ncom `Authorization: Bearer <JWT>`.\n\n## Vocabulário-chave\n\n- **Câmera**: fonte de vídeo (stream RTSP) da sua organização.\n- **Gravador**: DVR/NVR que hospeda múltiplos canais; propaga credenciais às Câmeras filhas.\n- **Evento**: detecção persistida (tipo + gravidade + Veredito da IA).\n- **Veredito**: decisão da verificação por IA — `confirmado` ou `refutado`.\n- **Canal de Notificação**: destino externo (webhook / WhatsApp / e-mail) para Eventos confirmados.\n- **Calibração**: ajuste de limiares de cada análise de IA por Câmera.\n- **IA**: análise que a Câmera pode ligar/desligar (Fogo, EPI, Aglomeração, etc.).\n- **Empresa**: sua organização/conta no Argos.\n- **MCP**: superfície de API para agentes de IA remotos (conforme seu papel e escopo).\n","contact":{"name":"Automatizase","url":"https://automatizase.com.br/","email":"contato@automatizase.com.br"},"license":{"name":"Proprietário","url":"https://automatizase.com.br/termos"},"version":"1.0.0"},"paths":{"/api/v1/me":{"get":{"tags":["Auth"],"summary":"Identidade do login atual","description":"Retorna username, papel e escopo da organização do token/cookie ativo.","operationId":"getMe","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/auth/token":{"post":{"tags":["Auth"],"summary":"Obtém token Bearer (OAuth2 password form)","description":"Emite um Bearer token opaco via `application/x-www-form-urlencoded`. Compatível com o esquema `OAuth2PasswordBearer` do OpenAPI. Use `POST /api/v1/auth/login` para JSON + cookie simultâneo.","operationId":"authToken","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_authToken"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/auth/login":{"post":{"tags":["Auth"],"summary":"Login JSON (SPA) — token + cookie","description":"Login para o SPA: retorna `{access_token, token_type}` como JSON e seta cookie HttpOnly `session=<token>`. Isolamento por host ativo quando o domínio neutro está configurado.","operationId":"authLogin","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_authLogin"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/auth/logout":{"get":{"tags":["Auth"],"summary":"Encerra a sessão","description":"Invalida o token server-side imediatamente e limpa o cookie. Redireciona para /login.","operationId":"authLogout","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/empresas":{"get":{"tags":["Empresas/Usuários"],"summary":"Lista Empresas","description":"Retorna Empresas no escopo do login.","operationId":"listEmpresas","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"BearerCookie":[]}]},"post":{"tags":["Empresas/Usuários"],"summary":"Cria uma Empresa","description":"Cria uma Empresa na organização. Requer papel administrador da conta.","operationId":"createEmpresa","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmpresaCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/usuarios":{"get":{"tags":["Empresas/Usuários"],"summary":"Lista Usuários","description":"Retorna logins no escopo do login.","operationId":"listUsuarios","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"BearerCookie":[]}]},"post":{"tags":["Empresas/Usuários"],"summary":"Cria um Usuário (login)","description":"Cria um login vinculado a uma Empresa. `username` deve ser único.","operationId":"createUsuario","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsuarioCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/calibracao/templates":{"get":{"tags":["Calibração/ML"],"summary":"Lista templates de calibração","description":"Retorna os valores default de calibração e todos os templates de use-case disponíveis. Cada template é um conjunto de limiares otimizados para um cenário específico (ex.: armazém, estacionamento, portaria).","operationId":"listCalibTemplates","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/calibracao/perfis":{"get":{"tags":["Calibração/ML"],"summary":"Lista perfis de use-case","description":"Retorna os Perfis de use-case disponíveis para aplicar no cadastro de Câmeras. Cada Perfil inclui IAs recomendadas, calibração completa e Agenda padrão. Use o campo `perfil` no `POST /api/v1/cameras` para aplicá-lo automaticamente.","operationId":"listCalibPerfis","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/cameras":{"post":{"tags":["Câmeras"],"summary":"Cadastra uma Câmera","description":"Cria uma nova Câmera (stream RTSP) vinculada a uma Empresa. O campo `perfil` aplica IAs, calibração e Agenda de um use-case pré-configurado.","operationId":"createCamera","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CameraCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]},"get":{"tags":["Câmeras"],"summary":"Lista Câmeras","description":"Retorna as Câmeras no escopo do login. Filtre por `empresa_id`. Senhas RTSP sempre mascaradas.","operationId":"listCameras","parameters":[{"name":"empresa_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Empresa Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/cameras/{camera_id}":{"get":{"tags":["Câmeras"],"summary":"Detalhe de uma Câmera","description":"Retorna todos os campos de uma Câmera (status, IAs, calibração, zonas). Senha RTSP mascarada.","operationId":"getCamera","parameters":[{"name":"camera_id","in":"path","required":true,"schema":{"type":"integer","title":"Camera Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]},"patch":{"tags":["Câmeras"],"summary":"Atualiza campos de uma Câmera","description":"Atualização parcial: apenas os campos enviados são alterados. Suporta ativar/desativar (`ativa`), trocar IAs, ajustar Zonas/Linha, Agenda, Calibração, Instruções de Verificação e Modo B (LPR).","operationId":"patchCamera","parameters":[{"name":"camera_id","in":"path","required":true,"schema":{"type":"integer","title":"Camera Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CameraPatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]},"delete":{"tags":["Câmeras"],"summary":"Remove uma Câmera","description":"Exclui a Câmera e encerra o streaming associado.","operationId":"deleteCamera","parameters":[{"name":"camera_id","in":"path","required":true,"schema":{"type":"integer","title":"Camera Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/cameras/{camera_id}/hls_url":{"get":{"tags":["Câmeras"],"summary":"URL HLS para vídeo ao vivo","description":"Retorna a URL HLS do stream ao vivo. Quando há substream cadastrado, inclui também `sub_hls_url` (baixa resolução). Use `hls_url` no player HLS do Painel.","operationId":"getCameraHlsUrl","parameters":[{"name":"camera_id","in":"path","required":true,"schema":{"type":"integer","title":"Camera Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/cameras/{camera_id}/snapshot":{"get":{"tags":["Câmeras"],"summary":"Snapshot JPEG da câmera","description":"Captura e retorna um frame JPEG via ffmpeg. Câmera offline → 404. ffmpeg ausente ou RTSP inacessível → 503. Sem cache (sempre atualizado).","operationId":"getCameraSnapshot","parameters":[{"name":"camera_id","in":"path","required":true,"schema":{"type":"integer","title":"Camera Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/cameras/{camera_id}/calibracao":{"get":{"tags":["Calibração/ML"],"summary":"Calibração efetiva da Câmera","description":"Retorna a calibração salva (`salva`) e a calibração efetiva (`efetiva`) — a salva mesclada sobre os defaults do sistema. Use `efetiva` para exibir os limiares em vigor no motor de regras.","operationId":"getCameraCalibration","parameters":[{"name":"camera_id","in":"path","required":true,"schema":{"type":"integer","title":"Camera Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/cameras/{camera_id}/geometria":{"get":{"tags":["Calibração/ML"],"summary":"Zona e Linha da Câmera","description":"Retorna a Zona (polígono) e a Linha (cruzamento direcional) configuradas na Câmera. Coordenadas em frações [0, 1] independentes de resolução. Lista vazia = frame inteiro (sem restrição geométrica).","operationId":"getCameraGeometry","parameters":[{"name":"camera_id","in":"path","required":true,"schema":{"type":"integer","title":"Camera Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/cameras/{camera_id}/agenda":{"get":{"tags":["Calibração/ML"],"summary":"Agenda de IAs da Câmera","description":"Retorna a Agenda configurada: janelas de horário/dias em que cada IA fica armada. Formato: `{ 'invasao': { 'inicio': 18, 'fim': 6, 'dias': [1,2,3,4,5] } }`. Agenda vazia = sem restrição (IA armada 24h).","operationId":"getCameraAgenda","parameters":[{"name":"camera_id","in":"path","required":true,"schema":{"type":"integer","title":"Camera Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/cameras/testar-conexao":{"post":{"tags":["Câmeras"],"summary":"Testa conectividade RTSP de um host","description":"Sonda portas candidatas para um host RTSP via TCP e RTSP DESCRIBE. Retorna `{porta, tcp, rtsp}` por porta solicitada. Guard SSRF: loopback/link-local → 422. Somente administrador da conta.","operationId":"testarConexaoCamera","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestarConexaoPayload"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/gravadores/descobrir":{"post":{"tags":["Gravadores"],"summary":"Descobre canais de um Gravador","description":"Varre os canais RTSP de um DVR/NVR (sonda DESCRIBE, sem abrir vídeo) e retorna o estado de cada canal: `nova`, `já cadastrada` ou `vazia`. Nada é persistido — use `POST /gravadores/cadastrar` para criar as Câmeras. Guard SSRF ativo (loopback/link-local → 422).","operationId":"descobrirCanaisGravador","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DescobertaPayload"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ResultadoCanal"},"type":"array","title":"Response Descobrircanaisgravador"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/gravadores/cadastrar":{"post":{"tags":["Gravadores"],"summary":"Cadastra Gravador e Câmeras em lote","description":"Cria (ou reutiliza) o Gravador e cadastra uma Câmera por canal selecionado, derivando as URLs RTSP do template do fabricante. Não duplica canais já cadastrados. Retorna o Gravador criado/atualizado e as Câmeras registradas.","operationId":"cadastrarGravador","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CadastroPayload"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResultadoCadastro"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/gravadores/{gravador_id}":{"get":{"tags":["Gravadores"],"summary":"Detalhe de um Gravador","description":"Retorna os dados do Gravador. Senha mascarada para não-Admin.","operationId":"getGravador","parameters":[{"name":"gravador_id","in":"path","required":true,"schema":{"type":"integer","title":"Gravador Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Getgravador"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]},"patch":{"tags":["Gravadores"],"summary":"Atualiza um Gravador","description":"Atualiza campos do Gravador (host, porta, usuário, senha, nome, num_canais). Quando a senha é alterada, propaga a nova credencial para todas as Câmeras filhas. As câmeras ficam online com a nova URL.","operationId":"patchGravador","parameters":[{"name":"gravador_id","in":"path","required":true,"schema":{"type":"integer","title":"Gravador Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GravadorPatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Patchgravador"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/events":{"get":{"tags":["Alertas/Eventos"],"summary":"Lista Eventos","description":"Retorna Eventos detectados pelo funil, com Veredito embutido (confirmado/refutado). Filtre por `camera_id`, `tipo`, `gravidade`, `veredito` e `empresa_id`. Paginação via `limit` (máx. 200) e `offset`. Escopo aplicado pelo papel.","operationId":"listEvents","parameters":[{"name":"camera_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Camera Id"}},{"name":"tipo","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tipo"}},{"name":"gravidade","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gravidade"}},{"name":"veredito","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Veredito"}},{"name":"empresa_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Empresa Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/events/{event_id}":{"get":{"tags":["Alertas/Eventos"],"summary":"Detalhe de um Evento","description":"Retorna o Evento com Veredito completo (confiança + motivo + fonte), caminho do crop (imagem recortada) e revisão humana (se houver).","operationId":"getEvent","parameters":[{"name":"event_id","in":"path","required":true,"schema":{"type":"integer","title":"Event Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/events/{event_id}/revisao":{"post":{"tags":["Alertas/Eventos"],"summary":"Grava revisão humana de um Evento","description":"Registra o rótulo humano (`confirmado` ou `refutado`) sobre um Evento. Se houver crop, move-o para o dataset de treinamento na pasta correta (flywheel). Somente Admin.","operationId":"postEventRevisao","parameters":[{"name":"event_id","in":"path","required":true,"schema":{"type":"integer","title":"Event Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevisaoCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/crops/{event_id}":{"get":{"tags":["Alertas/Eventos"],"summary":"Imagem recortada do Evento (crop)","description":"Retorna o crop JPEG/PNG do Evento. Para Eventos de Veículo sem crop, faz fallback para a foto da Passagem. 404 quando não há arquivo no disco.","operationId":"getEventCrop","parameters":[{"name":"event_id","in":"path","required":true,"schema":{"type":"integer","title":"Event Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/metrics":{"get":{"tags":["Alertas/Eventos"],"summary":"Métricas de Eventos","description":"Retorna contagens de Eventos por tipo/gravidade/veredito no escopo do login. Filtre por `camera_id` e `empresa_id`.","operationId":"getMetrics","parameters":[{"name":"camera_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Camera Id"}},{"name":"empresa_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Empresa Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/cameras-count":{"get":{"tags":["Câmeras"],"summary":"Contagem de Câmeras por organização","description":"Retorna contagem de câmeras ativas no escopo do login.","operationId":"getCamerasCount","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/sse/alertas":{"get":{"tags":["Alertas/Eventos"],"summary":"Stream de alertas via SSE","description":"Server-Sent Events: stream de alertas em tempo real. Autentique via `Authorization: Bearer <token>` ou query param `?token=`. Alternativa ao WebSocket para ambientes sem suporte a upgrade.","operationId":"sseAlertas","parameters":[{"name":"token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/notificacoes":{"post":{"tags":["Notificações"],"summary":"Cria Canal de Notificação","description":"Cria um Canal de Notificação (webhook, WhatsApp ou e-mail) para uma Empresa ou Câmera. Configure `tipos_evento` para filtrar IAs específicas e `agenda` para janelas horárias. Use `destinatarios` para múltiplos destinos.","operationId":"createCanalNotificacao","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CanalCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]},"get":{"tags":["Notificações"],"summary":"Lista Canais de Notificação","description":"Lista os Canais de Notificação no escopo do login. Filtre por `empresa_id`.","operationId":"listCanaisNotificacao","parameters":[{"name":"empresa_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Empresa Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/notificacoes/{canal_id}":{"get":{"tags":["Notificações"],"summary":"Detalhe de um Canal de Notificação","description":"Retorna um Canal de Notificação pelo ID. 404 se não encontrado ou fora do escopo.","operationId":"getCanalNotificacao","parameters":[{"name":"canal_id","in":"path","required":true,"schema":{"type":"integer","title":"Canal Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]},"patch":{"tags":["Notificações"],"summary":"Atualiza Canal de Notificação","description":"Atualização parcial de Canal de Notificação. Apenas campos enviados são alterados.","operationId":"patchCanalNotificacao","parameters":[{"name":"canal_id","in":"path","required":true,"schema":{"type":"integer","title":"Canal Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CanalPatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]},"delete":{"tags":["Notificações"],"summary":"Remove Canal de Notificação","description":"Exclui o Canal de Notificação. Somente administrador da conta.","operationId":"deleteCanalNotificacao","parameters":[{"name":"canal_id","in":"path","required":true,"schema":{"type":"integer","title":"Canal Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]}},"/api/v1/notificacoes/{canal_id}/teste":{"post":{"tags":["Notificações"],"summary":"Dispara notificação de teste","description":"Envia uma notificação sintética para todos os destinos do Canal, confirmando que o destino está acessível. Retorna `{ok: bool, erros: list[str]}`. Somente administrador da conta.","operationId":"testeCanalNotificacao","parameters":[{"name":"canal_id","in":"path","required":true,"schema":{"type":"integer","title":"Canal Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Testecanalnotificacao"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]}},"/.well-known/oauth-authorization-server":{"get":{"tags":["MCP"],"summary":"Metadata do Authorization Server OAuth 2.0","description":"Metadata de descoberta OAuth 2.0 (RFC 8414/9728): endpoints de autorização, token, registro, JWKS e escopos suportados. Ponto de entrada para agentes de IA.","operationId":"mcpOauthMetadata","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"OAuth2MCP":["mcp"]}]}},"/.well-known/jwks.json":{"get":{"tags":["MCP"],"summary":"JWKS (chaves públicas do JWT)","description":"Conjunto de chaves públicas RSA para verificação dos JWTs emitidos pelo Authorization Server.","operationId":"mcpJwks","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"OAuth2MCP":["mcp"]}]}},"/.well-known/oauth-protected-resource":{"get":{"tags":["MCP"],"summary":"Metadata do Protected Resource (RFC 9728)","description":"Anuncia o recurso MCP e o Authorization Server que emite tokens válidos.","operationId":"mcpProtectedResourceMetadata","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"OAuth2MCP":["mcp"]}]}},"/.well-known/oauth-protected-resource/mcp":{"get":{"tags":["MCP"],"summary":"Metadata do Protected Resource (path-scoped /mcp)","description":"Forma path-scoped do Protected Resource Metadata para o endpoint /mcp (RFC 9728 §3.1).","operationId":"mcpProtectedResourceMetadataMcp","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"OAuth2MCP":["mcp"]}]}},"/oauth/register":{"post":{"tags":["MCP"],"summary":"Registro dinâmico de cliente OAuth (DCR)","description":"Registra um cliente OAuth 2.0 dinamicamente (RFC 7591). Endpoint público (sem auth). Informe `redirect_uris` e opcionalmente `client_name`. Retorna `client_id` para usar no fluxo de autorização.","operationId":"mcpOauthRegister","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_RegisterBody"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"OAuth2MCP":["mcp"]}]}},"/oauth/authorize":{"post":{"tags":["MCP"],"summary":"Autentica e emite authorization code","description":"Valida credenciais Argos e emite o authorization code (PKCE). Redireciona para `redirect_uri?code=<code>&state=<state>`.","operationId":"mcpOauthAuthorizeSubmit","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_mcpOauthAuthorizeSubmit"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"OAuth2MCP":["mcp"]}]}},"/oauth/token":{"post":{"tags":["MCP"],"summary":"Troca authorization code por JWT","description":"Troca o authorization code (PKCE verificado) por um JWT de acesso ao MCP. Retorna `{access_token, token_type, expires_in, scope}`. Use o JWT em `Authorization: Bearer <jwt>` nas chamadas ao `/mcp`.","operationId":"mcpOauthToken","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_mcpOauthToken"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"OAuth2MCP":["mcp"]}]}},"/healthz":{"get":{"tags":["Auth"],"summary":"Healthcheck","description":"Retorna `{status: ok}`. Público, sem autenticação. Usado pelo orchestrador de containers.","operationId":"healthz","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"BearerCookie":[]}]}},"/login":{"post":{"summary":"Login Browser","description":"Login via formulário do navegador: seta cookie de sessão HttpOnly e vai pro dashboard.\n\nCookie sem domain= → restrito ao host que o serviu (isolamento por subdomínio).\n`secure=True` quando a requisição é HTTPS (ou COOKIE_SECURE=true), igual ao\n/api/v1/auth/login — impede transmissão em claro em produção.","operationId":"login_browser_login_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_login_browser_login_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerCookie":[]}]}},"/logout":{"get":{"summary":"Logout Legacy","operationId":"logout_legacy_logout_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"BearerCookie":[]}]}}},"components":{"schemas":{"Body_authLogin":{"properties":{"username":{"type":"string","title":"Username"},"password":{"type":"string","title":"Password"}},"type":"object","required":["username","password"],"title":"Body_authLogin"},"Body_authToken":{"properties":{"grant_type":{"anyOf":[{"type":"string","pattern":"^password$"},{"type":"null"}],"title":"Grant Type"},"username":{"type":"string","title":"Username"},"password":{"type":"string","format":"password","title":"Password"},"scope":{"type":"string","title":"Scope","default":""},"client_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Id"},"client_secret":{"anyOf":[{"type":"string"},{"type":"null"}],"format":"password","title":"Client Secret"}},"type":"object","required":["username","password"],"title":"Body_authToken"},"Body_login_browser_login_post":{"properties":{"username":{"type":"string","title":"Username"},"password":{"type":"string","title":"Password"}},"type":"object","required":["username","password"],"title":"Body_login_browser_login_post"},"Body_mcpOauthAuthorizeSubmit":{"properties":{"username":{"type":"string","title":"Username"},"password":{"type":"string","title":"Password"},"client_id":{"type":"string","title":"Client Id"},"redirect_uri":{"type":"string","title":"Redirect Uri"},"code_challenge":{"type":"string","title":"Code Challenge"},"response_type":{"type":"string","title":"Response Type","default":"code"},"code_challenge_method":{"type":"string","title":"Code Challenge Method","default":"S256"},"state":{"type":"string","title":"State","default":""}},"type":"object","required":["username","password","client_id","redirect_uri","code_challenge"],"title":"Body_mcpOauthAuthorizeSubmit"},"Body_mcpOauthToken":{"properties":{"grant_type":{"type":"string","title":"Grant Type"},"code":{"type":"string","title":"Code"},"redirect_uri":{"type":"string","title":"Redirect Uri"},"client_id":{"type":"string","title":"Client Id"},"code_verifier":{"type":"string","title":"Code Verifier"}},"type":"object","required":["grant_type","code","redirect_uri","client_id","code_verifier"],"title":"Body_mcpOauthToken"},"CadastroPayload":{"properties":{"fabricante":{"type":"string","title":"Fabricante"},"host":{"type":"string","title":"Host"},"porta":{"type":"integer","title":"Porta"},"usuario":{"type":"string","title":"Usuario"},"senha":{"type":"string","title":"Senha"},"num_canais":{"type":"integer","title":"Num Canais"},"empresa_id":{"type":"integer","title":"Empresa Id"},"canais":{"items":{"$ref":"#/components/schemas/CanalSelecionado"},"type":"array","title":"Canais"},"nome_gravador":{"type":"string","title":"Nome Gravador","default":""},"perfil":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Perfil"}},"type":"object","required":["fabricante","host","porta","usuario","senha","num_canais","empresa_id","canais"],"title":"CadastroPayload","description":"Payload para POST /gravadores/cadastrar."},"CameraCreate":{"properties":{"nome":{"type":"string","title":"Nome"},"rtsp_url":{"type":"string","title":"Rtsp Url"},"empresa_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Empresa Id"},"perfil":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Perfil"},"eventos_habilitados":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Eventos Habilitados"},"sub_rtsp_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sub Rtsp Url"}},"type":"object","required":["nome","rtsp_url"],"title":"CameraCreate","examples":[{"empresa_id":1,"eventos_habilitados":["fogo","invasao"],"nome":"Portaria Principal","perfil":"portaria","rtsp_url":"rtsp://admin:s3cr3t@192.168.1.100:554/stream1"}]},"CameraPatch":{"properties":{"nome":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nome"},"rtsp_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rtsp Url"},"zonas":{"anyOf":[{"items":{"items":{"type":"number"},"type":"array"},"type":"array"},{"type":"null"}],"title":"Zonas"},"linha":{"anyOf":[{"items":{"items":{"type":"number"},"type":"array"},"type":"array"},{"type":"null"}],"title":"Linha"},"eventos_habilitados":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Eventos Habilitados"},"config_ia":{"anyOf":[{"additionalProperties":{"additionalProperties":{"anyOf":[{"type":"number"},{"type":"boolean"}]},"type":"object"},"type":"object"},{"type":"null"}],"title":"Config Ia"},"agenda":{"anyOf":[{"additionalProperties":{"additionalProperties":true,"type":"object"},"type":"object"},{"type":"null"}],"title":"Agenda"},"ativa":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Ativa"},"modo_b_config":{"anyOf":[{"$ref":"#/components/schemas/ModoBConfig"},{"type":"null"}]},"instrucoes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Instrucoes"},"sub_rtsp_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sub Rtsp Url"}},"additionalProperties":false,"type":"object","title":"CameraPatch","examples":[{"agenda":{"invasao":{"dias":[0,1,2,3,4],"fim":6,"inicio":18}},"config_ia":{"fogo":{"conf":0.65},"invasao":{"conf":0.55}},"eventos_habilitados":["fogo","epi","invasao"],"instrucoes":["Aglomeração de funcionários uniformizados é normal neste galpão"]}]},"CanalCreate":{"properties":{"empresa_id":{"type":"integer","title":"Empresa Id"},"destino":{"type":"string","title":"Destino","default":""},"tipo":{"type":"string","title":"Tipo","default":"webhook"},"ativo":{"type":"boolean","title":"Ativo","default":true},"camera_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Camera Id"},"tipos_evento":{"items":{"type":"string"},"type":"array","title":"Tipos Evento","default":[]},"agenda":{"additionalProperties":true,"type":"object","title":"Agenda","default":{}},"destinatarios":{"items":{"type":"string"},"type":"array","title":"Destinatarios","default":[]}},"type":"object","required":["empresa_id"],"title":"CanalCreate","examples":[{"agenda":{"fim":6,"inicio":18},"ativo":true,"destino":"https://meu-sistema.com/webhook/argos","empresa_id":1,"tipo":"webhook","tipos_evento":["fogo","invasao"]},{"ativo":true,"destinatarios":["+5511999999999"],"empresa_id":1,"tipo":"whatsapp","tipos_evento":[]},{"ativo":true,"destinatarios":["alertas@empresa.com.br"],"empresa_id":1,"tipo":"email"}]},"CanalPatch":{"properties":{"destino":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Destino"},"tipo":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tipo"},"ativo":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Ativo"},"camera_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Camera Id"},"tipos_evento":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Tipos Evento"},"agenda":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Agenda"},"destinatarios":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Destinatarios"}},"additionalProperties":false,"type":"object","title":"CanalPatch"},"DescobertaPayload":{"properties":{"fabricante":{"type":"string","title":"Fabricante"},"host":{"type":"string","title":"Host"},"porta":{"type":"integer","title":"Porta"},"usuario":{"type":"string","title":"Usuario"},"senha":{"type":"string","title":"Senha"},"num_canais":{"type":"integer","title":"Num Canais"},"empresa_id":{"type":"integer","title":"Empresa Id"}},"type":"object","required":["fabricante","host","porta","usuario","senha","num_canais","empresa_id"],"title":"DescobertaPayload"},"EmpresaCreate":{"properties":{"nome":{"type":"string","title":"Nome"}},"type":"object","required":["nome"],"title":"EmpresaCreate","examples":[{"nome":"Construtora Alpha Ltda"}]},"GravadorPatch":{"properties":{"host":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Host"},"porta":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Porta"},"usuario":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Usuario"},"senha":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Senha"},"nome":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nome"},"num_canais":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Num Canais"}},"additionalProperties":false,"type":"object","title":"GravadorPatch","description":"Payload parcial para PATCH /gravadores/{id}. Campos ausentes não são alterados."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ResultadoCadastro":{"properties":{"gravador_id":{"type":"integer","title":"Gravador Id"},"cameras_criadas":{"type":"integer","title":"Cameras Criadas"},"cameras_ignoradas":{"type":"integer","title":"Cameras Ignoradas"},"cameras":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Cameras"},"vagas_restantes":{"type":"integer","title":"Vagas Restantes"}},"type":"object","required":["gravador_id","cameras_criadas","cameras_ignoradas","cameras","vagas_restantes"],"title":"ResultadoCadastro"},"ResultadoCanal":{"properties":{"canal":{"type":"integer","title":"Canal"},"estado":{"type":"string","title":"Estado"},"codec":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Codec"},"resolucao":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resolucao"},"nome_camera":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nome Camera"}},"type":"object","required":["canal","estado"],"title":"ResultadoCanal"},"RevisaoCreate":{"properties":{"rotulo_humano":{"type":"string","title":"Rotulo Humano"},"revisor":{"type":"string","title":"Revisor"}},"type":"object","required":["rotulo_humano","revisor"],"title":"RevisaoCreate"},"TestarConexaoPayload":{"properties":{"host":{"type":"string","title":"Host"},"portas":{"items":{"type":"integer"},"type":"array","title":"Portas"},"usuario":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Usuario"},"senha":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Senha"}},"type":"object","required":["host","portas"],"title":"TestarConexaoPayload"},"UsuarioCreate":{"properties":{"username":{"type":"string","title":"Username"},"senha":{"type":"string","title":"Senha"},"papel":{"type":"string","title":"Papel","default":"usuario"},"empresa_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Empresa Id"}},"type":"object","required":["username","senha"],"title":"UsuarioCreate","examples":[{"empresa_id":1,"papel":"usuario","senha":"senhaSegura123","username":"operador.portaria"},{"empresa_id":2,"papel":"usuario","senha":"senhaSegura123","username":"operador.galpao"}]},"_RegisterBody":{"properties":{"redirect_uris":{"items":{"type":"string"},"type":"array","title":"Redirect Uris"},"client_name":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Client Name"}},"type":"object","required":["redirect_uris"],"title":"_RegisterBody"}},"securitySchemes":{"BearerCookie":{"type":"http","scheme":"bearer","description":"Token opaco obtido em `POST /api/v1/auth/login`. Envie como `Authorization: Bearer <token>` **ou** cookie `session=<token>`. Expiração: 1 hora."},"OAuth2MCP":{"type":"oauth2","description":"JWT emitido pelo Authorization Server interno (fluxo Authorization Code + PKCE). Use para autenticar agentes de IA no endpoint `/mcp`. Descoberta: `GET /.well-known/oauth-authorization-server`.","flows":{"authorizationCode":{"authorizationUrl":"/oauth/authorize","tokenUrl":"/oauth/token","scopes":{"mcp":"Acesso ao MCP Resource Server (escopado pelo papel do usuário)"}}}}}},"tags":[{"name":"Auth","description":"Login, logout e identidade. Emite cookie HttpOnly de sessão ou Bearer token opaco para o Painel."},{"name":"Câmeras","description":"CRUD de Câmeras: cadastro, listagem, atualização parcial e exclusão. Inclui análises de IA habilitadas, Zonas, Linhas, snapshot JPEG e URL HLS para vídeo ao vivo."},{"name":"Calibração/ML","description":"Ajuste fino de limiares de cada análise de IA por Câmera (Calibração), templates de use-case e Perfis pré-configurados. Também expõe Agenda e Geometria (Zona/Linha) da Câmera."},{"name":"Gravadores","description":"DVRs e NVRs: descoberta de canais ativos por RTSP, cadastro em lote e atualização de credenciais com propagação automática às Câmeras filhas."},{"name":"Alertas/Eventos","description":"Eventos detectados por visão computacional e verificação por IA: listagem com filtros, detalhe com Veredito, revisão humana, crops (imagem recortada) e exportação de dataset. Inclui push em tempo real via SSE e WebSocket."},{"name":"Notificações","description":"Canais de Notificação (webhook, WhatsApp, e-mail) por Empresa ou Câmera. CRUD completo, filtro por tipo de análise de IA e Agenda horária. Botão de teste envia notificação sintética para validar o destino."},{"name":"Empresas/Usuários","description":"Empresas (organizações donas das câmeras) e Usuários (logins vinculados). Administradores da conta gerenciam usuários e câmeras da própria organização."},{"name":"MCP","description":"Superfície OAuth 2.0 para agentes de IA remotos (Claude Code, Cursor, etc.). Authorization Server: metadata (RFC 8414), JWKS, DCR, authorize, token. Resource Server em `/mcp` (Streamable HTTP, autenticado por JWT). Escopado pelo papel conforme seu acesso na organização."}],"servers":[{"url":"https://argos.automatizase.com.br","description":"Produção"},{"url":"http://localhost:8000","description":"Local (dev)"}]}