# tracker by aquarela

> Gestor de issues, pruebas y releases de Aquarela, con organizaciones (empresas) y proyectos separados. Registra bugs y mejoras con evidencias, ciclos de prueba y el Release Gate de cada versión, y guarda la base de conocimiento (documentos) de cada empresa. La interfaz habla portugués, inglés y español.

Toda la app exige iniciar sesión (aquarela ID). Los agentes acceden por el servidor MCP de abajo, y los scripts y el CI por la API REST, siempre en nombre de una persona y de sus organizaciones: las que elige al autorizar el MCP (una, varias o todas), o la del token personal.

Versión en producción: 0.2.0 · 0cc7eb9 (versión semántica; el commit identifica el deploy). También en `GET /api/v1/version`, en whoami y en el serverInfo del MCP.

El mismo manual, una parte por página, está en el portal para desarrolladores: https://developers.tracker.aquarela.app/es

Este manual en otros idiomas: Português: https://tracker.aquarela.app/llms.txt · English: https://tracker.aquarela.app/llms.txt?lang=en

## Servidor MCP

- URL: https://tracker.aquarela.app/mcp
- Transporte: Streamable HTTP, sin sesión, respuestas JSON (POST).
- Autenticación: OAuth 2.1 con PKCE (S256) y registro dinámico de cliente (RFC 7591). El cliente descubre todo a partir del 401 de /mcp (RFC 9728).
- Metadata del recurso: https://tracker.aquarela.app/.well-known/oauth-protected-resource/mcp
- Metadata del servidor de autorización: https://tracker.aquarela.app/.well-known/oauth-authorization-server
- El token de acceso vale 1 hora; el refresh token vale 30 días y se cambia en cada uso.

## Cómo conectar

- Claude (web y escritorio): Configuración > Conectores > Agregar conector personalizado, con la URL https://tracker.aquarela.app/mcp.
- Claude Code: `claude mcp add --transport http tracker https://tracker.aquarela.app/mcp` y después `/mcp` para autorizar.
- Codex: `codex mcp add tracker --url https://tracker.aquarela.app/mcp` y después `codex mcp login tracker`.
- ChatGPT: Configuración > Apps y conectores > Crear, con la URL https://tracker.aquarela.app/mcp y autenticación OAuth.

En la pantalla de autorización la persona elige las organizaciones (una, varias o todas, incluso las que se sumen después) y si la aplicación puede crear issues. Con más de una, toda tool acepta el argumento `organization` (slug o nombre de la empresa, ver whoami); sin él, la empresa sale de la issue o del proyecto del pedido cuando solo una de ellas tiene ese código, whoami y list_projects muestran todas, y search_issues sin `project` busca en todas (cada issue con `organization`). Las conexiones están en https://tracker.aquarela.app/account/connections, donde se pueden deshacer.

Sin navegador (CI, un agente corriendo en un pipeline), /mcp también acepta el token personal de la API REST en el encabezado `Authorization: Bearer <token>`, por ejemplo `claude mcp add --transport http tracker https://tracker.aquarela.app/mcp --header "Authorization: Bearer $TRACKER_TOKEN"`.

## Permisos

- `tracker:read`: Leer proyectos, issues, releases, ciclos de prueba y documentos
- `tracker:write`: Crear issues, cambiar su estado y escribir documentos en tu nombre

Dentro de la organización, la conexión ve solo los proyectos de los que la persona es miembro (los admins y dueños de la organización ven todos) y cada acción respeta su perfil en el proyecto (PO/PM, QA, dev, stakeholder).

## Tools

Las descripciones de las tools están en portugués, tal como el servidor MCP las envía a los agentes.

- `whoami` (tracker:read): Mostra a pessoa conectada (com o idioma dela em user.locale: pt-BR, en, es, ou null no automático), as empresas desta conexão, o papel dela em cada empresa, os projetos que ela vê com o perfil em cada um e o que esta conexão pode fazer e a versão do tracker (server: version e commit do deploy). Bom primeiro passo.
- `list_projects` (tracker:read): Lista os projetos que a pessoa vê nas empresas desta conexão, com prefixo do código, tipo do projeto (kind: software, design, admin ou support), perfil dela, tipos de issue, campos personalizados, labels e módulos de cada um. Use antes de create_issue para saber os valores aceitos.
- `search_issues` (tracker:read): Busca issues com os mesmos filtros da lista do tracker. Devolve as mais recentes primeiro (por atualização; com done_last, as concluídas no fim), com código, título, status (e close_reason em Fechado), marca de bloqueio (blocked, blocked_reason), retest_failed (reprovada no último reteste), severidade, prioridade, responsável e QA, sprint, release, todas as releases (releases, como "GOP 1.4.8"), módulo, pai, épico, labels, datas, external_id, edit_version e link; description e custom_fields com include. Aceita também a consulta avançada da tela em `query` (ex.: projeto = MOB e status in (Aberto, "Em teste") e responsavel = eu login, ou bloqueio = sim) e um filtro salvo em `saved_filter`; os filtros se somam. Numa conexão de várias empresas, sem project nem organization, busca em todas (até 200 issues), com organization em cada uma.
- `list_saved_filters` (tracker:read): Lista os filtros salvos da pessoa e os compartilhados na empresa, com o nome e a query string. Para usar um, passe o nome em search_issues (saved_filter).
- `get_issue` (tracker:read): Abre uma issue pelo código (ex.: MOB-0012) ou id: todos os campos (com épico ou pai, filhas, sprint, estimativa, prazo, repetição e quem acompanha), todas as releases, campos personalizados, labels, vínculos, evidências (os anexos, com o comment_id quando vieram num comentário; get_attachment lê o conteúdo, e o download_url pede o token da API), comentários e histórico recentes (com o texto da tela, nomes em vez de ids). Quando alguém pediu o resumo da aqua na tela, aqua_summary traz o que aconteceu, o que falta e quem está com a bola (outdated: houve comentários ou mudanças depois). aqua_context é a ficha de contexto que a aqua escreve sozinha de cada issue (o problema em palavras neutras, onde acontece e os termos), a mesma que o search_similar cruza. Para conferir várias issues, peça só o que precisa em fields ou use search_issues, que já traz releases e edit_version.
- `list_comments` (tracker:read): Todos os comentários de uma issue, na ordem em que foram escritos, cada um com as respostas, o autor e as pessoas mencionadas. Editado traz edited_at; excluído fica no lugar sem o texto, com deleted. Use o id de um comentário em add_comment para responder a ele.
- `get_issue_history` (tracker:read): Histórico completo de uma issue, do mais recente para o mais antigo, em páginas: quem fez, quando e o quê (status, campos, responsável, release, sprint, labels, vínculos, evidências, reteste e horas), com os valores de antes e depois já com nome. get_issue traz só os 50 últimos eventos.
- `list_sprints` (tracker:read): Mostra o planejamento de um projeto: as sprints abertas (ativa e planejadas) com as issues e os pontos, o começo do backlog na ordem de prioridade e a velocidade das últimas sprints encerradas.
- `list_releases` (tracker:read): Lista releases com status, tipo (kind: version ou ota), momento (stage: current é a que está no ar, uma por trilho: versão e OTA têm cada uma a sua; previous as anteriores, approved, testing, development, planned, cancelled; is_next marca a próxima a ir ao ar), datas, quantas issues em aberto estão vinculadas e a última decisão do Release Gate.
- `get_release` (tracker:read): Abre uma release pelo id, ou pelo projeto + versão: dados, tipo e momento (kind, stage, is_next), o resumo da tela (summary: onde ela está, qual está no ar e o próximo passo), métricas e sugestão do Release Gate (gate.applies; em OTA sem BLOCKED registrado a sugestão é "not_applicable" e vem só a contagem das issues em gate.validation: ready_for_qa, testing, approved, failed), última decisão, issues vinculadas, ciclos de teste e as notas da versão (salvas ou o rascunho gerado das issues concluídas).
- `list_test_cycles` (tracker:read): Lista os ciclos de teste mais recentes com release, status e progresso (pass, fail, bloqueado, pendente).
- `get_test_cycle` (tracker:read): Abre um ciclo de teste: release, ambiente, status, totais e cada caso com o resultado (pass, fail, blocked, in_progress, not_started), o comentário, quem testou e a issue aberta a partir do fail.
- `list_test_cases` (tracker:read): Lista os casos de teste ativos (código TC-MOB-0001, título, módulo, prioridade e quantos passos), filtrando por projeto e por parte do código ou do título.
- `create_issue` (tracker:write): Cria uma issue em nome da pessoa conectada, com as mesmas regras da tela: o perfil dela no projeto precisa permitir (PO/PM, QA ou admin; dev cria só subtarefas, com o pai) e a prioridade só vale para PO/PM e admin (os demais criam com prioridade média). Consulte list_projects para tipos (e o nível de cada um: épico, comum, subtarefa), módulos e campos personalizados aceitos. Tipo com template tem modelo: siga a estrutura dele nos campos de texto; campo de texto omitido vem preenchido com o modelo, e texto vazio deixa o campo vazio. Com recurrence (weekly, monthly, quarterly, yearly), concluir a issue cria a próxima ocorrência. assignee e qa_owner atribuem na criação (veja list_people). Antes de criar, pergunte ao search_similar se já aconteceu (query com o título e a descrição): se a aqua disser que é o mesmo problema (relation same), não crie; comente na issue que já existe (add_comment) com o que há de novo. Depois de criar, similar traz as parecidas que a aqua confirmou (relation same ou related, com reason): vincule a nova com link_issues (type duplicates para o mesmo problema, relates_to para relacionada) e diga à pessoa.
- `update_issue` (tracker:write): Edita uma issue em nome da pessoa conectada, com as mesmas regras da tela. Só os campos enviados mudam; null tira o valor. Cobre título, tipo, severidade, prioridade, módulo, textos, campos personalizados (só as chaves enviadas), responsável e QA (veja list_people), registro técnico, pai, pontos, sprint, release (a principal, do projeto da issue), outras releases em que a correção também sai, inclusive de outros projetos (add_releases, remove_releases) e labels (add_labels, remove_labels). Cada grupo exige o perfil certo no projeto: editar é de PO/PM, QA ou admin; atribuir outra pessoa e sprint, de PO/PM ou admin; registro técnico, de dev ou admin; dev e PO/PM assumem a issue com assignee "me", e QA assume como QA responsável com qa_owner "me". Mande edit_version (lido em get_issue) para não sobrescrever a edição de outra pessoa: se a issue mudou depois (campos, pessoas, registro técnico, pai, pontos, sprint, release, labels, estimativa ou datas), nada é gravado e o erro pede para ler de novo. Para status use move_issue; para estimativa, início, prazo e repetição, plan_issue.
- `add_comment` (tracker:write): Comenta numa issue em nome da pessoa conectada, como na tela: quem vê a issue pode comentar. @Nome menciona alguém do canal (veja people em list_chat_messages) e a pessoa é avisada; parent_id responde a um comentário. Quem acompanha a issue recebe o comentário. attachments anexa arquivos ao comentário (cada um em content_base64 com file_name, ou url pública), com as regras de attach_file.
- `edit_comment` (tracker:write): Corrige o texto de um comentário da issue. Só quem escreveu pode corrigir; o texto anterior fica guardado para o admin e a edição entra no histórico. Quem passa a ser mencionado agora é avisado.
- `delete_comment` (tracker:write): Exclui um comentário da issue: ele fica no lugar como excluído, sem o texto, e as respostas continuam. Quem escreveu ou um admin da empresa pode excluir.
- `attach_file` (tracker:write): Anexa um arquivo à issue (as evidências da tela) em nome da pessoa conectada: relatório, print, log, planilha. Mande o conteúdo em content_base64 com file_name (a extensão define o formato), ou a url pública (https) para o tracker baixar; para arquivo grande, prefira url. Formatos: png, jpg, webp, gif, mp4, mov, pdf, zip, json, txt, log, md e csv, até 15 MB, 60 arquivos por hora. comment_id põe o arquivo embaixo de um comentário seu da issue (para comentar e anexar de uma vez, use attachments em add_comment). Perfil que anexa: PO/PM, QA, dev ou admin. Devolve o anexo com download_url; os anexos da issue aparecem em get_issue (evidences), e get_attachment lê o conteúdo.
- `get_attachment` (tracker:read): Lê o conteúdo de um anexo que a pessoa conectada vê: a evidência de uma issue ou o arquivo de uma mensagem das Conversas (do canal ou da conversa direta dela). md, csv, txt, log e json voltam em text (até 200 mil caracteres, com truncated quando passa), e png, jpg, webp e gif até 5 MB voltam como imagem. PDF, vídeo e zip voltam só com o download_url, que pede um token pessoal da API. O id do anexo está em get_issue (evidences), nos attachments de list_comments, na resposta de attach_file ou nos attachments de list_chat_messages; a resposta traz issue (na evidência) ou chat_message_id (na mensagem).
- `remove_attachment` (tracker:write): Remove um anexo (evidência) da issue e apaga o arquivo. Só quem enviou ou um admin do projeto; o arquivo de uma versão enviada para aprovação não sai. O id está em get_issue (evidences) ou na resposta de attach_file.
- `link_issues` (tracker:write): Vincula a issue a outra (blocks, relates_to ou duplicates), como na tela: exige editar a issue, e a outra precisa ser de um projeto que a pessoa vê. Devolve os vínculos da issue, cada um com o id para unlink_issues.
- `unlink_issues` (tracker:write): Remove um vínculo da issue pelo id (o id em links de get_issue ou de link_issues). Exige editar a issue.
- `bulk_update_issues` (tracker:write): Aplica a mesma ação a várias issues (até 100), como a barra de ações da lista: mudar status, responsável, prioridade, severidade ou release, pôr ou tirar label, acompanhar, deixar de acompanhar ou excluir. Cada issue passa pela regra da ação individual (perfil no projeto dela, fluxo de status); as que não puderem mudar voltam em failed com o motivo, e as outras mudam. Responsável e release são achados no projeto de cada issue.
- `update_issues` (tracker:write): Fecha várias issues numa chamada só (até 50), cada uma com o que muda nela: os campos de update_issue (registro técnico, release, outras releases com add_releases, sprint, labels, responsável...), os PRs e commits a ligar (code_links, como link_code) e o status final (status, como move_issue), nessa ordem. Cada passo segue a regra da chamada individual. Um item que falha não para os outros: results traz, por issue, ok, os grupos gravados (updated), os links ligados (linked), o status final e, se falhou, failed_at (issue, fields, code_links ou status) e o motivo; edit_version protege o item todo; os passos anteriores ao que falhou ficam gravados. Para a mesma ação em todas (só status, só release), use bulk_update_issues.
- `list_proofs` (tracker:read): Versões de uma peça enviadas para aprovação (Design), da mais nova para a mais antiga: número, status (pending, approved, changes_requested, superseded), nota de quem enviou, decisão com comentário e o arquivo (download_url com o token da API). pending é o id da versão que espera decisão. O envio de versão nova é pela API REST (POST /issues/{ref}/proofs, multipart).
- `decide_proof` (tracker:write): Decide a versão que espera aprovação (o pending de list_proofs): approve aprova; changes pede ajustes e exige feedback. PO/PM, QA, admin ou o cliente do projeto (stakeholder). Com o fluxo de Design, a issue vai para Aprovado ou Ajustes. Devolve as versões e o status da issue.
- `list_people` (tracker:read): Lista as pessoas ativas que participam de um projeto, com o id e o perfil de cada uma (PO/PM, QA, dev, stakeholder, admin). Use para escolher o responsável em update_issue.
- `get_person` (tracker:read): O perfil de uma pessoa da empresa, como na tela: nome, e-mail, papel na empresa, desde quando está nela, os projetos com o perfil em cada um (all_projects quando é admin ou dona da empresa), quantas tarefas estão com ela (em aberto como responsável e como QA, concluídas nos últimos 30 dias), as tarefas em aberto mais recentes e a atividade recente (comentários e mudanças). Só entram os projetos e as tarefas que a pessoa conectada vê. person: "me", id, e-mail ou nome (veja list_people). Quem só acompanha os projetos vê só quem participa deles, sem o e-mail.
- `set_avatar` (tracker:write): Troca ou tira a foto da pessoa conectada (a de Sua conta → Perfil), que aparece no menu, no perfil, nas listas de pessoas e nas conversas, para quem divide uma empresa com ela. A imagem vai em image_base64 (PNG, JPG ou WebP até 2 MB); remove: true tira e ficam as iniciais. Só a própria foto. Devolve avatar_url, o endereço novo da foto com o token (null sem foto).
- `set_language` (tracker:write): Troca o idioma da pessoa conectada no tracker (o de Sua conta → Perfil → Idioma): as telas, os e-mails e os apps passam a usar este idioma, em todas as empresas dela. locale: "pt-BR" (português), "en" (inglês) ou "es" (espanhol); null volta ao automático, pelo idioma do navegador ou do aparelho. O idioma atual vem em whoami (user.locale). Não muda o que já foi escrito nas tarefas e conversas. Devolve locale.
- `watch_issue` (tracker:write): Liga ou desliga o "Acompanhar" numa issue: quem acompanha recebe os avisos de mudança de status e de comentário novo. Sem person, vale para a pessoa conectada; pôr ou tirar outra pessoa do projeto exige editar a issue (PO/PM, QA ou admin). Devolve person (a pessoa da ação e se ela acompanha agora), watching (se a pessoa conectada acompanha) e a lista watchers.
- `move_issue` (tracker:write): Muda o status de uma issue (para levar a outro projeto, use move_issue_to_project). Move uma issue (código, ex.: MOB-0012, ou id) para outro status do fluxo do projeto dela, com as mesmas regras da tela e do quadro: a transição precisa existir no fluxo e o perfil da pessoa no projeto precisa poder levar a issue até lá. Veja os status de cada projeto em list_projects. Para "closed" (Fechado, modelo QA), mande close_reason; com a issue já em closed, troca só o motivo. Bloqueio não é status: use block_issue.
- `block_issue` (tracker:write): Marca uma issue como bloqueada, com o motivo, ou tira a marca (blocked: false). O bloqueio vale em qualquer status e fluxo (não é uma coluna) e sai sozinho quando a issue conclui; marcar de novo troca o motivo. Todo perfil do projeto menos stakeholder. Devolve blocked (since e reason) ou null. Para achar as bloqueadas, search_issues com blocked_only.
- `move_issue_to_project` (tracker:write): Leva uma issue criada no projeto errado para outro projeto da mesma empresa, com as mesmas regras da tela: a pessoa precisa editar a issue e criar issue no destino. A issue ganha o código seguinte do destino e o antigo continua achando ela. Tipo, status, módulo, release e campos personalizados vão para o equivalente no destino; sem equivalente, caem no padrão de lá. Comentários, anexos, horas e histórico vão junto; subtarefas acompanham a issue pai. Chame primeiro sem confirm para ver os avisos, mostre-os à pessoa e só então chame com confirm: true. Para mudar o status dentro do mesmo projeto, use move_issue.
- `delete_issue` (tracker:write): Exclui a issue como na tela: ela some de listas, busca, API e MCP e pode ser restaurada (restore_issue) por 30 dias. Admin ou PO/PM do projeto. Issue com issues dentro dela (épico, subtarefas) não é excluída.
- `restore_issue` (tracker:write): Restaura uma issue excluída nos últimos 30 dias, pelo código ou id (veja list_deleted_issues), com comentários, evidências e histórico. Admin ou PO/PM do projeto.
- `duplicate_issue` (tracker:write): Cria uma cópia da issue no mesmo projeto, como o Duplicar da tela: conteúdo, classificação, pai, pontos, campos personalizados e labels. Não copia comentários, evidências, vínculos, pessoas, sprint, release nem o registro técnico; a cópia nasce no status inicial. Exige criar issue no projeto.
- `list_deleted_issues` (tracker:read): Lista as issues excluídas que a pessoa pode restaurar (admin ou PO/PM do projeto), com quem excluiu e até quando dá para restaurar.
- `create_release` (tracker:write): Cria uma release (versão) no projeto, no status planned. Exige PO/PM ou admin. A versão é única no projeto: pedir de novo a mesma versão devolve a release que já existe, com already_existed (bom para o CI criar a release a cada deploy). kind "ota" para atualização sem loja; o padrão é "version". Release que já foi ao ar (OTA enviada, deploy do backend, versão publicada fora do tracker) pode nascer com status "production" (sem o gate) e released_date.
- `set_release_status` (tracker:write): Muda o status de uma release (pelo id, ou projeto + versão), com as regras da tela: planned → in_development → staging (em teste), e de approved para production; cancelled sai do caminho. PO/PM ou admin. OTA vai a production direto (de planned, in_development, staging ou approved), sem a decisão do gate, a não ser que a última decisão seja BLOCKED; a validação segue depois pelas issues vinculadas. As outras releases chegam a approved pela decisão APPROVED do Release Gate, registrada por uma pessoa na tela, e produção exige essa decisão. Fora desse caminho (approved direto, inclusive) só admin, com override_note.
- `move_release` (tracker:write): Move uma release (pelo id, ou projeto + versão) para outro projeto da empresa, por exemplo as releases de deploy do backend que nasceram no app para o projeto da plataforma. Leva o histórico, as decisões do gate e as notas; as issues continuam vinculadas (a principal e as de "Também sai em"), mesmo as de outros projetos. Exige PO/PM ou admin nos dois projetos. A versão não pode existir no destino, e release com ciclo de teste não se move (ciclo é do projeto).
- `save_release_notes` (tracker:write): Grava as notas da versão de uma release (pelo id, ou projeto + versão), o texto para o cliente que a tela copia e manda por e-mail. PO/PM ou admin. Para escrever em linguagem de cliente, leia em get_release as issues e o rascunho gerado (release_notes): diga o que muda para quem usa, sem códigos de issue nem jargão técnico, e deixe de fora o que o cliente não percebe. Substitui o texto inteiro; vazio ou null volta ao rascunho gerado. Não envia o e-mail: o envio é na tela.
- `create_test_case` (tracker:write): Cria um caso de teste com os passos (ação e resultado esperado de cada um). Exige perfil QA ou admin no projeto. O código (TC-MOB-0001) é gerado.
- `create_test_cycle` (tracker:write): Cria um ciclo de teste no projeto, de uma release (pela versão) ou avulso, com os casos escolhidos pelo código, ou com todos os casos ativos do projeto quando cases não vem. Exige perfil QA ou admin. Devolve o ciclo com os casos, para registrar os resultados com record_test_results.
- `record_test_results` (tracker:write): Registra o resultado de um ou mais casos (até 200) num ciclo de teste em nome da pessoa conectada (QA ou admin), como na execução da tela: pass, fail, blocked ou in_progress, com comentário obrigatório para fail e blocked. Ciclo planejado passa a em andamento. Com open_issue, o fail abre a issue já preenchida com o caso (ou devolve a que já existe); se a issue não abrir, o resultado fica gravado e o item volta com warning. Item a item: um recusado não desfaz os outros. Gravar de novo o mesmo caso substitui o resultado.
- `set_test_cycle_status` (tracker:write): Inicia (start), encerra (finish), cancela (cancel) ou reabre (reopen) um ciclo de teste. Ciclo encerrado não aceita resultados até ser reaberto.
- `time_report` (tracker:read): Horas registradas num período (padrão: mês corrente), por pessoa, por projeto e por issue, com os registros mais recentes. person "me" mostra só as suas. Só cobre projetos em que o perfil da pessoa vê horas (stakeholder não vê).
- `log_time` (tracker:write): Registra tempo trabalhado numa issue em nome da pessoa conectada (perfis PO/PM, QA, dev ou admin). Use duration em texto ("1h30", "45m") ou minutes. Sem work_date, vale hoje. Devolve o total registrado e o restante da estimativa.
- `plan_issue` (tracker:write): Define estimativa ("8h", "2d"), início, prazo e repetição de uma issue. Campo omitido fica como está; null tira o valor. As datas aparecem no cronograma do projeto. Com recurrence (weekly, monthly, quarterly, yearly), concluir a issue cria a próxima ocorrência com início e prazo avançados.
- `list_code_links` (tracker:read): Lista os PRs (GitHub), MRs (GitLab) e commits ligados a uma issue, com repositório, estado (aberto, mergeado, fechado), branch, autor e link. Os que vêm da integração com o repositório se atualizam sozinhos; os ligados na mão ficam como foram ligados.
- `link_code` (tracker:write): Liga um PR ou commit do GitHub, ou um MR ou commit do GitLab (inclusive self-hosted), a uma issue, pelo link. Aparece no card Código da issue e no histórico. Não muda o status: quem move a issue é a integração do repositório ou move_issue. Perfis PO/PM, QA, dev ou admin.
- `unlink_code` (tracker:write): Tira um PR/MR ou commit da issue, pelo id do link (veja list_code_links) ou pelo mesmo link usado para ligar. Fica no histórico.
- `list_repositories` (tracker:read): Lista os repositórios do GitHub e projetos do GitLab conectados a um projeto do tracker, com o status que cada evento aplica (PR aberto, mergeado, fechado, commit) e o último evento recebido. connection diz como chega: github_app (o app do GitHub da empresa), gitlab_token (o tracker criou e cuida do webhook no GitLab) ou webhook (configurado à mão). Um PR ou commit que cita o código da issue (ex.: MOB-0012) nesses repositórios aparece na issue sozinho.
- `search_similar` (tracker:read): Busca issues (com os comentários) e chamados do Atendimento parecidos com um texto ou com uma issue, pelo sentido e pelas palavras (busca híbrida). Use antes de criar uma issue (query com o título e a descrição) e na triagem de um erro ou chamado, para saber se já aconteceu antes; com relation same, comente ou vincule a que já existe em vez de criar outra. likely_duplicate marca o que parece ser o mesmo problema; mode diz se a busca foi também pelo significado (hybrid) ou só por texto (text); semantic diz se a busca por significado está ligada na empresa (on, com indexed: quantos itens já estão no índice) e, se não, por quê (reason: no_vector, a busca pelo sentido não está disponível neste servidor; no_key, aqua desligada, chave própria da empresa ou sem créditos de IA). Com issue, a busca parte da ficha de contexto que a aqua escreve de cada issue. Com explain (padrão), a aqua confere cada achado: tira o que só divide palavras e devolve relation (same: o mesmo problema; related: relacionado) e reason (a ligação numa frase); explained diz se ela conferiu. Para filtrar por status, responsável ou datas, use search_issues.
- `list_aqua_recommendations` (tracker:read): As sugestões abertas da aqua (a IA do tracker) para as issues abertas, como na tela aqua: cada issue com código, título, status, o resumo da revisão (summary), reviewed_at, outdated (a issue mudou depois da revisão) e as recommendations (id, kind: comment, status, field, custom_field, label, duplicate ou move; title, before, after, reason e comment, o texto do pedido de informações). Traz também available (a IA ligada na empresa), can_review, a revisão diária (daily_review) e sentry_waiting (erros do Sentry esperando decisão; veja list_sentry_errors). Só de quem edita issues. Use apply_aqua_recommendation ou dismiss_aqua_recommendation para decidir.
- `get_ai_credits` (tracker:read): Os créditos de IA da empresa, como em Empresa → Integrações → Assistente de IA. Sem chave própria, a aqua usa a chave do tracker e gasta créditos de um pacote por ciclo de 30 dias, que renova sozinho; key_source diz qual chave a empresa usa (server: créditos; organization: chave própria, sem créditos e credits null). credits traz state (ok, low: 20% ou menos; empty: acabou e a aqua pausa até renovar; expired), remaining, used, total (per_cycle mais granted, os créditos que a equipe Aquarela lançou no ciclo), cycle_start, cycle_end, auto_renew e renews_at. usage (só admin e dono; null para os demais) traz o uso por ação desde since (o início do ciclo; com a chave própria, os últimos 30 dias), das mais usadas para as menos: action, label, calls, failures, credits. Nunca há valores em dinheiro. Quando a aqua recusar um pedido por falta de créditos, use isto para dizer à pessoa quando renova.
- `get_billing` (tracker:read): O plano da empresa, como em Empresa → Plano (só admin e dono): currency (a moeda da empresa, em que ela vê os preços; currency_locked: com a cobrança ligada, só a equipe da Aquarela troca), plan (key, name, description, ai_credits_per_cycle, storage_gb, features e os preços na moeda da empresa), plan_source (subscription, ou default quando vale o plano padrão), price (o preço de agora: amount_minor na unidade mínima da moeda, amount e formatted), subscription (status trialing, active, past_due ou canceled; interval month ou year; current_period_end; trial_ends_at; billing_provider, null enquanto não há cobrança), plans (os planos que as empresas podem ter, com o preço na moeda da empresa) e billing_details (dados de faturamento: kind, legal_name, email, country, endereço, tax_id_type e tax_id; null se ninguém preencheu) e plan_request (o pedido de plano pago esperando a equipe da Aquarela, ou null) e storage (o espaço de arquivos: used_bytes, limit_bytes, plan_storage_gb, state ok, warning a partir de 90% ou full, e quanto ocupam tasks, tickets, chat e direct_messages, cada um com bytes e files). Durante o beta nada é cobrado. Para mudar de plano, use change_plan; para ver e excluir os arquivos, list_files e delete_file.
- `update_billing` (tracker:write): Troca a moeda da empresa (currency) e muda os dados de faturamento (billing_details: só os campos enviados mudam; null apaga), como em Empresa → Plano (só admin e dono). O documento fiscal segue o país (no Brasil, CNPJ para empresa e CPF para pessoa física, com os dígitos conferidos). Devolve o mesmo que get_billing.
- `change_plan` (tracker:write): Muda o plano da empresa, como em Empresa → Plano → Mudar de plano (só admin e dono). plan é a key ou o id de um dos plans de get_billing; interval, month (padrão) ou year. Plano gratuito na moeda da empresa muda na hora (outcome changed). Plano pago, ou sem preço na moeda, vira um pedido para a equipe da Aquarela (outcome requested), que combina o pagamento com a empresa: durante o beta não há cobrança automática. Pedir outro plano troca o pedido. O plano de agora devolve outcome unchanged. Devolve também billing, como em get_billing.
- `cancel_plan_request` (tracker:write): Cancela o pedido de plano pago que a empresa fez (plan_request em get_billing), só admin e dono. cancelled false: não havia pedido.
- `list_files` (tracker:read): Os arquivos guardados pela empresa, como em Empresa → Arquivos (só admin e dono): storage (o espaço usado do plano, como em get_billing), total, has_more e files, cada um com id, origin (task: anexo de uma tarefa ou comentário; ticket: do atendimento; chat: das Conversas dos projetos), name, type, size_bytes, created_at, uploaded_by, project, issue (code, title e comment_id), ticket (number e subject), chat_message (id e thread_id), url (onde está), download_url (pede o token da API; get_attachment lê o conteúdo dos de tarefa e das Conversas), can_delete e locked (proof: versão enviada para aprovação; trash: tarefa ou projeto excluído). Os das conversas diretas contam no espaço (direct_messages), mas não aparecem: só as duas pessoas leem. Passou do espaço, anexo novo é recusado até excluir arquivos ou mudar de plano. sort size traz os maiores primeiro.
- `delete_file` (tracker:write): Exclui um arquivo de list_files (só admin e dono) e libera o espaço: o anexo sai da tarefa (fica no histórico dela), do chamado ou da mensagem das Conversas (a mensagem sem texto e sem outro anexo fica como excluída). Não dá para desfazer: confirme com a pessoa antes. Versão enviada para aprovação e arquivo de tarefa ou projeto excluído não saem.
- `list_inbox` (tracker:read): A caixa de entrada da pessoa conectada, como na tela Caixa de entrada: os comentários em que alguém a mencionou (kind mention) e as respostas nas conversas em que ela escreveu (kind reply; own_thread: a conversa começou com um comentário dela), as mais recentes primeiro. Cada item traz id, text (a frase da tela), author, excerpt (o trecho do comentário), issue (id, code, title, project), url (direto ao comentário), at, read e hidden; unread conta as não lidas. Escrever na tarefa depois do comentário, abrir a tarefa ou ler a notificação no sino também contam como lida. As menções e respostas nas Conversas dos projetos vêm à parte, em chat_items (com chat_unread): cada uma traz id, kind (mention ou reply, a resposta numa thread em que ela está), own_thread (a thread começou com uma mensagem dela), thread, text, author, excerpt (o trecho da mensagem), project, url (direto à mensagem), at, read e hidden; abrir a thread ou ler o canal conta como lida. Use update_inbox para marcar como lida, ocultar ou trazer de volta (os ids de items e chat_items), get_issue ou list_comments para ler a conversa da tarefa e list_chat_messages para a das Conversas.
- `update_inbox` (tracker:read): Marca itens da caixa de entrada da pessoa conectada como lidos (action read, com ids ou all: true), oculta (hide, com ids ou older_than_days para as antigas; ocultar também conta como lida) ou traz de volta da aba Ocultadas (unhide, com ids). Vale para os itens das tarefas e das Conversas. Ler também marca no sino as notificações dos mesmos comentários. Devolve updated (quantos mudaram), unread e chat_unread.
- `get_aqua_summary` (tracker:read): O resumo da aqua do dia (period day, o padrão) ou do mês (month) da pessoa conectada, como no topo da Caixa de entrada: headline (o principal), highlights (o que aconteceu nas tarefas dela, nas que ela registrou e nas que acompanha), attention (o que pede a atenção dela: menção não lida, prazo, bloqueio), issues (as tarefas citadas, com code, id e url), generated_at e outdated (chegou novidade depois). Sem refresh, devolve o último resumo do período (summary null quando ela ainda não pediu); refresh: true pede um novo, com os créditos de IA da empresa. available diz se a aqua está ligada.
- `list_aqua_conversations` (tracker:read): As conversas da pessoa conectada com a aqua no chat de ajuda do tracker (o chat do site e dos apps), as mais recentes primeiro: id, title (a primeira pergunta), messages (quantas mensagens), created_at e updated_at. get_aqua_conversation abre uma.
- `get_aqua_conversation` (tracker:read): Uma conversa da pessoa conectada com a aqua: as mensagens (role, content, at) e, nas respostas da aqua, as mudanças que ela propôs (actions: tool, input, title, details, state — pending, running, done, cancelled ou failed —, result com text e url, error). Confirmar ou cancelar uma proposta é na tela ou pela API (POST /aqua/conversations/{id}/actions/{action}).
- `list_chat_channels` (tracker:read): Os canais das Conversas da pessoa conectada, como na lista da tela: um por projeto ativo em que ela conversa (o stakeholder não tem canal). Cada canal traz project (id, slug, name), unread (mensagens novas na conversa principal), mentions (menções e respostas nas threads dela ainda não lidas), last_message_at e url. directs traz as conversas diretas dela (só ela e a outra pessoa leem), a mais recente primeiro: id, person (id, name), unread, last_message_at e url; people, com quem ela pode abrir uma (id, name e url). unread no topo soma os canais e as conversas diretas; mentions, os canais. Use list_chat_messages para ler um canal (project) ou uma conversa direta (person).
- `list_chat_messages` (tracker:read): As mensagens do canal de um projeto em Conversas (project) ou da conversa direta com uma pessoa da equipe (person: id ou e-mail; abre a conversa na primeira vez; não tem thread nem @menção), na ordem em que foram escritas. Sem thread_id, a conversa principal: sem cursor, as últimas; before traz as anteriores e after as que chegaram depois. Com thread_id (o id da primeira mensagem de uma thread), devolve thread (essa mensagem) e replies (as respostas da thread). A conversa é da equipe do projeto (o stakeholder não vê). Cada mensagem traz id, thread_id (a thread em que é resposta, ou null), in_channel e thread_root (a resposta da thread que também foi para a conversa principal, com author e text da mensagem da thread), author, app (a mensagem escrita por um app das Conversas, com key e name; null na de uma pessoa), text (markdown; o texto dos apps sai no idioma da pessoa conectada), at, edited_at, deleted, mine (escrita pela pessoa conectada), can_edit e can_delete (o que ela pode fazer com a mensagem), mentions, mentions_me, attachments (os arquivos anexados: id, name, type, kind image, video ou file, size_bytes e download_url; get_attachment lê o conteúdo), issues (as tarefas citadas pelo código, com code, title, open e url), card (o cartão de um app: a reunião do Google Meet, com url, code, title, started_by e, na agendada, starts_at, ends_at e event_id; null nas outras), meeting_responses (na reunião agendada: quantos responderam yes, maybe e no pelo tracker e mine, a resposta da pessoa conectada; null nas outras), aqua_actions (na resposta da aqua: as ações que ela propôs, com asked_by, mine e actions com id, tool, title, details, state, replaced, result e error; decida com decide_aqua_chat_action; null nas outras), reactions (emoji, count, mine e people), thread (na conversa principal: replies, last_reply_at e people de quem respondeu, ou null) e url. people traz quem pode ser mencionado com @Nome no canal (id, name e me) e, com o app aqua ligado no canal, a aqua no fim (app: "aqua"). unread conta as não lidas da pessoa conectada; mark_read: true marca como lida até a última devolvida (na thread, as menções e respostas dela).
- `send_chat_message` (tracker:write): Envia uma mensagem no canal do projeto em Conversas (project) em nome da pessoa conectada, como na tela: a equipe do projeto lê na hora. Com thread_id, responde na thread daquela mensagem (quem está na thread é avisado); com also_in_channel: true, a resposta também aparece na conversa principal; sem thread_id, vai para a conversa principal. @Nome menciona alguém do canal (veja people em list_chat_messages) e a pessoa recebe na Caixa de entrada, no sino e no aparelho; o código de uma tarefa (ex.: MOB-0012) vira link e cartão. @aqua, com o app aqua ligado no canal, faz a aqua responder na thread da mensagem com os dados do projeto (aqua_replying: true; a resposta chega em seguida, leia com list_chat_messages e thread_id); pedindo para criar ou mudar tarefas do projeto, ela propõe as ações em aqua_actions da resposta, para quem a chamou confirmar (decide_aqua_chat_action). Com person (id ou e-mail) no lugar de project, escreve na conversa direta com essa pessoa da equipe (só as duas leem; ela é avisada no aparelho; sem thread). attachments anexa arquivos à mensagem (fotos, PDF, zip, planilhas, logs; até 5, 15 MB cada e 14 MB somados), cada um em content_base64 com file_name ou pela url pública; com anexo, text pode faltar. Para falar de uma tarefa específica, prefira add_comment nela.
- `react_to_chat_message` (tracker:write): Marca (remove: false, o padrão) ou tira (remove: true) a reação da pessoa conectada a uma mensagem das Conversas (do canal, com project, ou da conversa direta, com person), com um emoji (ex.: 👍, ✅, 🎉). Cada pessoa marca cada emoji uma vez por mensagem; uma mensagem aceita até 20 emojis diferentes. Devolve reacted.
- `edit_chat_message` (tracker:write): Corrige o texto de uma mensagem das Conversas (do canal, com project, ou da conversa direta, com person). Só quem escreveu pode corrigir; no canal, o texto anterior fica no histórico da empresa e quem passa a ser mencionado agora é avisado.
- `delete_chat_message` (tracker:write): Exclui uma mensagem das Conversas (do canal, com project, ou da conversa direta, com person): fica no lugar como excluída, sem o texto. No canal, o texto vai para o histórico da empresa e quem escreveu ou um admin da empresa exclui; na conversa direta, só quem escreveu.
- `suggest_chat_issues` (tracker:read): As tarefas para citar numa mensagem das Conversas (do canal, com project, ou da conversa direta, com person), como a lista que abre ao escrever "#" no campo da tela. Sem q, sugere pelo contexto: as citadas por último na conversa (reason cited), as abertas com a pessoa conectada (mine) e as abertas do projeto do canal mexidas por último (project). Com o começo de um código (MOB-12), completa pelo código; com palavras, usa a busca da aqua (texto e significado). Cada uma traz id, code, title, open, project, reason e url; escreva o code na mensagem (send_chat_message) para virar link e cartão.
- `list_chat_apps` (tracker:read): Os apps das Conversas, que escrevem nos canais sem ser uma pessoa. Nomes, resumos, descrições e permissões vêm no idioma da pessoa conectada. Sem project nem person, o catálogo da empresa: key, name, summary, description, official, built_in (vem com o tracker), company_wide (vem ligado na empresa toda, em todos os canais; o admin só desliga ou religa na empresa), permissions (cada uma com key, description, required: obrigatória, e granted: a empresa concedeu), events, installed, installed_at, channels (em quantos canais está ligado) e all_channels (o admin ligou em todos os canais, inclusive nos novos). Com project, os apps do canal do projeto: can_manage (a pessoa conectada pode ligar e desligar: PO/PM ou admin), can_install (admin) e apps (key, name, summary, built_in, company_wide, installed, enabled, all_channels: ligado em todos os canais pelo admin, e, em task_updates, events, os avisos que o canal recebe, e allowed_events, os que as permissões da empresa deixam escolher). Com person, os apps da conversa direta com a pessoa: active, can_install e apps (key, name, summary, installed, allowed: a empresa liberou nas conversas diretas, enabled e enabled_by: quem ligou). Os oficiais: task_updates (Avisos de tarefas: tarefa criada, status mudou, responsável mudou e, com a permissão comments:read, comentário novo), automations (Automações: escreve quando uma regra de automação manda), google_meet (Google Meet: cria reuniões no canal e, com a permissão chat:direct, na conversa direta; vem account, a conta Google da pessoa conectada: configured, connected e email) aqua (responde quem escreve @aqua; company_wide: vem ligada em todos os canais, installed false quando um admin desligou na empresa) e google_calendar (Google Calendar: pessoal, cada pessoa usa com a própria conta Google, sem ligar por canal, por isso channels vem null; a agenda do dia, o aviso antes de cada reunião e, com a permissão calendar:respond, a resposta aos convites). Use install_chat_app e set_chat_app para mudar, start_meeting para criar ou agendar a reunião, list_meetings para ver as próximas, get_agenda para a agenda de um dia, respond_to_meeting para responder a um convite e set_meeting_reminder para o aviso.
- `install_chat_app` (tracker:write): Instala (o padrão) ou desinstala (uninstall: true) um app das Conversas na empresa, como em Empresa → Apps. Só admin e dono. permissions escolhe o que a empresa concede: as obrigatórias entram sempre e, das opcionais, só as da lista (sem permissions na instalação, só as obrigatórias). Com o app já instalado, permissions troca o que está concedido; tirar chat:direct desliga o app em todas as conversas diretas. Instalado, o PO/PM do projeto ou um admin liga o app em cada canal (set_chat_app), ou all_channels: true liga em todos os canais de uma vez, inclusive nos que forem criados depois (na instalação ou depois; false volta para os canais que ligaram um por um; não vale para os pessoais, como google_calendar). Desinstalar desliga em todos os canais e conversas diretas; as mensagens que o app já escreveu ficam. Os que vêm com o tracker (built_in) não se instalam. Os que vêm ligados na empresa toda (company_wide, a aqua) não têm permissões para escolher nem se ligam por canal: uninstall: true desliga na empresa e instalar liga de novo. Devolve key, installed, permissions (as concedidas) e, quando mudou, all_channels.
- `set_chat_app` (tracker:write): Liga (enabled: true) ou desliga (enabled: false) um app instalado na empresa no canal de um projeto (project), como em "Apps", no topo do canal, ou na conversa direta com uma pessoa (person). No canal, só o PO/PM do projeto ou um admin; em task_updates (Avisos de tarefas), events escolhe o que o canal recebe: issue.created, issue.status_changed, issue.assigned e comment.created (este só com a permissão comments:read concedida; sem events, fica o que estava ou o padrão: tarefa criada e status mudou). Na conversa direta, qualquer uma das duas pessoas, e só apps que a empresa liberou nas conversas diretas (permissão chat:direct, ex.: google_meet); o app conta na conversa quem ligou ou desligou. Os que vêm ligados na empresa toda (company_wide, a aqua) não se ligam por canal: install_chat_app liga e desliga na empresa. Com all_channels (o admin ligou em todos os canais), o canal não desliga, mas ainda escolhe os events. Devolve project ou person e app.
- `start_meeting` (tracker:write): Cria uma reunião no Google Meet na conta Google da pessoa conectada e posta o cartão com o link de entrar no canal do projeto (project) ou na conversa direta com uma pessoa (person), como o botão da câmera ou /meet nas Conversas. O app google_meet precisa estar instalado na empresa e ligado ali (set_chat_app; na conversa direta, com a permissão chat:direct concedida), e a pessoa precisa ter conectado a conta Google pelo navegador (o erro traz o link). title é o assunto (opcional); thread_id cria dentro da thread daquela mensagem (só no canal). Com starts_at, agenda para depois, como "Agendar reunião…" no botão da câmera: cria o evento no Google Calendar da pessoa conectada com a sala do Meet, duration_minutes (padrão 30), time_zone (padrão America/Sao_Paulo) e guests (e-mails ou ids de pessoas da conversa; o Google manda o convite), e o cartão diz quando é; agendar pede que a conta Google tenha permitido o acesso à agenda (senão, o erro traz o link para conectar de novo). Devolve project ou person, message (id, thread_id, url) e meeting (url para entrar, code, title, starts_at e ends_at, null na que começa agora, e guests na agendada).
- `list_meetings` (tracker:read): As próximas reuniões com sala do Google Meet nos próximos 14 dias da agenda Google da pessoa conectada, como "Próximas reuniões" no botão da câmera ou /meet lista nas Conversas (só ela vê; nada vai para a conversa). Pede o app google_meet instalado na empresa e a conta Google conectada com o acesso à agenda (o erro traz o link para conectar). Devolve meetings: title, url (para entrar), code, starts_at, ends_at e calendar_url (o evento no Google Calendar).
- `get_agenda` (tracker:read): A agenda de um dia no Google Calendar da pessoa conectada, como /agenda ou o botão da agenda nas Conversas (só ela vê; nada vai para a conversa). Pede o app google_calendar instalado na empresa e a conta Google conectada com o acesso à agenda (o erro traz o link para conectar). date (AAAA-MM-DD) ou days (a partir de hoje: 0 hoje, o padrão; 1 amanhã) e time_zone (padrão America/Sao_Paulo). Devolve date, time_zone, can_respond (a empresa liberou responder pelo tracker), reminder_minutes (o aviso antes de cada reunião; 0 desligado) e events: id, title, starts_at, ends_at, all_day, meeting_url (a sala do Meet, para entrar), calendar_url e response (yes, maybe, no; pending sem resposta; null quando ela marcou ou não é convidada).
- `respond_to_meeting` (tracker:write): Responde ao convite de uma reunião em nome da pessoa conectada (yes vou, maybe talvez, no não vou), como o Sim, Talvez e Não do cartão da reunião agendada ou da agenda do dia. A resposta vai para o Google Calendar dela e o Google avisa quem marcou. Com message (e project ou person), o cartão da reunião agendada naquela mensagem das Conversas, que passa a contar a resposta; com event_id, um evento da agenda (o id de get_agenda). Pede o app google_calendar com a permissão calendar:respond concedida e a conta Google conectada com o acesso à agenda. Quem marcou a reunião não responde. Devolve response e, no cartão, responses (yes, maybe, no e mine).
- `decide_aqua_chat_action` (tracker:write): Confirma ou cancela uma ação que a aqua propôs na resposta dela num canal das Conversas (criar tarefa, editar, mudar status, comentar, bloquear, acompanhar, planejar, registrar horas, vincular), como o Confirmar e o Cancelar do cartão. As propostas estão em aqua_actions da mensagem da aqua (list_chat_messages): asked_by é quem chamou a aqua, o único que decide (mine: é a pessoa conectada), e cada ação traz id, title, details e state (pending espera decisão; replaced: a aqua propôs de novo na thread e esta deixou de valer). confirm faz a mudança em nome da pessoa conectada, com o perfil dela no projeto do canal, uma vez só; devolve a ação com state done (result com o texto e a url, como a tarefa criada) ou failed (error).
- `set_meeting_reminder` (tracker:write): Quantos minutos antes de cada reunião da agenda Google da pessoa conectada o tracker avisa, no sino e no celular, como em "Aviso antes da reunião" na agenda do dia (app google_calendar). 5, 10, 15 ou 30; 0 desliga. Sem reminder_minutes, só devolve como está: installed, can_respond, account (configured, connected, email e calendar: permitiu a agenda), connect_url e reminder_minutes.
- `list_issues_to_review` (tracker:read): As issues abertas que a aqua ainda não revisou ou que mudaram desde a última revisão, as nunca revisadas primeiro (id, código e título). Para revisar todas de uma vez, use start_aqua_review; para revisar uma a uma, review_issue_with_aqua com batch: true.
- `start_aqua_review` (tracker:write): O botão "Revisar issues abertas" da tela aqua: a aqua revisa no servidor, uma de cada vez, todas as issues abertas que ainda não viu ou que mudaram desde a revisão (até 500), e grava as sugestões. Uma revisão por vez na empresa: se já houver uma rodando, devolve essa. Sem nada pendente, run: null. Acompanhe com get_aqua_review e pare com stop_aqua_review. Exige editar issues e a IA ligada na empresa.
- `get_aqua_review` (tracker:read): O andamento de uma revisão de start_aqua_review: status (running, done, stopped ou failed), total, reviewed, failed, suggestions, error e issues (id, code, title, state: waiting, running, done ou failed; suggestions e error). Sem id, a que roda na empresa ou a última.
- `stop_aqua_review` (tracker:write): Para uma revisão de start_aqua_review: a issue da vez termina, as que faltam ficam sem revisar.
- `review_issue_with_aqua` (tracker:write): A aqua lê a issue (campos, comentários, histórico e os anexos: até 3 prints, 1 PDF e 2 textos ou logs, os mais novos primeiro) e grava sugestões: pedir informações, mover de status, corrigir campos, labels, apontar duplicada ou mover de projeto. Nada muda na issue até alguém aplicar. Devolve a revisão e as sugestões abertas. Exige editar issues no projeto e a IA ligada na empresa. batch: true usa o modelo da revisão em lote (para a fila de list_issues_to_review).
- `apply_aqua_recommendation` (tracker:write): Aplica uma sugestão da aqua pela regra de sempre (comentário, status, campo, label, vínculo de duplicada ou mudança de projeto), em nome da pessoa e com o perfil dela. comment troca o texto do pedido de informações. A sugestão que já não vale (a issue mudou) é recusada: revise de novo.
- `dismiss_aqua_recommendation` (tracker:write): Ignora uma sugestão da aqua por 1, 7 ou 30 dias, ou para sempre ("forever": não volta nas próximas revisões).
- `list_sentry_errors` (tracker:read): Lista os erros que o Sentry mandou para os projetos (o mesmo erro aparece uma vez, com eventos, pessoas afetadas e releases). state: pending (juntando avisos antes da triagem), suggested (a aqua já fez a triagem e espera uma decisão), linked (ligado a uma issue), regressed (voltou depois de a issue ser concluída), merged (igual a outro), dismissed ou muted. triage traz a issue que a aqua escreveu e, em candidate, a issue aberta que parece a mesma. Com project, traz também a integração (último aviso). waiting: true mostra só o que espera decisão, como na tela aqua.
- `decide_sentry_error` (tracker:write): Decide um erro do Sentry como na tela aqua: create_issue cria a issue já preenchida (título, severidade, passos, link do Sentry, alcance, release, ambiente, aparelho e stack resumido; title troca o título), link liga a uma issue do projeto (a candidata da aqua, ou issue) e comenta nela, reopen reabre a issue de um erro que voltou, dismiss ignora por snooze dias ou para sempre. Exige editar issues no projeto (create_issue, criar issue).
- `list_sentry_connections` (tracker:read): As conexões com o Sentry da empresa (Empresa → Integrações → Sentry; só admin e dono): o projeto que recebe o que sobrar, o webhook_url, se o segredo está salvo, a tabela dos projetos do Sentry (sentry_project_targets: prefixo do projeto do tracker, "rules" ou "ignore"), o que fazer com projeto novo do Sentry, as regras na ordem ("/v1/pedidos/* → API", "project=APP → APP"), ambientes, gravidade mínima, triagem pela aqua, o que os avisos já mostraram (seen) e o último aviso. Use antes de save_sentry_connection.
- `save_sentry_connection` (tracker:write): Conecta um Sentry (sem id: project, o projeto que recebe o que sobrar; a resposta traz o webhook_url para criar a Internal Integration no Sentry, e o client_secret entra depois) ou muda uma conexão (com id: só o que vier muda). sentry_project_targets e rules trocam a tabela e as regras inteiras. Só admin e dono.
- `test_sentry_connection` (tracker:read): Mostra para onde iriam os últimos erros (até 20) de uma conexão com o Sentry: com a configuração salva ou, sem gravar nada, com a tabela, as regras, os ambientes e a gravidade que vierem. Cada resultado: result (project, ignored ou outside_environment), project (prefixo), by (table, rule com o número da regra, ou leftover), below_min_level, a rota e as tags que as regras leram. Só admin e dono.
- `search_docs` (tracker:read): Busca na base de conhecimento da empresa (documentos publicados: manuais, regras de negócio, FAQs, processos) e devolve os trechos mais relevantes para a pergunta, com o documento e a seção de cada um. Use antes de responder dúvidas sobre o produto ou os processos da empresa e cite o documento (url). Para o texto inteiro, use get_doc com o slug.
- `list_docs` (tracker:read): Lista os documentos da base de conhecimento que a pessoa lê (título, slug, resumo, projeto e visibilidade), sem o texto. Filtre por projeto, ou "empresa" para os da empresa toda.
- `get_doc` (tracker:read): Abre um documento da base de conhecimento pelo slug (ou id), com o texto inteiro em markdown.
- `save_doc` (tracker:write): Cria ou atualiza um documento da base de conhecimento em nome da pessoa conectada (texto em markdown). Com o slug de um documento que já existe, atualiza: campo omitido fica como está. Documento de projeto: equipe do projeto (PO/PM, QA, dev) ou admin; da empresa toda: só admin. Cria como rascunho (draft) se a visibilidade não for informada; internal publica para a empresa e os agentes, public também na central de ajuda pública.
- `list_status` (tracker:read): Mostra as páginas de status da empresa (Empresa → Status; só admin e dono): os serviços que o cliente vê, cada um com o projeto dono, o status de agora (o pior entre o manual e os incidentes abertos) e a versão no ar; as páginas, com o endereço público (url), o slug, se estão publicadas e qual é a principal (primary, a que abre em /status); e os incidentes e manutenções recentes (phase active primeiro, depois upcoming, depois closed). Use antes de save_status_service, save_status_page e open_status_incident.
- `get_status_page` (tracker:read): Uma página de status com os blocos na ordem: o tipo, o título, os projetos ou serviços escolhidos, os escondidos e o que cada bloco mostra hoje (shows, com o status).
- `get_status_incident` (tracker:read): Um incidente ou manutenção da página de status com a linha do tempo que o cliente lê (status, texto, quem publicou e quando), os serviços afetados e a janela.
- `save_status_service` (tracker:write): Cria um serviço da página de status (sem id: name e project) ou edita um (com id: só o que vier muda). O serviço é o que o cliente reconhece (o app, a API, o portal); o status manual vale até mudar de novo, e os incidentes abertos pioram o que o cliente vê sem trocar o manual. Serviço novo entra sozinho nos blocos do projeto dele. Monitoramento automático: check_url (healthcheck a cada minuto) e sentry_alerts (erro novo do Sentry do projeto deixa Lento). Só admin e dono.
- `save_status_page` (tracker:write): Cria uma página de status (sem id: title) ou edita uma (com id: só o que vier muda). blocks traz todos os blocos na ordem e troca os que havia: {"kind": "projects", "projects": ["MOB"]} (um projeto, o título é o nome dele), {"kind": "projects", "title": "Plataforma", "projects": ["PLT", "API"], "hidden_services": ["Fila interna"]} (grupo de projetos) ou {"kind": "services", "title": "Destaques", "services": ["App do motorista"]} (avulsos, por id ou nome). published: true abre o endereço público (url), sem conta. slug: endereço legível /status/<slug> no endereço da empresa; primary: true faz a página abrir em /status. Aparência: brand_name (marca da página), accent_color (#rrggbb), links (até 4 botões no topo, {"label": "Suporte", "url": "https://..."}), show_history e show_past_incidents (false esconde); a logo se envia pela tela. Só admin e dono.
- `open_status_incident` (tracker:write): Abre um incidente (kind incident: impacto e status investigating → identified → monitoring → resolved) ou agenda uma manutenção (kind maintenance: starts_at e ends_at, status scheduled → in_progress → completed) nos serviços afetados (id ou nome), com a primeira atualização que o cliente lê em message. Enquanto está aberto (ou na janela da manutenção), os serviços afetados aparecem com o impacto nas páginas de status. Só admin e dono.
- `update_status_incident` (tracker:write): Publica uma atualização num incidente ou manutenção (message, com status e, no incidente, impact): resolved ou completed encerra, e ele vai para Incidentes anteriores. Também corrige os detalhes sem atualização nova (title, services, starts_at, ends_at; impact sem message). Só admin e dono.
- `delete_status_item` (tracker:write): Exclui um serviço (sai de todas as páginas e incidentes), uma página (o endereço público para de abrir) ou um incidente (some da página, inclusive de Incidentes anteriores; para encerrar, use update_status_incident com resolved). Só admin e dono.
- `list_idea_boards` (tracker:read): Mostra os quadros de ideias da empresa (Empresa → Ideias; só admin e dono), onde os clientes votam no que querem ver: título, se está publicado, o endereço público (url, que o cliente abre sem conta), quantas ideias tem e quantas sugestões de clientes esperam a equipe (pending_suggestions). Use antes de get_idea_board e save_idea.
- `get_idea_board` (tracker:read): Um quadro de ideias: projetos, quem pode ver e votar (access e allow_list), se aceita sugestões, as ideias publicadas (mais votadas primeiro, com status que o cliente vê, issue ligada, votos e quantos comentários) e as sugestões dos clientes esperando a equipe (pending_suggestions, com o e-mail de quem sugeriu).
- `get_idea` (tracker:read): Uma ideia com os votos e os comentários (de clientes, com o nome e o e-mail, e da equipe), do mais antigo para o mais novo.
- `save_idea_board` (tracker:write): Cria um quadro de ideias (sem id: title) ou edita um (com id: só o que vier muda). projects troca os projetos do quadro (prefixo, slug ou id). published: true abre o endereço público (url). access link deixa qualquer um com o link ver (votar pede o e-mail confirmado por código); restricted mostra as ideias só a quem confirmar um e-mail de allow_list (["@cliente.com.br", "ana@gmail.com"]). Só admin e dono.
- `save_idea` (tracker:write): Cria uma ideia num quadro (sem id: board e title, ou board e issue) ou edita uma (com id: só o que vier muda). Com issue (código de um projeto do quadro), o status que o cliente vê segue o da issue (a fazer: Planejado; em andamento: Em andamento; concluída: Lançado; cancelada: Não vamos fazer); sem issue, vale status. published: true publica a sugestão de um cliente; para recusar, delete_idea_item. Quando o status muda para planned, in_progress, released ou closed, quem votou recebe um e-mail. Só admin e dono.
- `reply_to_idea` (tracker:write): Responde numa ideia publicada como a equipe: no quadro, o cliente vê "Equipe <empresa>". Devolve a ideia com os comentários. Só admin e dono.
- `delete_idea_item` (tracker:write): Exclui um quadro (com as ideias e os votos; o endereço público para de abrir), uma ideia (inclusive a sugestão de um cliente, que é como se recusa) ou um comentário (o id vem em get_idea). Só admin e dono.
- `create_project` (tracker:write): Cria um projeto como o Novo projeto da tela (quem configura algum projeto, ou admin): nome, prefixo do código (MOB vira MOB-0001), tipo (software, design, admin ou support), modelo do fluxo (software_qa, software ou qa), início e fim previstos (start_date e target_date, AAAA-MM-DD) e, no mesmo pedido, a cor e um ícone pronto (library_icon, de list_project_icons). Quem cria entra como PO/PM; as outras pessoas entram pela tela. Devolve o projeto como em list_projects.
- `get_project` (tracker:read): O Resumo de um projeto, como a primeira aba dele na tela: o projeto como em list_projects, início e fim previstos com os dias que passaram e que faltam (time_progress: fração do tempo previsto que já passou), quantas issues há, abertas, concluídas e atrasadas, a sprint ativa, a próxima release, até 5 atrasadas, os 5 próximos prazos e quem participa. project: prefixo, slug ou id.
- `get_timeline` (tracker:read): O Cronograma de um projeto, como a aba da tela: as issues com início ou prazo (até 300, agrupadas por épico ou módulo, com responsável, sprint, horas e se estão atrasadas), as dependências "bloqueia" (conflict quando a que bloqueia termina depois que a outra começa), as sprints, as releases planejadas, o início e o fim previstos do projeto e as issues abertas ainda sem datas. Para mudar as datas de uma issue use plan_issue; do projeto, plan_project. project: prefixo, slug ou id.
- `plan_project` (tracker:write): Define o início e o fim previstos de um projeto (quem configura o projeto, ou admin), que aparecem no Resumo e na linha do projeto no Cronograma (get_timeline). Só o que vier muda; null tira a data. Datas em AAAA-MM-DD, e o início não pode vir depois do fim. Para as datas de uma issue use plan_issue.
- `set_organization_logo` (tracker:write): Troca ou tira um dos logos da empresa (Empresa → Geral; só admin e dono). kind square é o quadrado (ícone, seletor de empresa, menu recolhido) e wide o horizontal (topo do menu, login, convites, e-mails, portal). A imagem vai em image_base64 (PNG, JPG ou WebP até 2 MB); remove: true tira. Devolve os endereços públicos dos dois logos.
- `list_project_icons` (tracker:read): A biblioteca dos 108 ícones prontos para projetos (mobilidade, energia, comércio, tecnologia e outros grupos), com slug, nome e o desenho (url, em preto com fundo transparente). query filtra pelo nome, slug ou grupo, sem acento. Para usar: set_project_brand com library_icon, que pinta o desenho na cor do projeto.
- `set_project_brand` (tracker:write): Troca a cor e o ícone de um projeto (PO/PM ou admin no projeto); só o que vier muda. color: um nome da paleta (azul, violeta, rosa, vermelho, laranja, âmbar, verde, turquesa, grafite) ou #rrggbb; null volta à automática. O ícone é um de três: library_icon (slug de list_project_icons, pintado na cor; para repintar depois de trocar a cor, mande library_icon de novo), image_base64 (ícone próprio, quadrado, PNG, JPG ou WebP até 2 MB) ou remove_icon: true (volta ao prefixo na caixa da cor). Devolve a cor e o endereço do ícone.
- `list_beta_invites` (tracker:read): Lista os convites do beta do tracker (só a equipe da plataforma), os mais novos primeiro: status (active, used, expired, revoked), e-mail, anotação, usos (uses de max_uses), validade, quem gerou (created_by) e quem usou cada um com a empresa que criou (redemptions). O link não volta aqui: só ao criar ou reenviar.
- `create_beta_invite` (tracker:write): Gera um convite do beta (só a equipe da plataforma): quem abre o link entra ou cria a conta aquarela ID e cria a própria empresa, como dono. Para chamar alguém para uma empresa que já existe, não é isto. Com email, o link vai por e-mail e só vale para a conta desse e-mail (uma empresa só); sem email, max_uses diz quantas empresas o link cria. days é a validade (padrão 14). Devolve url (o link, que não aparece de novo), expires_at e emailed (sent, disabled ou failed).
- `resend_beta_invite` (tracker:write): Reenvia um convite do beta com e-mail (só a equipe da plataforma): gera um link novo (o anterior para de valer), recomeça a validade e manda de novo para o e-mail. Devolve url, expires_at e emailed.
- `revoke_beta_invite` (tracker:write): Revoga um convite do beta (só a equipe da plataforma): o link para de valer para quem ainda não criou a empresa. As empresas que ele já criou continuam.
- `get_platform_overview` (tracker:read): Números do tracker inteiro (só a equipe da plataforma, seção Visão geral): empresas ativas, suspensas e arquivadas, pessoas, projetos e issues, uso de IA nos últimos 30 dias (chamadas, custo em dólar e empresas que usaram), convites do beta e a versão.
- `list_platform_organizations` (tracker:read): Lista todas as empresas do tracker (só a equipe da plataforma, seção Empresas), por nome: status (active, suspended, archived), donos, membros, projetos, issues, chamadas de IA nos últimos 30 dias, última atividade e, na suspensa, quando e por quê. status e query filtram.
- `suspend_organization` (tracker:write): Suspende uma empresa (só a equipe da plataforma que gerencia Empresas): ninguém da empresa entra no app, na API, no MCP ou no Slack até reativar; os dados ficam. reason vai para o registro da plataforma. Confirme com a pessoa antes: a empresa inteira perde o acesso na hora.
- `reactivate_organization` (tracker:write): Reativa uma empresa suspensa (só a equipe da plataforma que gerencia Empresas): quem era membro volta a entrar.
- `prepare_demo_account` (tracker:write): Prepara a conta de demonstração das revisões da Apple e da OpenAI (só a equipe da plataforma que gerencia Empresas): cria a Empresa Demo (empresa-demo) com dois projetos, 16 issues em vários status, releases, um ciclo de teste e um documento, com a conta do email como dona e três colegas fictícios. O revisor entra com a conta aquarela ID desse e-mail, criada lá com senha. Se a Empresa Demo já existe, só garante a conta ativa e dona dela (created false). Recusa e-mail da equipe da plataforma e conta que já participa de outra empresa. Confirme com a pessoa antes de rodar em produção.
- `list_platform_team` (tracker:read): Lista a equipe da plataforma (só quem é da equipe): o dono (role owner, pode tudo) e os demais (role staff) com o nível de cada seção em sections (view ou manage; ausente = não vê). Seções: overview, organizations, support (entrar nas empresas como suporte), invites, ai, plans, audit. Mudar a equipe é só na tela, pelo dono.
- `list_platform_audit` (tracker:read): O que a equipe fez na plataforma (só a equipe, seção Registro), mais novo primeiro: mudanças na equipe, empresas criadas, suspensas, reativadas, arquivadas e excluídas, convites do beta e cada entrada como suporte numa empresa de cliente. Filtros: action, organization_id, actor_id; before (a created_at da última linha) pagina.
- `list_platform_plans` (tracker:read): Os planos do tracker (só a equipe da plataforma, seção Planos), na ordem da lista: key, name, description, public (aparece para as empresas), active, is_default (o plano de quem não tem assinatura), ai_credits_per_cycle (null: o padrão do servidor), storage_gb (o espaço de arquivos em GB; null: o padrão do servidor, 10 GB), features (o que o plano inclui), prices (currency, interval, amount_minor na unidade mínima da moeda, amount e formatted) e subscriptions (quantas empresas estão nele).
- `save_platform_plan` (tracker:write): Cria um plano ou edita o que tem a mesma key (só a equipe da plataforma que gerencia Planos). prices é a lista inteira de preços, um por moeda e período, sem conversão entre moedas; sem ela, os preços ficam como estão. Sempre há um plano padrão: marcar is_default tira a marca do outro. Ainda não há cobrança.
- `list_platform_subscriptions` (tracker:read): O plano e a moeda de cada empresa (só a equipe da plataforma, seção Planos): organization, currency, plan (key e name), plan_source (subscription ou default), subscription (status, interval, datas e note) e billing_details (kind, legal_name, email, country, tax_id_type e tax_id) e plan_request (o plano pago que a empresa pediu, ou null; pôr a empresa nesse plano com save_organization_subscription atende o pedido).
- `save_organization_subscription` (tracker:write): Põe uma empresa num plano ou muda o estado (status), o período (interval), as datas e a moeda dela (só a equipe da plataforma que gerencia Planos). Só os campos enviados mudam. Sem cobrança: registra o combinado com o cliente. Confirme com a pessoa antes.

## ¿Ya pasó antes? (antes de crear una issue)

tracker guarda una ficha de contexto de cada issue, escrita por aqua (el problema en palabras neutras, dónde ocurre y los términos que alguien usaría para contar el mismo problema de otra forma). "¿Ya pasó antes?" cruza esas fichas y aqua dice, de cada parecida, si es el mismo problema o solo relacionado, con el motivo en una frase. Los agentes usan el mismo camino para no abrir duplicadas:

1. Antes de crear, pregunta: `search_similar` en el MCP (o `GET /search`) con `query` = título y descripción del problema (el mensaje de error, el relato del cliente). Para ver también los tickets de la Mesa de ayuda, `kinds: ["issue", "ticket"]`.
2. `relation: "same"` (el mismo problema): no crees otra. Comenta en la issue que ya existe (`add_comment`) con lo nuevo (otro cliente, otro entorno, la evidencia) y dile a la persona cuál es. Si la que existe ya se cerró y el problema volvió, crea la nueva y vincúlala con `link_issues` (`duplicates`), diciendo que volvió.
3. `relation: "related"`: créala y vincúlala con `link_issues` (`relates_to`); el `reason` explica la relación y sirve para el comentario.
4. Después de crear, `create_issue` devuelve `similar` con las parecidas de la issue nueva que aqua confirmó (en la API, `POST /issues?similar=true`): vincula las que tengan sentido.

Sin aqua activada en la empresa, `explained` vuelve `false` y vale el `likely_duplicate` de la búsqueda. Para un lote (importación, CI), usa `external_id`, que ya evita repetir la misma issue.

## API REST

Para CI, scripts e importación desde otras herramientas. JSON en los dos sentidos.

- Base: https://tracker.aquarela.app/api/v1
- Autenticación: token personal en el encabezado `Authorization: Bearer <token>`. La persona crea el token en https://tracker.aquarela.app/account/tokens, elige la validez (30, 90, 180, 365 días) y si puede crear issues; el token vale para la organización en la que se creó, aparece una sola vez y se puede revocar en la misma pantalla (hasta 20 activos por persona). Los tokens OAuth del MCP no valen en la API.
- Las mismas reglas de la interfaz: el token ve solo los proyectos de la persona y crea issues solo donde su perfil lo permite. Una persona desactivada o quitada de la organización apaga el token al instante.
- Enlaces: el `url` de una issue es el enlace canónico por el código, `https://tracker.aquarela.app/issues/MOB-0012?org=<slug de la empresa>`, que se lee, se escribe y sigue valiendo después de mover la issue de proyecto (el código anterior también la abre). El prefijo es único solo dentro de la empresa, así que `org` dice de cuál: la app cambia a ella cuando la persona participa. Sin `org`, `/issues/MOB-0012` busca primero en la empresa seleccionada y después en las demás de la persona (si el código existe en más de una, pregunta en cuál abrir); `/issues/<id>` también funciona, en cualquier empresa de la persona.
- Límite: 120 llamadas por minuto por token (429 con Retry-After).
- Errores: `{"error": "<código>", "message": "<texto>"}`, con 400 (datos inválidos, y `details` por campo), 401 (sin token o token inválido), 403 (perfil o token sin permiso), 404 (también para un proyecto en la papelera y sus issues: eliminar y restaurar proyectos queda en la app, solo admin), 409 (ya existe), 413, 415, 422 (rechazado por las reglas del proyecto), 429. El `message` viene en el idioma de la persona del token (el `locale` de `GET /me`; en Automático, el `Accept-Language` del pedido); el `error` no cambia.
- `GET /version` (público, sin token): la versión de tracker en producción, `{"version": "0.2.0", "commit": "5f83c7b"}` (`commit` es el deploy, `null` fuera de él). La tag `v<version>` en GitHub marca cada versión. `GET /me` y whoami traen lo mismo en `server`.

Markdown: todo texto largo es markdown. En la issue, `description` y los demás textos largos (`preconditions`, `steps_to_reproduce`, `actual_result`, `expected_result`, `impact`, `suggested_solution`, `root_cause`, `fix_notes`) y el texto de los comentarios; fuera de ella, las notas de la release, la precondición, los datos, los pasos y el resultado esperado de los casos de prueba, el comentario de cada resultado de prueba, la descripción y las respuestas de las ideas, las actualizaciones de incidente de la página de estado, el comentario de las horas y el de la decisión de aprobación. Vale `**negrita**`, `*cursiva*`, `~~tachado~~`, listas con `-` o `1.` (sublista con dos espacios de sangría), código entre comillas invertidas (una para un fragmento, tres para un bloque), `[link](https://...)`, `> cita` y títulos con `##`. La API devuelve el texto como se escribió (markdown crudo); las pantallas de tracker (web y app) y los correos lo muestran con formato. El HTML no se interpreta.

### Cuenta, personas y marca

- `GET /me` (tracker:read): la persona (`user`, con `id`, `name`, `email`, `locale` y `avatar_url`), la organización, los proyectos con el perfil en cada uno, el token y la versión de tracker (`server`). Cada empresa de `organizations` trae `slug`, `public_url` (la dirección pública de la empresa, como `https://suaempresa.centraldeajuda.ia.br`: la raíz es el centro de ayuda, `/status` la página de estado publicada y `/suporte/...` e `/ideias/...` el portal de la mesa de ayuda y los tableros de ideas; `null` con la dirección pública desactivada), `app_url` (la empresa en tracker, `null` sin subdominio por empresa) y `brand` con `square_logo_url` y `wide_logo_url` (públicos, `null` sin el logo). whoami trae `public_url` también.
- Idioma: `user.locale` (en `GET /me` y en whoami) es el idioma que la persona eligió en Tu cuenta: `pt-BR`, `en` o `es`; `null` es el automático, por el idioma del navegador o del dispositivo (sin ninguno de los tres, portugués). `PATCH /me` (tracker:write) con `{"locale": "en"}` lo cambia (`null` vuelve al automático) y devuelve `{"locale": ...}`. Vale para las pantallas, los correos, los avisos en el dispositivo y las apps, en todas las empresas de la persona (en automático, los correos y avisos siguen el último idioma que pidió el navegador o el dispositivo de la persona); no traduce lo que ya se escribió en tareas y conversaciones. Una app que muestra la interfaz en el idioma del dispositivo usa `user.locale` cuando no es `null`. Las frases listas que devuelve la API (el `text` de `GET /notifications`, de `GET /inbox`, de `GET /issues/{ref}/history` y la actividad de `GET /people/{ref}`, también en `list_inbox`, `get_issue_history` y `get_person`; los `role_label` del perfil también) vienen en ese idioma, con los nombres de estados y tipos de las plantillas traducidos; en automático, por el `Accept-Language` del pedido. En el MCP: `set_language`.
- `GET /me/deletion` (tracker:read): lo que haría eliminar la cuenta ahora: `email`, `can_delete` (con `blocker`, el motivo, cuando no se puede: el dueño de la plataforma pasa el rol antes) y `organizations`, cada una con `outcome` (`deleted`: solo participa la persona y la empresa se elimina entera; `owner_transferred`: es la única dueña y `new_owner` pasa a ser dueño; `left`: solo sale). `DELETE /me` (tracker:write) con `{"confirm_email": "<correo de la cuenta>"}` elimina, sin vuelta atrás: borra nombre, correo, foto, login, tokens, conexiones y preferencias y la cuenta aquarela ID (`aquarela_id`: `deleted`, o `pending` cuando el soporte la termina allá); lo que la persona escribió queda con cada empresa como "Cuenta eliminada". El token deja de valer al instante. No existe en el MCP: eliminar la cuenta es solo de la propia persona, en el sitio, en la app o en la API.
- `GET /people/{me, id, correo o nombre}` (tracker:read): el perfil de una persona de la empresa, como en la pantalla /people/<id>. `person` (`id`, `name`, `email`, `active`, `organization_role` y `organization_role_label`, `member_since`, `url`, el perfil en la pantalla, y `avatar_url`), `all_projects` (`true` para admins y dueños de la empresa, que valen en todos los proyectos), `projects` (`name`, `prefix`, `slug`, `role`, `role_label`), `counts` (`open_assigned`, `open_qa`, `done_last_30_days`), `open_issues` (las más recientes abiertas con ella como responsable: `code`, `title`, `status`, `severity`, `project`, `updated_at`, `url`) y `activity` (hasta 20 comentarios y cambios, los más recientes primero: `kind` `comment` o `history`, `at`, `issue`, `text` y, cuando cambió un valor, `from` y `to`). Solo entran los proyectos y las issues que ve el token; las horas registradas, solo para quien ve horas. Quien solo sigue los proyectos (parte interesada) ve solo a quienes participan en ellos, con `email` `null`. En el MCP: `get_person`.
- Foto de la persona: `avatar_url` (en `GET /me`, `GET /people/{ref}` y `author_avatar_url` en los mensajes de la conversación) es `GET /people/{id}/avatar` (tracker:read, la imagen, con el token; 404 sin foto), `null` sin foto. La dirección cambia (`?v=`) en cada cambio, así que puede quedar en caché por la dirección: con la foto cambiada en la pantalla, el `avatar_url` nuevo llega en la próxima lectura. `PUT /me/avatar` (tracker:write) cambia la propia foto: `multipart/form-data` con la imagen en el campo `avatar`, o JSON `{"image_base64": "...", "file_name": "foto.jpg"}` (una imagen de más de 1,5 MB va en multipart). PNG, JPG o WebP hasta 2 MB; la pantalla y las apps recortan en cuadrado y mandan un JPG de hasta 512 px, y conviene hacer lo mismo (la foto aparece en un círculo). `DELETE /me/avatar` la quita (quedan las iniciales). La respuesta trae `id`, `name` y `avatar_url`. Quien comparte una empresa con la persona ve la foto. En el MCP: `set_avatar`.
- `GET /brand` (tracker:read): las marcas para las apps: la empresa (`organization`, con `slug`, `public_url` y `app_url` como en `GET /me`) y sus logos (`organization.square_logo_url` y `wide_logo_url`), la firma "tracker by aquarela" con los archivos (`product`; `product.by_url` es adonde lleva el "by aquarela", el sitio de Aquarela, en una pestaña o en el navegador), las reglas y `placements`, dónde va cada una (`place`, `organization`: `square`, `wide` o `null`; `product`: `icon`, `signature`, `signature_large`, `tab_title`, `header` o `null`). tracker by aquarela nunca desaparece: donde la empresa ocupa la parte de arriba, la firma pasa al pie.
- `PUT /brand/logos/{square|wide}` (tracker:write, admins y dueños): cambia el logo cuadrado (ícono, selector de empresa, menú contraído) o el horizontal (parte de arriba del menú, login, invitaciones, correos, portal). `multipart/form-data` con la imagen en el campo `logo`, o JSON `{"image_base64": "...", "file_name": "logo.png"}` (el cuerpo JSON va hasta 2 MB, así que una imagen de más de 1,5 MB va en multipart). PNG, JPG o WebP hasta 2 MB; el cuadrado con ancho y alto parecidos (al menos 64 px), el horizontal más ancho que alto. `DELETE /brand/logos/{square|wide}` lo quita. La respuesta trae la empresa y las dos direcciones. En el MCP: `set_organization_logo`.

### Proyectos

- `GET /projects` (tracker:read): proyectos con el tipo de proyecto (`kind`: `software`, `design`, `admin` o `support`, y el nombre en `kind_label`), tipos de issue (el nivel de cada uno y, cuando lo hay, el `template`, la plantilla de los campos de texto), flujo de estados, campos personalizados, etiquetas, módulos, el color del proyecto (`color`, `#rrggbb`) y el ícono (`icon_url`, con el mismo token, o `null` sin ícono: muestra el prefijo en una caja del color) y el inicio y el fin previstos (`start_date`, `target_date`, AAAA-MM-DD o `null`). Úsalo antes de crear issues.
- `GET /projects/{prefijo, slug o id}` (tracker:read): el Resumen del proyecto, su primera pestaña en la pantalla. `project` (como en `GET /projects`), `url`, `dates` (`start_date`, `target_date`, `days_elapsed` desde el inicio, `days_remaining` hasta el fin, negativo cuando ya pasó, y `time_progress`, la fracción del tiempo previsto que ya pasó, de 0 a 1, con las dos fechas), `issues` (`total`, `open`, `done`, `late`), `active_sprint` (o `null`, con `days_left`), `next_release` (la de fecha planificada más cercana que todavía no salió, o `null`), `late` y `upcoming` (hasta 5 issues abiertas con el plazo vencido y con plazo por delante: `code`, `title`, `due_date`, `assignee`, `url`) y `people` (`name`, `role`). En el MCP: `get_project`.
- `POST /projects` (tracker:write; quien configura algún proyecto, o admin): crea un proyecto, `{"name": "App del conductor", "prefix": "MOB", "kind": "software", "workflow": "software_qa", "description": "...", "color": "verde", "library_icon": "carro", "start_date": "2026-11-03", "target_date": "2027-02-26"}` (`start_date` y `target_date`, inicio y fin previstos, opcionales). `kind`: `software` (por defecto), `design`, `admin` o `support`; `workflow` (software): `software_qa` (por defecto), `software` o `qa`. Nombre y prefijo únicos en la empresa (422 cuando ya existen, incluso en la papelera). Quien crea entra como PO/PM. Devuelve `project` como en `GET /projects` (y `warning` si no se pudieron guardar el color o el ícono; inténtalo de nuevo con `PATCH /projects/{ref}/brand`). Crear a partir de otro proyecto queda en la app. En el MCP: `create_project`.
- `PATCH /projects/{prefijo, slug o id}` (tracker:write; PO/PM o admin en el proyecto): inicio y fin previstos, `{"start_date": "2026-11-03", "target_date": "2027-02-26"}`; solo cambia lo que venga y `null` lo quita. El inicio no puede venir después del fin (422). Devuelve `project`, `start_date` y `target_date`. En el MCP: `plan_project`.
- `PATCH /projects/{prefijo, slug o id}/brand` (tracker:write, PO/PM o admin en el proyecto): color e ícono; solo cambia lo que venga. JSON `{"color": "verde", "library_icon": "carro-eletrico"}`: `color` es un nombre de la paleta, en portugués (azul, violeta, rosa, vermelho, laranja, âmbar, verde, turquesa, grafite), o `#rrggbb`, y `null` vuelve al color automático; el ícono es uno de tres: `library_icon` (un ícono listo, pintado en el color del proyecto), `image_base64` (un ícono propio, cuadrado, PNG, JPG o WebP hasta 2 MB; o `multipart/form-data` con la imagen en el campo `icon` y los demás campos en texto) o `"remove_icon": true` (vuelve al prefijo en la caja del color). El ícono listo se pinta en el momento: cambiar solo el color no lo vuelve a pintar, así que manda `library_icon` de nuevo junto con el color nuevo. En multipart, `color` vacío vuelve al automático. Devuelve `color`, `color_chosen` e `icon_url`. En el MCP: `set_project_brand`.
- `GET /project-icons` (tracker:read): la biblioteca de los 108 íconos listos, por grupo, con `slug`, `name` y `url` (el dibujo en negro, fondo transparente); `?query=eletrico` filtra por nombre, slug o grupo, sin tildes (los nombres están en portugués). En el MCP: `list_project_icons`.
- `GET /projects/{prefijo, slug o id}/people` (tracker:read): personas activas del proyecto, con id y perfil. Úsalo para elegir al responsable.
- `GET /projects/{prefijo, slug o id}/sprints` (tracker:read): la planificación del proyecto: sprints abiertos con las issues y los puntos, el comienzo del backlog (`backlog.total` y las primeras issues, en orden de prioridad) y la velocidad de los últimos sprints.
- `GET /projects/{prefijo, slug o id}/timeline` (tracker:read): el Cronograma del proyecto. `project` (con `start_date` y `target_date`), `url`, `can_plan` (si la persona cambia las fechas de las issues), `items` (las issues con inicio o plazo, hasta 300, primero las abiertas y las de fecha más cercana a hoy: `code`, `title`, `status`, `category`, `closed`, `group` y `group_kind`, la épica o el módulo de la fila, `is_epic`, `sprint`, `assignee`, `start_date`, `due_date`, `first_day` y `last_day` de la barra, `late`, `estimate_minutes`, `spent_minutes`, `url`), `truncated`, `dependencies` (`blocker`, `blocked` y `conflict`, cuando la que bloquea termina el mismo día o después del inicio de la bloqueada), `sprints`, `releases` (con fecha planificada) y `unscheduled` (`total` y hasta 20 abiertas sin inicio ni plazo). Para cambiar fechas: `PATCH /issues/{ref}/plan` (la issue) y `PATCH /projects/{ref}` (el proyecto). En el MCP: `get_timeline`.
- `GET /projects/{prefijo, slug o id}/repositories` (tracker:read): repositorios de GitHub y proyectos de GitLab conectados, con el estado que aplica cada evento, el último evento recibido y `connection`: `github_app` (la app de GitHub de la empresa), `gitlab_token` (tracker creó y mantiene el webhook en GitLab) o `webhook` (configurado a mano). Conectar y desconectar queda en la app.
- `GET /panel?project=` (tracker:read): el panel de pared de un proyecto en una llamada (tablero, mesa de ayuda, releases, la semana, gráficos y `aqua.topics`, las preguntas listas de aqua, o `null` sin IA). `GET /panel/aqua?project=&topic=` (tracker:read): la respuesta de aqua a una de esas preguntas (`overview`, la de por defecto, `attention`, `week`, `releases` y `support`, esta solo en un proyecto con mesa de ayuda), en viñetas, con `generated_at`; la misma pregunta repite la respuesta durante 30 minutos.

### Issues

- `GET /issues` (tracker:read): búsqueda. Query: `project`, `status` (claves del flujo del proyecto), `category` (`todo`, `in_progress`, `done`), `severity`, `priority`, `type`, `nature` (`problem`, `improvement`, `task`: la naturaleza del tipo, que vale en cualquier proyecto) (listas separadas por coma), `label`, `assignee` (`me`, `none` o id), `open_only` (true), `blocked_only` (true: solo las que tienen la marca de bloqueo), `mine` (true: solo las de la persona del token como responsable o QA, como "Mis tareas" en la pantalla), `watching` (true: solo las que sigue), `q` (texto libre: código, título, descripción y comentarios), `query` (consulta avanzada, escrita en portugués, ej.: `projeto = MOB e status = Aberto e responsavel = eu`), `saved_filter` (nombre de un filtro guardado), `created_from` y `created_to` (AAAA-MM-DD), `limit` (hasta 50), `offset`, `sort` (`code`, `title`, `status`, `severity`, `assignee` o `updated`), `order` (`asc` o `desc`) y `done_last` (true: las completadas, grupo `done`, al final, antes del orden pedido). Sin `sort`, las más recientes primero. La API no oculta nada por defecto: sin filtro de estado, vienen también las completadas. La lista de tareas de la pantalla oculta las completadas hasta que la persona pide "Mostrar completadas" (y entonces las muestra al final); para reproducirlo, usa `open_only=true` o `done_last=true`. En el MCP, los mismos `open_only` y `done_last` de `search_issues`. Cada issue viene con `close_reason` (en Cerrado), `blocked` y `blocked_reason` (la marca de bloqueo), `retest_failed` (rechazada en el último retest y todavía no aprobada), `assignee`, `qa_owner`, `sprint`, `release`, `releases` (todas las entregas como `"GOP 1.4.8-ota002"`, la principal primero), `module`, `parent` y `epic` (códigos), `labels`, `start_date`, `due_date`, `external_id` y `edit_version` (para `PATCH` y el lote, sin abrir cada issue); `include=description,custom_fields` agrega la descripción y los campos personalizados.
- `GET /search?q=<texto>` (tracker:read): "¿ya pasó antes?". Búsqueda híbrida, por significado y por palabras, en issues (con los comentarios) y, con `kinds=issue,ticket`, en los tickets de la Mesa de ayuda (solo para quien atiende). `issue=<código>` en lugar de `q` busca las parecidas a esa issue, sin ella. `project`, `open_only` (true) y `limit` (hasta 30, por defecto 8). Cada resultado trae `kind`, `code`, `title`, `status_label`, `open`, `project`, `score` (0 a 1), `similarity` (del vector, cuando la encontró por significado), `matched_by` (`text`, `meaning`), `likely_duplicate` y `url`; `mode` es `hybrid` o `text` (solo por palabras); `semantic` dice si la búsqueda por significado está activada en la empresa: `on`, `indexed` (cuántos elementos ya están en el índice; crece cada minuto mientras el índice se arma) y, desactivada, `reason` (`no_vector`: la búsqueda por significado no está disponible en este servidor; `no_key`: aqua desactivada, clave propia de la empresa o sin créditos de IA). Con `on` true y `mode` `text`, la búsqueda por significado no respondió a tiempo o la persona pasó el límite de búsquedas por significado. Con `issue`, la búsqueda parte de la ficha de contexto que aqua escribe de cada issue (`aqua_context` de `GET /issues/{ref}`). `explain` (por defecto true; `false` devuelve solo la lista de la búsqueda, más rápida) hace que aqua compare cada hallazgo con la referencia: quita lo que solo comparte palabras y completa `relation` (`same`: el mismo problema; `related`: relacionado) y `reason` (la relación en una frase); `explained` dice si lo comparó (sin aqua activada, `false` y la lista de la búsqueda). Antes de crear una issue, busca aquí para no duplicar (ver "¿Ya pasó antes?" arriba). En el MCP: `search_similar`.
- `GET /issues/{código o id}` (tracker:read): la issue con `status` (`key`, `label`, `category`, `close_reason` en Cerrado con `key` y `label`, y `retest_failed_at` cuando se rechazó en el último retest), `blocked` (`since` y `reason`, o `null`), `releases` (todas las entregas, la principal primero con `primary: true`), padre y `epic` (el padre, o el padre del padre en una subtarea), hijas, sprint, puntos, estimación, fechas, campos personalizados, etiquetas, vínculos, quién la sigue, comentarios, historial (`from`/`to` con los valores guardados y `text`, como en la pantalla: nombres de releases, personas y sprints; `from_label`/`to_label` cuando la pantalla muestra de/a), `aqua_summary` (el último resumen de la conversación hecho por aqua en la pantalla: `happened`, `pending`, `ball` con quién tiene la pelota y `outdated` cuando hubo comentarios o cambios después; `null` sin resumen), `aqua_context` (la ficha de contexto que aqua escribe sola, en segundo plano, para "¿Ya pasó antes?": `summary` con el problema en palabras neutras, `area`, `terms`, `generated_at` y `outdated` cuando la issue cambió y la ficha todavía se va a rehacer; `null` con aqua desactivada o antes de la primera ficha) y `edit_version` (para `PATCH`). `?fields=status,releases` trae solo esas partes (`id`, `code`, `url` y `edit_version` siempre): sirve para revisar muchas issues sin traer comentarios e historial.
- `GET /issues/{código o id}/comments` (tracker:read): todos los comentarios, cada uno con `id`, `author`, `text`, `at`, `edited_at`, `deleted` (quién lo eliminó y cuándo; el texto viene vacío), `mentions` y las respuestas (`replies`).
- `POST /issues/{código o id}/comments` (tracker:write): comenta, `{"text": "...", "parent_id": "<id del comentario respondido>"}`. `@Nome` menciona a alguien del proyecto. `"attachments": [{"file_name": "informe.md", "content_base64": "..."}, {"url": "https://..."}]` adjunta hasta 5 archivos al comentario (14 MB sumados), con las reglas de los adjuntos de abajo; un adjunto con problemas rechaza el comentario entero. 201 con el `id` y los `attachments`. En el `GET`, cada comentario trae sus `attachments` (`id`, `name`, `type`, `size_bytes`, `download_url`).
- `GET /issues/{código o id}/evidences` (tracker:read): los adjuntos (evidencias) de la issue, del más antiguo al más nuevo, cada uno con `id`, `name`, `type`, `size_bytes`, `description`, `comment_id` (cuando vino en un comentario) y `download_url`. `GET /evidences/{id}` (tracker:read) descarga el archivo, con el mismo token (acepta `Range`). Por el MCP, cuyo token no vale aquí, `get_attachment` lee el contenido: texto (md, csv, txt, log, json) e imagen (png, jpg, webp, gif hasta 5 MB); vale también para los adjuntos de los chats.
- `POST /issues/{código o id}/evidences` (tracker:write): adjunta un archivo, de tres formas: `multipart/form-data` con el archivo en el campo `file` (y `description` y `comment_id` opcionales, en texto); JSON `{"file_name": "evaluacion.md", "content_base64": "...", "description": "..."}` (acepta el prefijo `data:...;base64,`); o JSON `{"url": "https://ejemplo.com/captura.png"}`, que tracker descarga (solo https y dirección pública, hasta 3 redirecciones y 20 s; la URL no se guarda; `file_name` opcional cambia el nombre). Formatos por la extensión del nombre: png, jpg, jpeg, webp, gif, mp4, mov, pdf, json, txt, log, md, csv, zip; hasta 15 MB por archivo y 60 archivos por hora por persona. `comment_id` pone el archivo debajo de un comentario tuyo de la issue. PO/PM, QA, dev o admin (403 para los demás). 201 con `attachment` (como en el `GET`). No hay URL prefirmada: el bucket es privado y todo archivo pasa por tracker, que revisa el formato y el tamaño. `DELETE /issues/{código o id}/evidences/{id}` lo quita (quien lo envió o un admin del proyecto; el archivo de una versión de aprobación se queda). En el MCP: `attach_file`, `remove_attachment` y `attachments` en `add_comment`; los adjuntos aparecen en `get_issue` (`evidences`).
- `PATCH /issues/{código o id}/comments/{id}` (tracker:write): corrige el texto, `{"text": "..."}`. Solo quien lo escribió (403 para los demás); el texto anterior queda guardado para el admin. `DELETE` en la misma dirección lo elimina: el comentario queda en su lugar, sin el texto (quien lo escribió o un admin de la empresa).
- `GET /issues/{código o id}/history` (tracker:read): el historial completo, el más reciente primero (`limit` hasta 200, `offset`), con `total` y cada evento con `at`, `by`, `action`, `field`, `text` y, cuando los hay, `from` y `to` ya con nombre.
- `POST /issues/{código o id}/move` (tracker:write): lleva la issue a otro proyecto de la misma organización, `{"project": "CHG", "confirm": true}`. Sin `confirm`, solo devuelve la vista previa (`moved: false`, avisos de lo que no tiene equivalente en el destino, subtareas que van juntas); con `confirm: true`, la mueve y devuelve el código nuevo (`code`) y el anterior (`previous_code`), que sigue encontrando la issue. 409 si la issue cambió en el medio: léela de nuevo y repite. Exige editar la issue y crear issues en el destino.
- `POST /issues/{código o id}/links` (tracker:write): la vincula a otra issue, `{"target": "MOB-0007", "type": "blocks"}` (`blocks`, `relates_to`, el de por defecto, o `duplicates`). Exige editar la issue. 201 con los vínculos de la issue (`links`, cada uno con `id`). `DELETE /issues/{ref}/links/{id}` quita un vínculo.
- `GET /issues/{código o id}/proofs` (tracker:read): aprobación de piezas (Design). Las versiones enviadas, de la más nueva a la más antigua, con `status` (`pending`, `approved`, `changes_requested`, `superseded`), nota, decisión con comentario y el archivo (`download_url`). `pending` es el id de la versión que espera decisión.
- `POST /issues/{código o id}/proofs` (tracker:write): envía la versión siguiente, en multipart/form-data con `file` y `note` opcional (mismos formatos y tamaño que las evidencias). La que esperaba decisión queda reemplazada. PO/PM, QA, dev o admin. 201 con `version` y las versiones.
- `POST /issues/{código o id}/proofs/{id}/decision` (tracker:write): `{"decision": "approve"}` o `{"decision": "changes", "feedback": "qué cambiar"}`. PO/PM, QA, admin o el cliente del proyecto (stakeholder). Con el flujo de Design, enviar lleva la issue a En aprobación, aprobar a Aprobado y pedir ajustes a Ajustes. 422 cuando la versión ya se decidió o fue reemplazada.
- `GET /issues/{código o id}/watchers` (tracker:read): quién sigue la issue (recibe los avisos de estado y de comentario nuevo), con `watching` para la persona del token. `POST` agrega y `DELETE` quita (tracker:write): sin nada, la propia persona; `{"person": "ana@empresa.com"}` en el POST o `?person=...` en el DELETE para otra persona del proyecto, lo que exige editar la issue. La respuesta trae `person` (a quién se agregó o quitó), `watching` y `watchers`. `GET /issues/{ref}` también trae `watchers`.
- `POST /issues/{código o id}/status` (tracker:write): cambia el estado de la issue, `{"status": "<clave>"}`. Vale el flujo del proyecto y el perfil de la persona en él (422 cuando la transición no está permitida). Para `closed` (Cerrado, plantilla QA), manda también `close_reason`: `duplicated`, `cancelled`, `not_reproduced` o `wont_fix`; con la issue ya en `closed`, el mismo pedido cambia solo el motivo.
- `POST /issues/{código o id}/block` (tracker:write): marca la issue como bloqueada, `{"reason": "lo que lo impide"}` (opcional; de nuevo cambia el motivo). La marca vale en cualquier estado y sale sola cuando la issue se completa. `DELETE` en la misma dirección la quita. Todo perfil del proyecto menos stakeholder; una issue completada no queda bloqueada (422). Devuelve `blocked` (`since`, `reason`) o `null`. En el MCP: `block_issue`.
- `DELETE /issues/{código o id}` (tracker:write): elimina la issue (admin o PO/PM del proyecto; 403 para los demás perfiles). Desaparece de listas, búsqueda y API y se puede restaurar durante 30 días (en la pantalla, en Issues → Eliminadas, o con `POST /issues/{ref}/restore`). Una issue con issues dentro (épica, subtareas) no se elimina (422).
- `POST /issues/{código o id}/restore` (tracker:write): restaura una issue eliminada en los últimos 30 días, con comentarios, evidencias e historial. Admin o PO/PM del proyecto. `GET /issues/deleted` (tracker:read, `project` opcional) lista las que la persona puede restaurar (las 200 más recientes), con `restore_until`. Después de 30 días la restauración devuelve 422.
- `POST /issues/{código o id}/duplicate` (tracker:write): crea una copia en el mismo proyecto, como el Duplicar de la pantalla (contenido, clasificación, padre, puntos, campos y etiquetas; sin comentarios, evidencias, vínculos, personas, sprint, release ni registro técnico). 201 con el `code` de la copia. Exige crear issues en el proyecto.
- `POST /issues` (tracker:write): crea una issue. 201 cuando la crea; 200 cuando ya existía una con el mismo `external_id` en el proyecto. `assignee` y `qa_owner` (`me`, id, correo o nombre de quien participa en el proyecto) ya asignan al crear: PO/PM y admins asignan a cualquiera, dev la toma como responsable y QA como QA responsable (403 fuera de eso; 422 cuando la persona no es del proyecto, sin crear la issue). Dev crea solo tipos de nivel subtarea, con el padre. Con `?similar=true`, la respuesta de la issue creada trae también `similar`: las parecidas que aqua confirmó (`code`, `title`, `status_label`, `open`, `relation`, `reason`, `likely_duplicate`, `url`), para vincular.
- `PATCH /issues/{código o id}` (tracker:write): edita la issue. Solo cambian los campos enviados; `null` quita el valor. Acepta los campos de texto, `title`, `type`, `severity`, `priority`, `module`, `custom_fields` (solo cambian las claves enviadas; una clave que no existe en el proyecto se rechaza), `assignee` y `qa_owner` (`me`, id, correo o nombre de quien participa en el proyecto), el registro técnico (`root_cause`, `fix_notes`, `fixed_version`, `fixed_build`), `parent`, `story_points`, `sprint` (nombre o id de un sprint abierto; `null` vuelve al backlog), `release` (la principal: versión o id; de otro proyecto de la empresa, `"PLT backend-1a2b3c"`, cuando la corrección sale solo allá), `add_releases`/`remove_releases` (otras entregas de la misma corrección, incluso de otro proyecto: id, versión en el proyecto de la issue, `"GOP 1.4.8-ota002"` o `{"project": "GOP", "version": "1.4.8-ota002"}`, en lista o en un texto separado por comas; exige vincular releases en los dos proyectos) y `add_labels`/`remove_labels`. Cada grupo exige el perfil adecuado en el proyecto (403 cuando no puede). Los grupos se guardan en secuencia: si uno falla, la respuesta trae `applied` con los que ya se guardaron. Para no sobrescribir la edición de otra persona, manda `edit_version` (el valor leído en `GET /issues/{ref}`): si alguien cambió la issue después (campos, personas, registro técnico, padre, puntos, sprint, release, etiquetas, estimación o fechas; estado, comentarios y horas no cuentan), no se guarda nada y la respuesta es 409 (`conflict`); léela de nuevo y vuelve a aplicar. La respuesta de éxito trae la `edit_version` nueva.
- `GET /issues/{código o id}/time` (tracker:read): estimación, inicio, plazo, repetición (`recurrence` y `next_occurrence`), total registrado, restante y los registros de horas.
- `POST /issues/{código o id}/time` (tracker:write): registra horas trabajadas, `{"duration": "1h30", "work_date": "AAAA-MM-DD", "comment": "..."}` (o `minutes` en lugar de `duration`; sin `work_date`, hoy). Perfiles PO/PM, QA, dev y admin.
- `PATCH /issues/{código o id}/plan` (tracker:write): `{"estimate": "8h", "start_date": "AAAA-MM-DD", "due_date": "AAAA-MM-DD", "recurrence": "monthly"}`. Un campo omitido queda como está; `null` quita el valor. `recurrence` (`weekly`, `monthly`, `quarterly`, `yearly`) hace que la issue se repita: al completarse, nace la próxima ocurrencia con inicio y plazo adelantados, los mismos responsables y etiquetas; `time.next_occurrence` apunta a ella. También vale en `POST /issues` y en cada elemento de `POST /issues/import`; `null`, `""` o `"none"` quita la repetición.
- `GET /issues/{código o id}/code` (tracker:read): PRs (GitHub), MRs (GitLab) y commits vinculados a la issue, con `id`, repositorio, estado (`open`, `draft`, `merged`, `closed`), rama, autor y enlace.
- `POST /issues/{código o id}/code` (tracker:write): vincula un PR o commit por el enlace, `{"url": "https://github.com/dueno/repo/pull/12", "title": "..."}` (MR o commit de GitLab también, incluso self-hosted). No cambia el estado. 409 cuando ya está vinculado (commit por el sha corto o completo). En un repositorio conectado, el webhook después actualiza el título y el estado. Perfiles PO/PM, QA, dev y admin.
- `DELETE /issues/{código o id}/code?link_id=<id>` (o `?url=<enlace>`, o lo mismo en el cuerpo JSON) (tracker:write): desvincula. 404 cuando no estaba vinculado.
- `GET /time` (tracker:read): informe de horas. Query: `from` y `to` (AAAA-MM-DD; por defecto, el mes en curso), `project` (prefijo, slug o id) y `person` (`me` o id); un filtro que no coincide es error. Totales por persona, por proyecto y por issue, y los registros.
- `POST /issues/bulk` (tracker:write): la misma acción en hasta 100 issues, `{"issues": ["MOB-0012", "MOB-0013"], "action": "status", "value": "done"}`. `action`: `status`, `assignee` (solo PO/PM o admin; `me`, id, correo o nombre; `none` quita), `priority`, `severity`, `label`, `unlabel`, `release` (versión o id; `none` quita), `watch`, `unwatch` y `delete` (las tres últimas sin `value`). Cada issue sigue la regla de la acción individual; la respuesta trae `requested` (issues distintas pedidas), `updated` y `failed` (`code` y `error` de cada una que no cambió). Sin `value` en una acción que lo pide: 422.
- `POST /issues/batch` (tracker:write): lote por issue, hasta 50, para cerrar una entrega en una llamada: `{"items": [{"issue": "MOB-0045", "fixed_version": "backend 4e8998ac", "fix_notes": "...", "code_links": ["https://github.com/org/repo/pull/251"], "status": "ready_for_qa"}]}`. Cada elemento acepta los campos del `PATCH /issues/{ref}` (incluso `edit_version`), `code_links` (enlaces, o `{url, title}`; un enlace ya vinculado no es error) y `status`, aplicados en ese orden con las reglas de las llamadas individuales. Un elemento que falla no detiene a los demás; la respuesta trae `requested`, `succeeded`, `failed` y `results` (por elemento: `ok`, `code`, `updated`, `linked`, `status` y, si falló, `failed_at` con `issue` (no encontrada, o cambió después de la `edit_version`), `fields`, `code_links` o `status`, y `error`; los pasos anteriores quedan guardados). Sin otros campos, la `edit_version` protege los enlaces y el estado.
- `POST /issues/import` (tracker:write): hasta 100 issues por pedido, `{"project": "MOB", "issues": [...]}` (cada elemento puede traer su propio `project`). Elemento por elemento: uno rechazado no deshace los demás. La respuesta trae `created`, `existing`, `failed` y `results` (`index`, `ok`, `code`, `url` o `error`).

Campos de una issue: `project` (prefijo, slug o id), `title` (obligatorio), `type` (clave del tipo), `severity` (S1 a S4, por defecto S3), `priority` (urgent, high, medium, low; solo PO/PM y admin, los demás quedan con medium), `module` (nombre), `description`, `environment`, `app_version`, `build`, `device`, `operating_system`, `preconditions`, `steps_to_reproduce`, `actual_result`, `expected_result`, `impact`, `suggested_solution`, `parent` (código o id de la issue padre: la épica de una issue común, o la issue de una subtarea; el nivel viene del tipo), `story_points` (número de 0 a 1000), `start_date` y `due_date` (inicio y plazo, AAAA-MM-DD; el inicio no viene después del plazo), `custom_fields` (objeto por la clave del campo), `labels` (hasta 10 nombres; la que no existe se crea) y `external_id` (id en el origen, hasta 200 caracteres).

Plantillas por tipo: `description`, `preconditions`, `steps_to_reproduce`, `actual_result`, `expected_result`, `impact` y `suggested_solution` omitidos vienen completados con la plantilla del tipo (el `template` en `GET /projects`). Manda `""` para dejar uno de esos campos vacío.

Usa `external_id` en el CI: con el nombre de la prueba que falló, por ejemplo, volver a correr el pipeline no duplica la issue.

### Ejemplos en el CI

Ejemplo (el token queda en un secreto del CI):

```sh
curl -sS -X POST https://tracker.aquarela.app/api/v1/issues/import \
  -H "Authorization: Bearer $TRACKER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"project": "MOB", "issues": [{"title": "El login falla en Android 15", "severity": "S2", "external_id": "e2e/login.spec.ts", "labels": ["CI"]}]}'
```

Resultados de una suite automatizada en un ciclo (el id sale de `POST /test-cycles` o de `GET /test-cycles`):

```sh
curl -sS -X POST https://tracker.aquarela.app/api/v1/test-cycles/$CYCLE_ID/results \
  -H "Authorization: Bearer $TRACKER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"results": [{"test_case": "TC-MOB-0001", "status": "pass"}, {"test_case": "TC-MOB-0002", "status": "fail", "comment": "Timeout en el login", "open_issue": true}]}'
```

### Chats

- Chats (`/chat`, un canal por proyecto, como en Slack): de quien trabaja en el proyecto (admin, PO/PM, QA y dev; el stakeholder no lo ve, 403). `GET /chat` (tracker:read) lista los canales de la persona del token (los proyectos activos): `unread` y `mentions` sumados y `channels`, cada uno con `project` (`id`, `slug`, `name`), `unread` (mensajes nuevos en la conversación principal), `mentions` (menciones y respuestas en sus hilos todavía no leídas), `last_message_at` y `url`. `GET /projects/{prefijo, slug o id}/chat` (tracker:read) trae `project`, `url`, `people` (a quién se puede mencionar con @Nombre: `id`, `name` y `me`, la persona del token; con `aqua` activada en la empresa, aqua viene al final, con `app` `aqua`; viene también con `?thread_id`), `unread` (las no leídas de la persona del token), `last_read_at`, `has_more` y `messages`, de la conversación principal en el orden en que se escribieron (sin cursor, las últimas 50; `?before=<id>` las anteriores, `?after=<id>` las que llegaron después, `?limit=` hasta 200, `?mark_read=1` marca como leída hasta la última devuelta). Con `?thread_id=<id>` (el primer mensaje de un hilo) trae `thread` (ese mensaje) y `replies` (las respuestas, hasta 500; `?after=<id>` solo las nuevas; `?mark_read=1` marca como leídas las menciones y respuestas del hilo). Cada mensaje: `id`, `thread_id` (el hilo en el que es respuesta, o `null`), `in_channel` y `thread_root` (`author`, `text`: la respuesta del hilo que también fue a la conversación principal y el comienzo del mensaje al que responde), `author`, `app` (el mensaje escrito por una app, con `key` y `name`; `null` en el de una persona), `author_id`, `author_avatar_url` (la foto, `null` sin foto), `text` (markdown; vacío en el eliminado y en el que es solo adjunto), `attachments` (los archivos adjuntos, en orden: `id`, `name`, `type`, `kind` (`image`, `video` o `file`), `size_bytes` y `download_url`; vacío en el eliminado), `at`, `edited_at`, `deleted` (`by`, `at`), `mine` (escrito por la persona del token), `can_edit` y `can_delete` (lo que puede hacer con el mensaje), `mentions`, `mentions_me`, `issues` (las tareas citadas por el código que la persona ve: `code`, `title`, `open`, `url`), `reactions` (`emoji`, `count`, `mine`, `people`), `thread` (en la conversación principal: `replies`, `last_reply_at` y `people`, quién respondió; `null` sin respuestas) y `url`. `POST /projects/{ref}/chat` (tracker:write) envía `{"text": "..."}` en la conversación principal o `{"text": "...", "thread_id": "<id>"}` en el hilo de ese mensaje (con el id de una respuesta, entra en el mismo hilo; `"also_in_channel": true` también pone la respuesta en la conversación principal, como el "También enviar al canal" de la pantalla; `reply_to_id` es el nombre anterior; hasta 10.000 caracteres; @Nombre menciona a alguien del canal, y quien está en el hilo recibe la respuesta, en la Bandeja de entrada, en la campana y en el dispositivo) y devuelve `message` (`id`, `thread_id`, `url` y `aqua_replying`: el mensaje llamó a @aqua y va a responder en el hilo), 201. Adjuntos (fotos, PDF, zip, planillas, logs, videos: los formatos de los adjuntos de tareas, png, jpg, jpeg, webp, gif, mp4, mov, pdf, json, txt, log, md, csv, zip): en JSON, `"attachments": [{"file_name": "captura.png", "content_base64": "..."}]` o `[{"url": "https://..."}]`, como en los comentarios; o `multipart/form-data` con `text`, `thread_id`, `also_in_channel` y un campo `files` por archivo. Hasta 5 por mensaje, 15 MB cada uno y 14 MB en total, y 60 archivos por hora por persona; con adjunto, `text` puede faltar. `GET /chat/attachments/{id}` (tracker:read) descarga el archivo, con el mismo token (acepta `Range`), para quien lee la conversación del mensaje. Eliminar el mensaje borra sus adjuntos. `POST /projects/{ref}/chat/{id}/reactions` (tracker:write) con `{"emoji": "👍"}` marca la reacción de la persona del token y `DELETE` con el mismo cuerpo la quita (cada persona, cada emoji una vez; hasta 20 emojis distintos por mensaje); devuelve `id`, `emoji` y `reacted`. `PATCH /projects/{ref}/chat/{id}` (tracker:write, solo quien lo escribió) lo corrige con `{"text": "..."}`; `DELETE /projects/{ref}/chat/{id}` (tracker:write, quien lo escribió o un admin) lo elimina (queda en su lugar como eliminado; el texto anterior va al historial de la empresa). `POST /projects/{ref}/chat/read` (tracker:read) con `{"message_id": "<id>"}` (o `{}`, hasta ahora) marca la conversación principal como leída y devuelve `unread`. `GET /projects/{ref}/chat/issues?q=` (tracker:read) trae `issues`, las tareas para citar en un mensaje, como la lista que se abre al escribir `#` en el campo de la pantalla (hasta 8; solo las que ve la persona del token): sin `q`, por contexto, primero las citadas por última vez en la conversación (`reason` `cited`), después las abiertas con la persona del token (`mine`) y las abiertas del proyecto movidas por última vez (`project`); con el comienzo de un código (`MOB-` o `MOB-12`, que encuentra MOB-0012), completa por el código, primero las del proyecto; con palabras, la búsqueda por texto y significado de `/search` (`reason` `null`). Cada una: `id`, `code`, `title`, `open`, `project` (el nombre), `reason` y `url`; el `code` escrito en el `text` se vuelve enlace y tarjeta. Proyecto archivado: solo lectura (422 al escribir). En el MCP: `list_chat_channels`, `list_chat_messages`, `send_chat_message`, `react_to_chat_message`, `edit_chat_message`, `delete_chat_message` y `suggest_chat_issues`.
- Mensajes directos (`/chat/direct/<id de la persona>`): la conversación privada entre la persona del token y otra persona del equipo de la misma empresa (quien trabaja en algún proyecto de ella, menos el stakeholder, y los admins y dueños). Solo las dos la leen; ni el admin la ve. `GET /chat` también trae `directs` (las conversaciones con mensajes, la más reciente primero: `id`, `person` (`id`, `name`), `unread`, `last_message_at`, `url`) y `people` (con quién puede abrir una: `id`, `name`, `url`); el `unread` de arriba suma los canales y las conversaciones directas. `GET /chat/direct/{persona}` (tracker:read; la persona por id o correo; abre la conversación la primera vez) trae `person`, `url`, `people` (vacío: no hay @menciones), `unread`, `last_read_at`, `has_more` y `messages`, con los mismos parámetros y campos de la conversación del proyecto, sin hilos. `POST /chat/direct/{persona}` (tracker:write) envía `{"text": "..."}`, con adjuntos como en el canal (la otra persona recibe el aviso en el dispositivo; `thread_id` da 422); `PATCH`/`DELETE /chat/direct/{persona}/{id}` corrige y elimina (solo quien lo escribió; fuera del historial de la empresa), `POST`/`DELETE /chat/direct/{persona}/{id}/reactions` reacciona, `POST /chat/direct/{persona}/read` marca como leída y `GET /chat/direct/{persona}/issues?q=` sugiere las tareas para citar (sin proyecto del canal: las citadas en la conversación y las abiertas con la persona del token). Una persona fuera del equipo: 404; quien salió del equipo después deja la conversación solo para lectura (422 al escribir). En el MCP, las mismas tools de la conversación con `person` en lugar de `project`.
- Apps de los Chats: quien escribe en el canal sin ser una persona (el mensaje viene con `app` y `author_id` `null`; nadie lo corrige, el admin lo elimina). El texto que escriben las apps y su catálogo (`name`, `summary`, `description` y la descripción de los permisos) vienen en el idioma de quien lee: el elegido en Tu cuenta o, en automático, el de `Accept-Language`; lo que escriben las personas (nombres, asuntos, la respuesta de aqua) queda como se escribió. Cada app tiene un manifiesto: nombre, lo que pide (`permissions`: obligatorias, que entran siempre, y opcionales, que el admin elige al instalar), los eventos que escucha y lo que elige cada canal. Los permisos: `chat:write` (escribir en la conversación), `issues:read` (ver las tareas del proyecto), `comments:read` (ver los comentarios, para el aviso de comentario nuevo), `meetings:create` (crear reuniones en Google Meet) `chat:direct` (funcionar en las conversaciones directas) y `ai:use` (responder con aqua, gastando los créditos de IA de la empresa). Las oficiales: `task_updates` (Avisos de tareas: publica en el canal cuando una tarea del proyecto se crea, cambia de estado, cambia de responsable o recibe un comentario (este solo con `comments:read`, opcional), con su tarjeta; cada canal elige los eventos), `google_meet` y `aqua` (ver abajo) y `automations` (Automatizaciones: viene con tracker y escribe cuando una regla de automatización tiene la acción "Enviar mensaje en el canal"). La empresa instala (solo admins y dueños) y el PO/PM del proyecto o un admin la activa en cada canal, o el admin la activa en todos los canales de una vez, incluso en los que se creen después. En la conversación directa, solo las apps con `chat:direct` concedido, cuando una de las dos personas la activa; cualquiera la desactiva, y la app cuenta en la conversación quién la activó o desactivó. `GET /apps` (tracker:read) trae `apps`, el catálogo de la empresa: `key`, `name`, `summary`, `description`, `official`, `built_in`, `company_wide` (viene activada en toda la empresa: sin instalar ni activar por canal; `installed` es false cuando un admin la desactivó), `permissions` (`key`, `description`, `required`: obligatoria, y `granted`: la empresa la concedió), `events`, `installed`, `installed_at`, `channels` (en cuántos canales está activada) y `all_channels` (activada en todos los canales, incluso en los nuevos). `POST /apps/{key}` (tracker:write) la instala, con `{"permissions": ["chat:write", "meetings:create", "chat:direct"]}` (las obligatorias entran siempre; sin cuerpo, solo ellas); `PATCH /apps/{key}` con `{"permissions": [...]}` cambia lo concedido (quitar `chat:direct` desactiva la app en todas las conversaciones directas); `{"all_channels": true}`, en el `POST` o en el `PATCH`, activa la app en todos los canales, incluso en los nuevos (entonces el canal no la desactiva, solo elige los avisos), y `false` vuelve a los canales que la activaron uno por uno (no vale para las personales); `DELETE /apps/{key}` la desinstala (la desactiva en todos los canales y conversaciones directas; los mensajes quedan). Devuelven `key`, `installed` y `permissions` (las concedidas); un permiso que la app no pide da 422. `GET /projects/{ref}/chat/apps` (tracker:read, quien participa en la conversación) trae `project`, `can_manage` (puede activar y desactivar), `can_install` y `apps` (`key`, `name`, `summary`, `built_in`, `company_wide`, `installed`, `enabled`, `all_channels` y, en `task_updates`, `events` y `allowed_events`, los que los permisos dejan elegir). `PATCH /projects/{ref}/chat/apps/{key}` (tracker:write, PO/PM o admin) con `{"enabled": true, "events": ["issue.created", "issue.status_changed", "issue.assigned", "comment.created"]}` la activa y elige los avisos (sin `events`, queda lo que había o lo de por defecto: tarea creada y estado cambiado) y `{"enabled": false}` la desactiva (con `all_channels`, da 422); una app no instalada da 422. En la conversación directa: `GET /chat/direct/{persona}/apps` (tracker:read; la persona por id o correo) trae `person`, `active`, `can_install` y `apps` (`key`, `name`, `summary`, `installed`, `allowed`: liberada en las conversaciones directas, `enabled` y `enabled_by`: quién la activó); `PATCH /chat/direct/{persona}/apps/{key}` (tracker:write, cualquiera de las dos personas) con `{"enabled": true}` la activa y `{"enabled": false}` la desactiva. En el MCP: `list_chat_apps` (con `project`, las del canal; con `person`, las de la conversación directa), `install_chat_app` (con `permissions` y `all_channels`) y `set_chat_app` (con `project` o `person`).
- Google Meet (`google_meet`, app oficial): crea la reunión en el canal, en la cuenta de Google de quien la llama, y publica la tarjeta con el enlace para entrar. Cada persona conecta su propia cuenta de Google una vez, por el navegador (la pantalla de Google no se abre por la API). En el canal, la app viene con `account` (`configured`, `connected` y `email`, la cuenta de la persona del token). `GET /apps/google_meet/account` (tracker:read) trae `configured`, `connected`, `email` y `connect_url` (ábrelo en el navegador, con sesión iniciada en tracker); `DELETE /apps/google_meet/account` (tracker:write) desconecta y quita el acceso en Google. `POST /projects/{ref}/chat/meet` (tracker:write, quien participa en la conversación) con `{"title": "Planificación del sprint"}` (opcional) y `"thread_id"` (opcional: la crea dentro del hilo de ese mensaje) crea la sala y devuelve `project`, `message` (`id`, `thread_id`, `url`) y `meeting` (`url` para entrar, `code`, `title`, `starts_at` y `ends_at`), 201. Para agendar, manda también `"starts_at"` (ISO 8601 con la zona horaria, ej.: `2026-10-22T14:00:00-03:00`), `"duration_minutes"` (15 a 480, por defecto 30), `"time_zone"` (IANA, por defecto `America/Sao_Paulo`) y `"guests"` (hasta 50 correos o ids de personas de la conversación): tracker crea el evento en el Google Calendar de quien llama, con la sala de Meet, Google envía la invitación a los invitados y `meeting` trae también `guests` (cuántos fueron invitados). Agendar pide que la cuenta de Google haya permitido el acceso al calendario; quien conectó antes de eso recibe 422 con el enlace para conectar de nuevo. `GET /apps/google_meet/meetings` (tracker:read) trae `meetings`, las reuniones con Meet de los próximos 14 días del calendario de la persona del token: `title`, `url`, `code`, `starts_at`, `ends_at` y `calendar_url`. En la conversación directa, `POST /chat/direct/{persona}/meet` con `{"title": "..."}` (sin hilo), con la app activada en la conversación. App desactivada en el canal o en la conversación, o sin `chat:direct`: 403; sin la cuenta conectada: 422, con el enlace para conectar. El mensaje trae `card`: `type` `meeting`, `provider` `google_meet`, `url`, `code`, `title`, `started_by` (el nombre de quien llamó) y, en la agendada, `starts_at`, `ends_at` y `event_id` (el evento en el Google Calendar de quien la agendó), y `meeting_responses` (en la agendada: `yes`, `maybe`, `no`, cuántos respondieron desde tracker, y `mine`, la respuesta de la persona del token; `null` en las demás). En la pantalla, el botón de la cámara al lado del campo (Empezar ahora, Agendar reunión… y Próximas reuniones), `/meet asunto` o `/meet lista`. En el MCP: `start_meeting` (con `project` o `person`; con `starts_at`, agenda) y `list_meetings`.
- Google Calendar (`google_calendar`, app oficial): personal. La empresa la instala y cada persona la usa con su propia cuenta de Google (la misma conexión de Google Meet, con acceso a la agenda), sin activarla por canal: en el catálogo, `channels` viene `null`. Permisos: `calendar:read` (obligatorio: leer la agenda de quien la usa) y `calendar:respond` (opcional: responder a las invitaciones desde tracker). `GET /apps/google_calendar/agenda` (tracker:read), con `?date=AAAA-MM-DD` o `?days=1` (desde hoy; por defecto hoy) y `?time_zone=` (por defecto `America/Sao_Paulo`), devuelve `date`, `time_zone`, `can_respond`, `reminder_minutes` y `events`: `id`, `title`, `starts_at`, `ends_at`, `all_day`, `meeting_url` (la sala de Meet), `calendar_url` y `response` (`yes`, `maybe`, `no`; `pending` sin respuesta; `null` cuando la persona la organizó o no está invitada). `GET /apps/google_calendar/settings` (tracker:read) devuelve `installed`, `can_respond`, `account` (`configured`, `connected`, `email` y `calendar`), `connect_url` y `reminder_minutes`; `PATCH` (tracker:write) con `{"reminder_minutes": 15}` cambia el aviso antes de cada reunión (5, 10, 15 o 30; 0 lo desactiva). El aviso llega a la campana y al celular (el tipo de notificación `meeting`, "Reuniones", en las preferencias; sin correo) y abre la sala de Meet. Responder a la invitación (con `calendar:respond`): `POST /projects/{ref}/chat/{id}/response` o `POST /chat/direct/{pessoa}/{id}/response` con `{"response": "yes"}` (`yes`, `maybe`, `no`) en la tarjeta de una reunión agendada desde tracker devuelve `responses`; `POST /apps/google_calendar/events/{id}/response` responde a un evento de la agenda. La respuesta va al Google Calendar de quien responde y Google avisa a quien la organizó; quien la organizó no responde (422). Sin la app: 403; sin el permiso de responder: 403; sin la cuenta conectada con acceso a la agenda: 422, con el enlace para conectar. En la pantalla, `/agenda` (o `/agenda amanhã`) y el botón de la agenda al lado del campo muestran, solo a quien lo pidió, la agenda del día, con Entrar, Sí/Tal vez/No y el aviso; la tarjeta de la reunión agendada gana Sí, Tal vez y No y el conteo. En el MCP: `get_agenda`, `respond_to_meeting` (con `message` y `project` o `person`, o con `event_id`) y `set_meeting_reminder`.
- aqua (`aqua`, app oficial, `company_wide`): viene activada en todos los canales de la empresa, sin instalar ni activar canal por canal (`PATCH /projects/{ref}/chat/apps/aqua` da 422). Un admin la desactiva en la empresa con `DELETE /apps/aqua` y la vuelve a activar con `POST /apps/aqua`; también necesita aqua (la IA) activada en la empresa. Activada, un mensaje con @aqua (en la conversación principal o en un hilo, por la pantalla, por la API o por el MCP) recibe la respuesta de aqua en el hilo de ese mensaje. aqua escribe en el momento "Consultando o projeto…" (un mensaje de la app, con `app` `aqua`) y cambia el texto por la respuesta cuando termina, sin marcarlo como editado: quien lee por la API ve el cambio en la próxima lectura del hilo (`GET /projects/{ref}/chat?thread_id=<id>`, con el id del mensaje que la llamó, o su `thread_id`). Consulta como quien la llamó, con el perfil de esa persona en el proyecto; como la respuesta es para todo el canal, solo entran el proyecto del canal (tareas, sprints, releases, pruebas, horas, personas y la conversación) y los documentos de la empresa: los demás proyectos, la Bandeja de entrada y los mensajes directos quedan fuera. Cuando piden crear o cambiar algo (crear tarea, editar, cambiar el estado, comentar, bloquear, seguir, planificar, registrar horas, vincular), aqua propone y nada se guarda en el momento: su respuesta viene con `aqua_actions` (`asked_by`, quien la llamó y el único que decide; `mine`, si es la persona del token; y `actions`, cada una con `id`, `tool`, `title`, `details`, `state` — `pending`, `running`, `done`, `cancelled` o `failed` —, `replaced`, `result` y `error`; `null` en los demás mensajes), y la pantalla muestra una tarjeta con Confirmar y Cancelar. `POST /projects/{ref}/chat/{id}/aqua-actions/{action}` (tracker:write, solo quien llamó a aqua; 403 para los demás) con `{"decision": "confirm"}` hace el cambio en nombre de esa persona, con su perfil, y devuelve `{"action": ...}` (`"cancel"` la descarta); la misma acción nunca se ejecuta dos veces (422). Las acciones también se quedan en el proyecto del canal: la tarea nueva va a él y solo se cambian sus tareas. En un hilo, aqua relee la conversación (lo que escribió, las respuestas y lo decidido en cada tarjeta): llamada de nuevo con las decisiones, lo junta todo y propone de nuevo, y sus propuestas que aún esperaban a la misma persona en ese hilo dejan de valer (`replaced` `true`, `state` `cancelled`). En el MCP: `decide_aqua_chat_action`. Cada respuesta cuenta como un uso de IA de quien la llamó, con los créditos de la empresa; sin créditos, la respuesta dice cuándo se renuevan. Quien la llamó recibe el aviso de la respuesta (la preferencia Comentarios). Los adjuntos del mensaje que la llamó van junto con la pregunta: aqua ve las capturas y los PDF y lee los textos; de los otros mensajes solo ve el nombre. La app pide `chat:write`, `issues:read` y `ai:use`.

### aqua y Bandeja de entrada

- `GET /aqua` (tracker:read): si aqua (el asistente de IA) está activada en la empresa, `{"available": true, "reason": null, "can_review": true}` (`reason`: `disabled` o `no_key`; `can_review`: la persona edita issues en algún proyecto y ve la pantalla aqua, `GET /aqua/recommendations`). `POST /aqua` (tracker:read): la conversación de ayuda de la app, `{"messages": [{"role": "user", "content": "¿Cómo creo una issue?"}], "path": "/issues", "app": "ios"}` (hasta 20 mensajes, empezando y terminando con `user` y alternando; `app`: `ios` o `mac`, opcional). aqua responde dudas de uso y también sobre los datos: consulta, solo para leer y como la persona del token, lo que esa persona ve en la empresa (sus tareas y las que sigue, proyectos, sprints, releases, pruebas, personas, horas, bandeja de entrada, Chats, documentos, ideas y estado), con las mismas consultas de lectura del MCP; no cambia nada. También abre los adjuntos de las tareas y de los comentarios cuando la pregunta lo pide: ve las capturas (png, jpg y webp hasta 3,7 MB) y los PDF (hasta 10 MB) y lee los textos (md, csv, txt, log y json), hasta 4 imágenes o PDF por pregunta; video no. aqua también propone cambios cuando la persona lo pide (crear una tarea, editar o asignar, comentar, cambiar el estado, bloquear, seguir, planificar, registrar horas, vincular tareas), pero la pregunta no guarda nada: cada propuesta vuelve en `actions` para que la persona la confirme. Los enlaces de la respuesta son direcciones de la app (`/issues/MOB-0012?org=...`). Cuando la pregunta trata de una tarea o de un proyecto, manda en `path` la pantalla abierta (`/issues/MOB-0012`, `/projects/mobilidade`): aqua entiende "esta tarea". Una pregunta que consulta los datos tarda un poco más y cuenta como un uso de IA. aqua responde en el idioma de la persona del token (el `locale` de `GET /me`; en Automático, el `Accept-Language` del pedido), y en el mismo idioma vienen `title`, `details` y `result` de las acciones y los textos de `status`. Toda conversación queda guardada: manda `conversation_id` (devuelto en la primera respuesta) para seguir la misma; sin él, una conversación nueva (o la reciente que tiene exactamente los mismos mensajes). Devuelve `{"answer": "...", "conversation_id": "...", "actions": [...]}`, cada acción con `id`, `tool` (la tool del MCP que ejecuta), `input`, `title` (la frase de la tarjeta, escrita por tracker), `details`, `state` (`pending`, `running`, `done`, `cancelled` o `failed`), `result` (`text` y `url`, una vez hecha) y `error`. Con `"stream": true`, la respuesta llega de a poco en NDJSON (`application/x-ndjson`, un JSON por línea): `{"type": "status", "text": "Consultando las tareas…"}` mientras consulta (el texto recibido antes de un `status` ya no vale), `{"type": "text", "text": "..."}` con cada parte de la respuesta, `{"type": "ping"}` para mantener la conexión y, al final, `{"type": "done", ...}` con el mismo JSON que sin stream (la respuesta que vale) o `{"type": "error", "message": "..."}`; el estado HTTP es 200 incluso con error. `POST /aqua/conversations/{id}/actions/{action}` (tracker:write) con `{"decision": "confirm"}` hace el cambio con el perfil de la persona del token (o `"cancel"` lo descarta) y devuelve `{"action": ...}`; la misma acción no se ejecuta dos veces (422 cuando ya se decidió). `GET /aqua/conversations` (tracker:read, `?limit=` hasta 50) lista las conversaciones de la persona (`id`, `title`, `messages`, `created_at`, `updated_at`; quedan las 50 más recientes), `GET /aqua/conversations/{id}` trae los mensajes (`role`, `content`, `at`, `actions`) y `DELETE /aqua/conversations/{id}` (tracker:write) la borra. En el MCP: `list_aqua_conversations` y `get_aqua_conversation`. 422 cuando la IA se niega o está desactivada, sin clave o en el límite.
- `GET /aqua/credits` (tracker:read): los créditos de IA de la empresa. Sin clave propia, aqua usa la clave de tracker y gasta créditos de un paquete por ciclo de 30 días (el del plan de la empresa), que se renueva solo; con la clave propia de la empresa, no gasta. Devuelve `available`, `reason`, `key_source` (`server`: créditos; `organization`: clave propia; `null` sin IA), `credits` (`null` con la clave propia): `state` (`ok`, `low` con 20% o menos, `empty` cuando se acabaron y aqua se pausa hasta renovar, `expired`), `remaining`, `used`, `total` (`per_cycle` más `granted`, los créditos que el equipo de la plataforma agregó en el ciclo), `cycle_days`, `cycle_start`, `cycle_end`, `auto_renew` y `renews_at`; y `usage` (solo admins y dueños, `null` para los demás): `since` (el inicio del ciclo; con la clave propia, 30 días atrás) y `by_action`, de las acciones más usadas a las menos (`action`, `label`, `calls`, `failures`, `credits`). Siempre en créditos, nunca en dinero. Cuando una llamada de IA vuelve 422 por falta de créditos, el mensaje dice cuándo se renuevan. En el MCP: `get_ai_credits`. La clave propia de la empresa (solo de Claude) se pone, cambia y quita solo en la pantalla, en Empresa → Integraciones → Asistente de IA (admins y dueños): ni la API ni el MCP leen o guardan la clave.
- Bandeja de entrada (la de la pantalla /inbox): `GET /inbox` (tracker:read, `?view=unread` solo las no leídas o `?view=hidden` las ocultas; sin él, las no ocultas; `?before=<id>` pagina, `?limit=` hasta 100) trae `unread` e `items`, los comentarios que llegaron a la persona del token, los más recientes primero: `kind` (`mention`: alguien escribió @su nombre; `reply`: respuesta en una conversación en la que escribió), `own_thread` (la conversación empezó con un comentario suyo), `id` (del comentario), `text`, `author`, `excerpt`, `issue` (`id`, `code`, `title`, `project`), `url`, `at`, `read` y `hidden`. Escribir en la tarea después del comentario, abrir la tarea o leer su notificación también cuentan como leída. Las menciones y las respuestas en los hilos de los Chats vienen aparte, en `chat_items` (con `chat_unread`): `id` (de la notificación), `kind` (`mention` o `reply`, respuesta en un hilo en el que está), `own_thread` (el hilo empezó con un mensaje suyo), `thread` (vino de un hilo), `text`, `author`, `excerpt` (el fragmento del mensaje), `project`, `url` (directo al mensaje), `at`, `read` y `hidden`; abrir el hilo o leer el canal cuenta como leída. `POST /inbox` (tracker:read) cambia el estado (de los ids de `items` y de `chat_items`): `{"action": "read", "ids": [...]}` o `{"action": "read", "all": true}`, `{"action": "hide", "ids": [...]}` o `{"action": "hide", "older_than_days": 30}` (ocultar cuenta como leída) y `{"action": "unhide", "ids": [...]}`; devuelve `updated`, `unread` y `chat_unread`. Resumen de aqua del día y del mes (el de arriba de la bandeja): `GET /aqua/summary` (tracker:read, `?period=day`, el de por defecto, o `month`, en la zona horaria de Brasilia) devuelve `available`, `reason`, `period` y `summary` (`null` antes de pedirlo): `headline`, `highlights` (lo que pasó en las tareas de la persona y en las que sigue), `attention` (lo que pide su atención), `issues` (las citadas, con `code`, `id` y `url`), `generated_at` y `outdated` (llegaron novedades después). `POST /aqua/summary` (tracker:read) con `{"period": "month"}` pide uno nuevo, escrito en el idioma de la persona del token (gasta créditos de IA; un período sin nada no gasta); 422 con la IA desactivada, sin clave o sin créditos. En el MCP: `list_inbox`, `update_inbox` y `get_aqua_summary`.
- aqua en las issues (la misma de la pantalla): `GET /issues/{ref}/aqua` (tracker:read) trae `available`, `can_review` (si la persona puede pedir la revisión), `summary` (como `aqua_summary` de `GET /issues/{ref}`), `review` (`summary`, `reviewed_at`, `outdated`), las `recommendations` abiertas (`id`, `kind`: `comment`, `status`, `field`, `custom_field`, `label`, `duplicate` o `move`; `title`, `before`, `after`, `reason` y `comment`, el texto del pedido de información) y `snooze_options`. `POST /issues/{ref}/aqua/summary` (tracker:read) resume la conversación (a partir de `summary_min_comments` comentarios) y devuelve `{"summary": ...}`. `POST /issues/{ref}/aqua/review` (tracker:write, quien edita la issue) la revisa y devuelve lo mismo que el `GET`. La revisión (esta, la en lote y la diaria) lee también los adjuntos de la issue y de los comentarios: hasta 3 capturas, 1 PDF (hasta 5 MB) y el texto de hasta 2 archivos de texto o log, los más nuevos primero; video no. El costo de leer entra en los créditos como el resto de la revisión. `POST /aqua/recommendations/{id}/apply` (tracker:write) aplica la sugerencia (`{"comment": "..."}` opcional cambia el texto del pedido de información); `POST /aqua/recommendations/{id}/dismiss` (tracker:write) con `{"for": "7"}` (`1`, `7`, `30` días o `forever`) la ignora. La pantalla aqua entera: `GET /aqua/recommendations` (tracker:read, `?project=MOB` opcional) trae `available`, `reason`, `can_review`, `daily_review` (`enabled`, `hour`, `limit`, `last_on`, `last_count`, `last_error`), `sentry_waiting` (errores de Sentry esperando decisión, en `GET /sentry/errors?waiting=true`), `total` e `issues`, las issues con sugerencias abiertas (de las paradas hace más tiempo a las recientes), cada una con `id`, `code`, `title`, `project`, `status` (`name`, `color`, `category`), `summary`, `reviewed_at`, `outdated` y `recommendations` (como arriba), y `snooze_options`. "Revisar issues abiertas" corre en el servidor: `POST /aqua/review-runs` (tracker:write, `{"project": "MOB"}` opcional) empieza la revisión de todas las issues abiertas que aqua todavía no vio o que cambiaron desde la revisión (hasta 500, una a la vez; una revisión a la vez por empresa, y pedirla de nuevo devuelve la que ya corre) y devuelve `{"run": ...}` (`null` sin nada pendiente); `GET /aqua/review-runs` (tracker:read) trae la que corre o la última, `GET /aqua/review-runs/{id}` el avance (`status`: `running`, `done`, `stopped` o `failed`; `total`, `reviewed`, `failed`, `suggestions`, `error`, `created_at`, `finished_at` e `issues`, cada una con `id`, `code`, `title`, `state` — `waiting`, `running`, `done` o `failed` —, `suggestions` y `error`) y `POST /aqua/review-runs/{id}/stop` (tracker:write) la detiene. `GET /aqua/review-queue` (tracker:read, `?project=` y `?limit=` hasta 25) sigue devolviendo la cola (`id`, `code`, `title`) para revisar una por una con `POST /issues/{ref}/aqua/review` y `{"batch": true}` (el modelo de la revisión en lote). En el MCP: `list_aqua_recommendations`, `start_aqua_review`, `get_aqua_review`, `stop_aqua_review`, `list_issues_to_review`, `review_issue_with_aqua`, `apply_aqua_recommendation` y `dismiss_aqua_recommendation`. `POST /aqua/draft` (tracker:read) con `{"text": "relato libre", "project": "MOB"}` (`project` opcional) devuelve los campos de `POST /issues` completados y `missing` (lo que el relato no dice), en el idioma del relato, sin crear nada. `POST /aqua/improve` (tracker:read) con `{"project": "MOB", "type": "bug", "title": "...", "description": "..."}` (y los demás textos del formulario) devuelve título y textos reescritos y `notes`, sin guardar. 422 cuando la IA está desactivada, sin clave o en el límite.

### Plan, moneda y facturación

- Plan y moneda (la pantalla Empresa → Plan; solo admins y dueños, para los demás 403). Durante la beta no se cobra nada. Cada empresa tiene una moneda (`BRL`, `USD`, `EUR`, `GBP`, `MXN`, `ARS`, `CLP` o `COP`), que nace del país desde donde se creó (sin él, BRL), y cada plan tiene un precio definido en cada moneda, mensual y anual, sin conversión. Los valores vienen en `amount_minor`, entero en la unidad mínima de la moneda (4990 es R$ 49,90; el peso chileno no tiene decimales), con `amount` y `formatted` (como lo muestra la pantalla) al lado.
- `GET /billing` (tracker:read): `currency`, `currency_locked` (con el cobro activado, solo el equipo de Aquarela cambia la moneda), `currencies`, `plan` (`key`, `name`, `description`, `ai_credits_per_cycle`, `null` para el de por defecto del servidor, `storage_gb`, el espacio de archivos en GB, `null` para el de por defecto, `features`, lo que incluye el plan, y `prices` en la moneda de la empresa), `plan_source` (`subscription`, o `default` cuando vale el plan por defecto), `price` (el actual), `subscription` (`status`: `trialing`, `active`, `past_due` o `canceled`; `interval`: `month` o `year`; `current_period_start`, `current_period_end`, `trial_ends_at` y `billing_provider`, `null` mientras no hay cobro), `plans` (los que la empresa puede tener, con el precio en su moneda), `billing_details` y `plan_request` (el pedido de plan pago esperando al equipo de Aquarela: `plan`, `interval`, `requested_by` y `created_at`; `null` sin pedido).
- `PATCH /billing` (tracker:write), en JSON: `{"currency": "USD"}` cambia la moneda; `{"billing_details": {...}}` cambia los datos de facturación, solo los campos enviados (`null` borra): `kind` (`company` o `individual`), `legal_name`, `email`, `country` (ISO de dos letras), `address_line1`, `address_line2`, `city`, `state`, `postal_code`, `tax_id_type` y `tax_id`. El documento sigue al país, con los tipos de Stripe: `br_cnpj` (empresa) y `br_cpf` (persona física), con los dígitos verificados, `us_ein`, `eu_vat`, `gb_vat`, `mx_rfc`, `ar_cuit`, `cl_tin` y `co_nit`; sin `tax_id_type`, vale el del país. Devuelve lo mismo que el GET; una regla rechazada vuelve 422.
- `POST /billing/plan` (tracker:write), en JSON: `{"plan": "pro", "interval": "year"}` (`plan` es la `key` o el `id` de uno de los `plans`; `interval`, `month` por defecto). Un plan gratuito en la moneda de la empresa cambia al instante (`outcome`: `changed`); un plan pago, o sin precio en la moneda, se vuelve un pedido al equipo de Aquarela (`requested`), que acuerda el pago y pone la empresa en el plan; el actual devuelve `unchanged`. Pedir otro plan reemplaza el pedido. Devuelve `outcome`, `plan`, `interval` y `billing` (lo mismo que el GET). `DELETE /billing/plan-request` (tracker:write) cancela el pedido (`{"cancelled": true}`; `false` sin pedido). En el MCP: `get_billing`, `update_billing`, `change_plan` y `cancel_plan_request`.
- Espacio de archivos: cada plan da un espacio para los adjuntos de la empresa (`storage_gb`; el de por defecto es 10 GB), que suma los adjuntos de las tareas (con los de los comentarios y las versiones de la aprobación), de la mesa de ayuda y de los Chats, también los directos. La foto de perfil, el logo y el ícono de proyecto no cuentan. Cuando se acaba el espacio, el envío de un adjunto nuevo (pantalla, API y MCP) devuelve 422 con el mensaje del límite; los archivos que el cliente manda a la mesa de ayuda entran igual, para que ningún ticket se pierda. El `GET /billing` trae `storage`: `used_bytes`, `limit_bytes`, `plan_storage_gb` (`null` en el de por defecto), `state` (`ok`, `warning` con 90% o más, `full`) y la suma por lugar en `tasks`, `tickets`, `chat` y `direct_messages` (`bytes` y `files`).
- Archivos de la empresa (la pantalla Empresa → Archivos; solo admins y dueño, para los demás 403): `GET /files` (tracker:read) trae `storage` (como en el `GET /billing`), `total`, `has_more` y `files`, del más nuevo al más antiguo (`?sort=size`, los más grandes primero), cada uno con `id`, `origin` (`task`, `ticket` o `chat`), `name`, `type`, `size_bytes`, `created_at`, `uploaded_by`, `project` (`id`, `slug`, `name`), `issue` (`id`, `code`, `title`, `comment_id`), `ticket` (`id`, `number`, `subject`) o `chat_message` (`id`, `thread_id`), según el origen, `url` (dónde está en el tracker), `download_url`, `can_delete` y `locked` (`proof`: versión enviada para aprobación; `trash`: tarea o proyecto en la papelera; `null` cuando se puede eliminar). Filtros: `?origin=`, `?query=` (parte del nombre), `?limit=` hasta 100 y `?offset=`. Los archivos de las conversaciones directas cuentan en el espacio, pero no aparecen: solo las dos personas de la conversación los ven. `GET /files/{id}` (tracker:read) descarga el archivo; `DELETE /files/{id}` (tracker:write) elimina el archivo de donde está y libera el espacio (`{"deleted": true, "origin": ..., "name": ...}`; un archivo bloqueado devuelve 422). En el MCP: `list_files` y `delete_file`.

### Releases y pruebas

- `GET /releases` (tracker:read): releases con estado, `kind` (`version` u `ota`), `stage` (`current` es la que está en producción, una por carril: la versión y la OTA del proyecto tienen cada una la suya; `previous`, `approved`, `testing`, `development`, `planned`, `cancelled`), `stage_label` (el nombre en la pantalla), `is_next` (la próxima en salir), fechas, issues abiertas y la última decisión del Release Gate. Query: `project` y `status` (lista separada por comas).
- `GET /releases/{id}` (tracker:read): la release con `kind`, `stage`, `stage_label`, `is_next`, el resumen de la pantalla (`summary`: `headline`, `context` y `next_step`), las métricas y la sugerencia del gate (`gate.applies`; en una OTA sin BLOCKED registrado, `gate.suggestion.decision` es `not_applicable` y en lugar de las métricas viene `gate.validation`, el conteo de las issues: `ready_for_qa`, `testing`, `approved`, `failed`), la última decisión, las issues, los ciclos de prueba y las notas de la versión.
- `POST /releases` (tracker:write): crea una release, `{"project": "MOB", "version": "2.7.0", "build": "...", "environment": "...", "planned_date": "AAAA-MM-DD", "notes": "...", "kind": "version"}`; `kind` es `version` (el de por defecto) u `ota`. Una release que ya salió (OTA enviada, deploy del backend, versión publicada fuera de tracker) puede nacer en producción, sin el gate: `"status": "production"` y `released_date` (por defecto: hoy). PO/PM o admin. La versión es única en el proyecto: repetir el pedido devuelve la release que ya existe (200, `already_existed: true`), sin cambiar nada en ella. Para que el CI registre una entrega: `POST /releases`, después `POST /issues/bulk` con `"action": "release"` y las issues, y `POST /releases/{id}/status`.
- `POST /releases/{id}/status` (tracker:write): `{"status": "in_development"}`. El camino es planned → in_development → staging (en prueba), y de approved a production; cancelled sale del camino. Una OTA (`kind: "ota"`) va a `production` directo, desde planned, in_development, staging o approved, sin la decisión del gate, a menos que la última decisión sea BLOCKED; la validación sigue después por las issues vinculadas (el `summary.next_step` de `GET /releases/{id}` dice cuántas están en Listo para QA, en prueba, aprobadas y rechazadas). Las demás releases llegan a approved por la decisión APPROVED del gate, registrada por una persona en la pantalla, y producción exige esa decisión. Fuera de ese camino (approved directo, incluido) solo un admin, con `override_note`.
- `POST /releases/{id}/move` (tracker:write): `{"to_project": "PLT"}` mueve la release a otro proyecto de la empresa (ej.: los deploys del backend que nacieron en la app van al proyecto de la plataforma). Lleva el historial, las decisiones del gate y las notas; las issues siguen vinculadas, incluso las de otros proyectos. PO/PM o admin en los dos proyectos; la versión no puede existir en el destino y una release con ciclo de prueba no se mueve. MCP: `move_release`.
- `PUT /releases/{id}/notes` (tracker:write): guarda las notas de la versión, el texto para el cliente que la pantalla copia y envía por correo: `{"release_notes": "# App 2.7.0\n\n## Novedades\n\n- Ahora se puede..."}`. Markdown simple (`# ` título, `## ` sección, `- ` elemento; sin negrita ni enlaces). Reemplaza el texto entero; vacío o `null` vuelve al borrador generado de las issues resueltas. PO/PM o admin. La respuesta trae `release_notes` y `release_notes_saved`. No envía el correo.
- `GET /test-cases` (tracker:read): casos de prueba activos. Query: `project`, `q` (parte del código o del título) e `include_archived`.
- `POST /test-cases` (tracker:write): crea un caso, `{"project": "MOB", "title": "...", "module": "...", "priority": "high", "steps": [{"action": "...", "expected": "..."}]}`. QA o admin. El código (TC-MOB-0001) se genera.
- `GET /test-cycles` (tracker:read): los ciclos más recientes con el avance. Query: `project` y `status`.
- `GET /test-cycles/{id}` (tracker:read): el ciclo con cada caso y el resultado (`pass`, `fail`, `blocked`, `in_progress`, `not_started`), el comentario, quién probó y la issue abierta a partir del fail.
- `POST /test-cycles` (tracker:write): crea un ciclo, `{"project": "MOB", "name": "Regresión 2.7.0", "release": "2.7.0", "environment": "...", "cases": ["TC-MOB-0001"]}`. Sin `cases`, entran todos los casos activos del proyecto. QA o admin.
- `POST /test-cycles/{id}/results` (tracker:write): resultados del CI, hasta 200 por pedido, `{"results": [{"test_case": "TC-MOB-0001", "status": "fail", "comment": "...", "open_issue": true}]}`. `status`: `pass`, `fail`, `blocked` o `in_progress`; comentario obligatorio para fail y blocked. Con `open_issue`, el fail abre la issue ya completada con el caso (o devuelve la que ya existe); si la issue no se abre, el resultado queda guardado y el elemento vuelve con `warning`. Elemento por elemento, con `recorded`, `failed` y `results`. QA o admin.
- `POST /test-cycles/{id}/status` (tracker:write): `{"action": "start" | "finish" | "cancel" | "reopen"}`. Un ciclo cerrado no acepta resultados hasta que se reabre.

### Documentos

- `GET /docs` (tracker:read): documentos de la base de conocimiento que la persona lee, sin el texto (título, `slug`, resumen, proyecto, visibilidad). Query: `project` (prefijo, slug o id, o `empresa` para los de toda la empresa).
- `GET /docs/search?q=<pregunta>` (tracker:read): búsqueda para RAG en los documentos publicados. Devuelve los fragmentos más relevantes (hasta 20, por defecto 5; `limit`), cada uno con el documento, la sección y el texto. `project` lo restringe a un proyecto (los de toda la empresa entran también).
- `GET /docs/{slug o id}` (tracker:read): el documento con el texto en markdown (`body`). Con `?format=markdown` (o `Accept: text/markdown`), solo el markdown.
- `POST /docs` (tracker:write): crea un documento, o actualiza el que tiene el mismo `slug` (201 cuando crea, 200 cuando actualiza). `{"title": "...", "body": "<markdown>", "summary": "...", "project": "MOB", "slug": "...", "visibility": "draft|internal|public"}`. Sin `project`, el documento es de toda la empresa (solo admin). Sirve para publicar la documentación de un repositorio desde el CI.

### Sentry

- `GET /sentry/errors` (tracker:read): errores que envió Sentry (`?project=MOB` para un proyecto, con la integración y el último aviso; `?waiting=true` solo los que esperan decisión, como en la pantalla aqua). El mismo error aparece una vez, con `events`, `users`, releases y `state`: `pending` (juntando avisos antes del triaje), `suggested` (aqua hizo el triaje), `linked`, `regressed` (volvió después de completarse la issue; tracker ya comentó en la issue), `merged`, `dismissed` o `muted`. `triage.issue` es la issue que escribió aqua y `candidate`, la issue abierta que parece la misma.
- `POST /sentry/errors/{id}` (tracker:write): decide como en la pantalla aqua, `{"action": "create_issue", "title": "..."}` (crea la issue completada, con enlace, alcance, release, entorno y stack resumido), `{"action": "link", "issue": "MOB-0012"}` (sin `issue`, la candidata de aqua; comenta en la issue), `{"action": "reopen"}` (un error que volvió) o `{"action": "dismiss", "snooze": "7"}` (`"1"`, `"7"`, `"30"` o `"forever"`). Exige editar issues en el proyecto.
- `GET /sentry/connections` (tracker:read, admins y dueños): las conexiones con Sentry de la empresa, cada una con `project` (el proyecto que recibe lo que sobra), `webhook_url`, `secret_saved`, `sentry_project_targets` (la tabla: `{"sentry_project": "mi-backend", "target": "MOB"}`, con `target` el prefijo del proyecto, `rules` o `ignore`), `new_sentry_projects` (`rules` o `ignore`), `rules` (en orden, `"/v1/pedidos/* → API"` o `"project=APP → APP"`), `environments`, `min_level`, `start_after`, `ai_triage`, `seen` (proyectos de Sentry y entornos que ya mostraron los avisos), el último aviso y `waiting`. `GET /sentry/connections/{id}` trae una.
- `POST /sentry/connections` (tracker:write, admins y dueños): conecta un Sentry, `{"project": "MOB"}`; la respuesta trae `webhook_url` para crear la Internal Integration en Sentry (Settings → Custom Integrations), y el `client_secret` que muestre Sentry entra después. `PATCH /sentry/connections/{id}`: solo cambia lo que venga, `{"client_secret": "...", "sentry_project_targets": [...], "new_sentry_projects": "ignore", "rules": ["/v1/pedidos/* → API", "project=APP → APP"], "environments": ["prod"], "min_level": "error", "ai_triage": true, "active": true, "start_now": true}` (la tabla y las reglas vienen enteras; `project` cambia quién recibe lo que sobra). En la ruta, un `*` al final es prefijo; sin `*` vale la ruta y lo que está debajo de ella. `DELETE /sentry/connections/{id}` desconecta (los errores recibidos se van, las issues creadas quedan).
- `POST /sentry/connections/{id}/test` (tracker:read, admins y dueños): adónde irían los últimos errores de la conexión (hasta 20), con la configuración guardada o con `sentry_project_targets`, `new_sentry_projects`, `rules`, `environments` y `min_level` del cuerpo, sin guardar. Cada elemento trae `result` (`project`, `ignored` u `outside_environment`), `project` (prefijo), `by` (`table`, `rule` con `rule`, el número de la regla, o `leftover`), `below_min_level`, `now_in`, `route` y `tags`. En el MCP: `list_sentry_connections`, `save_sentry_connection` y `test_sentry_connection`.

### Páginas de estado

- `GET /status` (tracker:read, admin o dueño de la empresa): páginas de estado. Servicios (`status` es lo que el cliente ve ahora, el peor entre `manual_status` y los incidentes abiertos; `version`, la versión en producción del proyecto), páginas (`url` pública, `slug`, `published`, `primary`) e incidentes y mantenimientos recientes (`phase`: active, upcoming o closed).
- `POST /status/services` y `PATCH`/`DELETE /status/services/{id}` (tracker:write): `{"name": "App del conductor", "project": "MOB", "description": "...", "status": "operational|degraded|partial_outage|major_outage|maintenance", "show_version": true, "check_url": "https://api.ejemplo.com/health", "sentry_alerts": true}`. En el PATCH, solo cambia lo que venga. `check_url`: un healthcheck cada minuto (más de 3 s es Lento; dos fallas seguidas, Caído); `sentry_alerts`: un error nuevo en el Sentry del proyecto lo deja en Lento durante 30 minutos. La respuesta trae `monitor` con el último resultado.
- `GET /status/pages/{id}`, `POST /status/pages` y `PATCH`/`DELETE /status/pages/{id}` (tracker:write): `{"title": "Estado", "intro": "...", "published": true, "blocks": [{"kind": "projects", "projects": ["MOB"]}, {"kind": "projects", "title": "Plataforma", "projects": ["PLT", "API"], "hidden_services": ["Cola interna"]}, {"kind": "services", "title": "Destacados", "services": ["App del conductor"]}]}`. `blocks` reemplaza todos los bloques, en orden; sin él, quedan como están. Servicio por id o nombre; proyecto por prefijo, slug o id. `slug`: dirección legible en la dirección de la empresa (`/status/<slug>`; sin él, sale del título); `primary: true`: la página que se abre en `/status` (una por empresa). La clave de la página sigue abriendo en cualquier dirección. Apariencia (lo que no venga queda como está): `brand_name` (la marca de la página; `null` vuelve al nombre de la empresa), `accent_color` (`#rrggbb`; `null` vuelve al color por defecto), `links` (hasta 4 botones arriba, `[{"label": "Soporte", "url": "https://..."}]`, `https:` o `mailto:`; reemplaza los que había), `show_history` y `show_past_incidents` (`false` oculta las barras de 90 días y los incidentes cerrados) y `remove_logo: true`; el logo se sube por la pantalla y vuelve en `logo_url`.
- `POST /status/incidents` (tracker:write): abre un incidente o agenda un mantenimiento. `{"kind": "incident|maintenance", "title": "...", "services": ["App del conductor"], "message": "<lo que lee el cliente>", "impact": "degraded|partial_outage|major_outage", "status": "...", "starts_at": "2026-10-18T23:00:00-03:00", "ends_at": "..."}` (el mantenimiento exige `starts_at` y `ends_at`).
- `GET /status/incidents/{id}` (con la línea de tiempo), `POST /status/incidents/{id}/updates` (`{"message": "...", "status": "monitoring", "impact": "degraded"}`; `resolved` o `completed` lo cierra), `PATCH /status/incidents/{id}` (corrige `title`, `services`, `starts_at`, `ends_at` e `impact`, y con `message` también publica una actualización) y `DELETE /status/incidents/{id}` (tracker:write).

### Tableros de ideas

- `GET /ideas/boards` (tracker:read, admin o dueño de la empresa): tableros de ideas, donde los clientes votan lo que quieren ver. Cada uno con `url` (la dirección pública, que el cliente abre sin cuenta), `published`, `ideas` y `pending_suggestions` (sugerencias de clientes esperando al equipo).
- `GET /ideas/boards/{id}` (tracker:read), `POST /ideas/boards` y `PATCH`/`DELETE /ideas/boards/{id}` (tracker:write): `{"title": "Ideas para la app", "intro": "...", "projects": ["MOB"], "published": true, "access": "link|restricted", "allow_list": ["@cliente.com", "ana@gmail.com"], "allow_suggestions": true}`. En el PATCH, solo cambia lo que venga; `projects` y `allow_list` reemplazan la lista entera. `link`: cualquiera con el enlace lo ve, y votar pide el correo confirmado por código; `restricted`: solo quien confirme un correo de la lista ve las ideas. El GET trae `ideas` (publicadas, las más votadas primero, con el `status` que ve el cliente, `issue`, `votes` y `comments`) y `pending_suggestions` (con `suggested_by`, el correo del cliente). Una idea de un proyecto que salió del tablero desaparece solo de la página pública.
- `POST /ideas/boards/{id}/ideas` (tracker:write): crea una idea publicada, `{"title": "Exportar a PDF", "description": "...", "project": "MOB", "issue": "MOB-0012", "status": "under_review|planned|in_progress|released|closed"}`. Con `issue` (de un proyecto del tablero; sin `title`, usa el de la issue), el estado sigue al de la issue; sin ella, vale `status`. Cuando el estado cambia a planned, in_progress, released o closed, quien votó recibe un correo, en el idioma de la página en la que confirmó su correo.
- `GET /ideas/{id}` (tracker:read; la idea con los votos y los comentarios, con el correo de quien escribió), `PATCH /ideas/{id}` (los campos de la creación, solo cambia lo que venga; `"issue": null` desvincula la issue; `{"published": true}` publica la sugerencia de un cliente) y `DELETE /ideas/{id}` (la elimina; así se rechaza una sugerencia) (tracker:write). `POST /ideas/{id}/comments` con `{"body": "..."}` responde como el equipo (el cliente ve "Equipe <empresa>"); `DELETE /ideas/comments/{id}` borra un comentario.

### Invitaciones a la beta

- Invitaciones a la beta (solo el equipo de la plataforma con la sección Invitaciones beta: ver para listar, gestionar para generar, reenviar y revocar; para los demás, 403): tracker está en beta con acceso por invitación, y la invitación a la beta deja que una persona de afuera cree la cuenta aquarela ID y su propia empresa, como dueña (es distinta de la invitación a una empresa que ya existe, que queda en Empresa → Personas). `GET /beta-invites` (tracker:read) lista las invitaciones, las más nuevas primero, con `status` (`active`, `used`, `expired`, `revoked`), `email`, `note`, `uses` de `max_uses`, validez, `created_by` (quién la generó) y `redemptions` (quién la usó, con la empresa que creó). `POST /beta-invites` (tracker:write) genera una, `{"email": "persona@empresa.com", "note": "Ana, de Construtora Sul", "days": 14}`: con `email`, el enlace va por correo y solo vale para la cuenta de ese correo (una empresa); sin él, `max_uses` dice cuántas empresas crea el enlace (por defecto 1). 201 con `url` (el enlace, que no vuelve a aparecer), `expires_at` y `emailed` (`sent`, `disabled` o `failed`; `null` sin correo). El correo sale en el idioma de la cuenta con ese correo, si existe, o en el de quien la genera. `POST /beta-invites/{id}/resend` (tracker:write) la envía de nuevo con un enlace nuevo (el anterior deja de valer) y `DELETE /beta-invites/{id}` la revoca; las empresas ya creadas siguen.

### Plataforma

- Plataforma (solo el equipo de la plataforma, que administra tracker; para los demás, 403): el dueño de la plataforma elige, sección por sección, lo que cada persona del equipo ve (`view`) o gestiona (`manage`): `overview` (visión general), `organizations` (empresas), `support` (entrar como soporte), `invites` (invitaciones a la beta), `ai` (uso de IA), `plans` (planes, precios y el plan de cada empresa) y `audit` (registro). `GET /me` y whoami traen `platform` (`{"role": "owner|staff", "sections": {"organizations": "manage", ...}}`) para quien es del equipo y ve al menos una sección, y las tools de plataforma solo aparecen con la sección. Cambiar el equipo y entrar como soporte quedan solo en la app, en Plataforma.
- `GET /platform/overview` (tracker:read, sección overview): los números de tracker: empresas (`active`, `suspended`, `archived`, `created_last_30_days`), personas, proyectos e issues, uso de IA en los últimos 30 días (llamadas, costo en USD, empresas que la usaron), invitaciones a la beta y la versión en producción.
- `GET /platform/organizations` (tracker:read, sección organizations): todas las empresas, con `status` (`active`, `suspended`, `archived`), dueños, miembros, proyectos, issues, llamadas de IA en 30 días, última actividad y el motivo de la suspensión. Filtros `?status=suspended` y `?query=` (nombre o slug).
- `POST /platform/organizations/{id}/suspend` (tracker:write, organizations para gestionar), con `{"reason": "..."}` opcional: la empresa queda suspendida, y los miembros dejan de entrar en la app, la API, el MCP y Slack hasta que alguien la reactive (`POST /platform/organizations/{id}/reactivate`). No se borra nada; el equipo con la sección de soporte sigue entrando.
- `POST /platform/demo-account` (tracker:write, organizations para gestionar), con `{"email": "revision@ejemplo.com", "name": "Cuenta de demostración"}`: prepara la cuenta que usan los revisores de la App Store y de ChatGPT. Crea la Empresa Demo (`empresa-demo`) con dos proyectos, 16 issues en varios estados, releases, un ciclo de prueba y un documento, con la cuenta del `email` como dueña y tres colegas ficticios (@example.com, sin correo ni push). El revisor entra con la cuenta aquarela ID de ese correo, creada allá con contraseña. Correrlo de nuevo solo garantiza la cuenta activa y dueña (`created: false`). Rechaza (422) el correo de alguien del equipo de la plataforma y una cuenta que ya participa en otra empresa.
- `GET /platform/plans` (tracker:read, sección plans): los planes, con `key`, `name`, `public`, `active`, `is_default` (el plan de quien no tiene suscripción), `ai_credits_per_cycle`, `storage_gb` (el espacio de archivos en GB, `null` para el de por defecto de 10 GB), `features` (lo que incluye el plan, en el orden de las tarjetas y de la comparación), `prices` (`currency`, `interval`, `amount_minor`) y `subscriptions` (cuántas empresas están en él). `POST /platform/plans` (tracker:write, plans para gestionar) crea el plan o edita el que tiene la misma `key`: `{"key": "pro", "name": "Profesional", "ai_credits_per_cycle": 30000, "storage_gb": 50, "prices": [{"currency": "BRL", "interval": "month", "amount_minor": 4900}, {"currency": "USD", "interval": "month", "amount_minor": 900}]}`; `prices` y `features` (hasta 12 elementos de hasta 120 caracteres; escríbelos igual en los planes que tienen el mismo elemento, para que la comparación marque los dos) son listas enteras (lo que no venga sale). Siempre hay un plan por defecto: `is_default` le quita la marca al otro.
- `GET /platform/subscriptions` (tracker:read, sección plans): el plan, la moneda, la suscripción, los datos de facturación y el pedido de plan (`plan_request`) de cada empresa. `PATCH /platform/organizations/{id}/subscription` (tracker:write, plans para gestionar) cambia `plan` (clave o id), `status`, `interval`, `currency`, `current_period_start`, `current_period_end`, `trial_ends_at` y `note`, solo los enviados. Sin cobro: registra lo acordado con el cliente. Poner la empresa en el plan que pidió atiende el pedido. Los créditos de IA del plan valen para la empresa sin ajuste propio en Créditos de IA.
- `GET /platform/team` (tracker:read, quien es del equipo y ve al menos una sección): el equipo de la plataforma, con el rol y las secciones de cada uno.
- `GET /platform/audit` (tracker:read, sección audit): el registro de la plataforma, los más nuevos primero: quién entró como soporte en qué empresa, empresas creadas, suspendidas, reactivadas, archivadas y eliminadas, cambios en el equipo y en las invitaciones a la beta. Filtros `?action=support_access`, `?organization_id=`, `?actor_id=`, `?limit=` (hasta 200) y `?before=` (la `created_at` de la última fila, para la página siguiente). En el MCP: get_platform_overview, list_platform_organizations, suspend_organization, reactivate_organization, prepare_demo_account, list_platform_team, list_platform_audit, list_platform_plans, save_platform_plan, list_platform_subscriptions y save_organization_subscription.

## Webhooks de salida

tracker avisa a otro sistema cuando algo cambia en una issue: un POST en JSON, firmado, a la dirección que registre la empresa. Los admins y dueños de la empresa crean los webhooks en la app, en Empresa → Integraciones → Webhooks (hasta 20 por empresa), eligiendo los eventos y si el webhook vale para todos los proyectos o solo para uno. La misma pantalla muestra el secreto, las últimas entregas con la respuesta del receptor, el botón de reenviar y el envío de prueba (`ping`). Crear y cambiar webhooks no existe en la API ni en el MCP.

Eventos:

- `issue.created`: Tarea creada. Desde la pantalla, por correo o por el MCP.
- `issue.updated`: Tarea editada. Campos, registro técnico, release, etiquetas, épica, sprint, estimación y plazo. Viene con lo que cambió.
- `issue.status_changed`: Estado cambió. Incluso por la reprueba (pasó o falló).
- `issue.assigned`: Responsable cambió. Responsable o QA responsable.
- `comment.created`: Comentario nuevo. Incluso respuestas y respuestas por correo.
- `automation.triggered`: se disparó una regla de automatización del proyecto con la acción "Enviar webhook". Va solo al webhook que eligió la regla, esté suscrito al evento o no, y trae `automation.rule` (`id`, `name`).
- `ping`: el envío de prueba de la pantalla, con `message`.

Cuerpo (JSON), igual en todo evento: `id` (del evento; el mismo en los reintentos, úsalo para no procesar dos veces), `event`, `created_at`, `organization` (`id`, `slug`, `name`), `actor` (`id`, `name`: quien hizo el cambio) e `issue`, el retrato de la issue en ese momento: `id`, `code`, `title`, `url`, `type`, `status` (la clave del flujo), `status_name`, `status_category` (`todo`, `in_progress`, `done`), `severity`, `priority`, `project` (`id`, `name`, `key`), `assignee`, `qa_owner` y `reporter` (`id`, `name`, o `null`), `labels`, `release_id`, `fixed_version`, `description`, `custom_fields`, `created_at`, `updated_at` y `resolved_at`. `issue.updated`, `issue.status_changed` e `issue.assigned` traen `changes`, campo por campo, `{"status": {"from": "open", "to": "doing"}}` (las claves del historial de la issue). `comment.created` trae `comment` (`id`, `body` en markdown, `parent_id` cuando es respuesta, `author`, `mentions` y `url`).

Encabezados: `Content-Type: application/json`, `User-Agent: tracker-by-aquarela-webhooks/1.0`, `X-Tracker-Event` (el evento), `X-Tracker-Delivery` (el id de la entrega) y `X-Tracker-Signature`.

Entrega: solo direcciones `https` públicas (la red interna se rechaza), sin seguir redirecciones, con 10 s para responder. Cualquier 2xx cuenta como entregado; otra respuesta, o ninguna, se reintenta hasta 6 veces, después de 1 min, 5 min, 30 min, 2 h, 6 h. Responde rápido y procesa después: el orden de llegada no está garantizado.

Firma: `X-Tracker-Signature: t=<unix>,v1=<hex>`, donde `v1` es el HMAC-SHA256, en hexadecimal, del texto `<t>.<cuerpo crudo>` con el secreto completo del webhook (`whsec_...`) como clave. Recalcúlalo con el cuerpo exactamente como llegó (antes de interpretar el JSON), compara en tiempo constante y rechaza `t` con más de 5 minutos de diferencia. Durante el cambio del secreto el encabezado puede traer más de un `v1`: acéptalo si alguno coincide. En Node:

```js
import { createHmac, timingSafeEqual } from "node:crypto";

// body: o corpo cru da requisição (string), antes do JSON.parse
function isFromTracker(body, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const t = Number(parts.t);
  if (Math.abs(Date.now() / 1000 - t) > 300) return false; // mais de 5 min
  const expected = createHmac("sha256", secret).update(`${t}.${body}`).digest();
  const given = Buffer.from(parts.v1 ?? "", "hex");
  return given.length === expected.length && timingSafeEqual(given, expected);
}
```

## Conceptos

- Organización: la empresa cliente. Los datos de una organización nunca aparecen en otra.
- Plataforma: el equipo de Aquarela que administra tracker. El dueño de la plataforma define lo que cada persona ve o gestiona (empresas, soporte, invitaciones, créditos de IA, registro); una empresa suspendida queda sin acceso hasta que se reactive.
- Beta: tracker está en beta, con acceso por invitación. El equipo de la plataforma genera la invitación a la beta (`create_beta_invite`, `POST /beta-invites`); quien la usa crea la cuenta y su propia empresa, y la invitación registra quién invitó a quién.
- Proyecto: tiene un prefijo (ej.: MOB) usado en el código de las issues (MOB-0001), y sus propios tipos de issue, etiquetas, módulos y campos personalizados.
- Flujo de estados: cada proyecto tiene sus estados, cada uno en un grupo (todo, in_progress, done), con los perfiles que pueden mover issues a él y desde qué estados puede venir. Los proyectos empiezan con la plantilla Software + QA (Triaje, Por hacer, Haciendo, En revisión, En prueba, Hecho, Cancelado), la plantilla Software (Por hacer, Haciendo, En revisión, Hecho) o la plantilla QA (Borrador, Abierto, Triaje, Priorizado, En desarrollo, Listo para QA, En retest, Aprobado, Rechazado, Completado y Cerrado). "Abierta" es todo lo que no está en el grupo done.
- Retest: QA registra el resultado con versión y dispositivo. En Software + QA, aprobar lleva a Hecho y rechazar devuelve a Por hacer con la marca de rechazada (`retest_failed`), hasta que pase; en la plantilla QA, va a Aprobado o Rechazado.
- Cerrado (plantilla QA): un solo estado para lo que se cierra sin resolverse, con el motivo (`close_reason`: duplicada, cancelada, no reproducida o no se hará).
- Bloqueo: una marca en la issue, con el motivo, que vale en cualquier estado y flujo (no es una columna). Filtra con `blocked_only` o `bloqueio = sim` en la consulta avanzada.
- Los proyectos creados antes de estos flujos siguen con los estados anteriores (En retest, Aprobado, Rechazado, Bloqueado, Reabierto, Duplicado...) hasta que alguien pide "Simplificar el flujo" en Configuración > Flujo de estados.
- Severidad: S1 (crítica) a S4 (baja), impacto técnico. Prioridad: urgent, high, medium, low, importancia de negocio.
- Horas: cada issue tiene estimación, inicio y plazo, y registros de horas (quién, día, cuánto, qué hizo). Restante = estimación menos lo registrado. Duración en texto: "2h", "1h30", "45m", "1d" (8 h).
- Documentos: la base de conocimiento de la empresa (manuales, reglas de negocio, preguntas frecuentes, procesos), en markdown, de toda la empresa o de un proyecto. El borrador (draft) solo lo ve quien edita; internal vale para el equipo (admins y quien es PO/PM, QA o dev del proyecto; los stakeholders no lo ven) y para los agentes conectados en su nombre; public aparece también en el centro de ayuda público de la empresa, que tiene su propio llms.txt (y llms-full.txt, con todos los textos) para chatbots y RAG sin login. La búsqueda (search_docs, GET /docs/search) devuelve fragmentos por sección, sin distinguir tildes.

## Releases y pruebas

Guía completa en la pantalla /releases/como-funciona (botón "Cómo funciona" en la lista de releases).
- Release: una versión de un proyecto (ej.: App 2.4.0), con fecha prevista, issues vinculadas, ciclos de prueba y notas de la versión (un borrador generado de las issues resueltas, editable, para copiar o enviar al cliente).
- Camino de la release (estado): Planificada (planned: en el plan, nadie trabajando todavía) → En desarrollo (in_development: el equipo hace las issues) → En prueba (staging: QA prueba en ciclos de prueba; antes se llamaba Homologación) → Aprobada (approved: el gate registró APPROVED, lista para salir) → En producción (production: salió). Cancelada (cancelled) sale del camino y puede volver a Planificada. El PO/PM mueve el estado; Aprobada normalmente viene de la decisión APPROVED del gate; En producción exige el gate APPROVED (sin él, solo un admin, con justificación), menos en la OTA.
- Cuál está en producción y cuál es la próxima (el momento de la release): versión y OTA son carriles separados: en cada proyecto, la versión más reciente en producción es la versión en producción y la OTA más reciente en producción es la OTA en producción (current, "En producción"); las demás en producción del mismo carril son anteriores (previous). La próxima (is_next, sello "Siguiente") es la que sale después en su carril: la más avanzada en el camino (Aprobada, después En prueba, En desarrollo, Planificada) y, en empate, la de fecha prevista más temprana. La lista de releases se separa en En producción, En prueba (incluye Aprobadas), Siguientes y Anteriores (incluye Canceladas).
- Actualización sin tienda (OTA): una release con tipo (kind) "ota", para lo que llega a los dispositivos sin pasar por la App Store o la Play Store. Crea una release nueva para cada OTA, con la versión que lleva (ej.: 2.4.1) y el build. Puede seguir el camino con ciclo de prueba y gate, pero no hace falta: un hotfix por OTA sale en horas y QA lo valida después, ya en producción. Por eso la OTA va a En producción desde cualquier paso, sin decisión en el gate, y hasta se puede crear directamente como En producción (estado "production") cuando ya se envió. Cualquier release que ya salió fuera de tracker (deploy del backend, versión publicada antes) también puede nacer En producción, con la fecha en que salió. Su página muestra la validación por las issues vinculadas: cuántas están en Listo para QA, En prueba, Aprobado y Rechazado. Publicada, pasa a ser la OTA en producción del proyecto y la OTA anterior va a Anteriores; la versión de la tienda sigue en producción, porque son carriles separados. Una versión normal (tienda o deploy) es kind "version", la de por defecto.
- En la página de la release, la parte de arriba dice en una frase dónde está, cuál está en producción y cuál es el próximo paso (ej.: crear un ciclo de prueba, terminar las pruebas, corregir lo que falló, registrar APPROVED, publicar).
- Issue y release: el campo Release de la issue dice en qué versión sale (la release principal), normalmente de su proyecto; cuando la corrección sale solo en otro proyecto (el deploy de la plataforma, por ejemplo), la principal puede ser la release de allá; la tarjeta del tablero y la issue muestran, por ejemplo, "Sale en la 2.4.0 · en prueba". Cuando la misma corrección sale en más de un lugar (deploy del backend, la OTA de cada runtime, consola), las demás releases entran en "También sale en", de cualquier proyecto de la empresa. La issue aparece en todas: en la lista de la release, en el gate, en las notas de la versión y en la validación. La "Versión corregida" del registro técnico es solo la anotación del dev sobre el build de la corrección.
- Caso de prueba: un guion con pasos y resultado esperado, reutilizado en cada versión. Ciclo de prueba: una ronda de pruebas de una release; cada caso recibe aprobado, falló o bloqueado, y un FAIL puede abrir una issue ya completada.
- Release Gate: la decisión de QA de liberar (APPROVED) o retener (BLOCKED) la versión. La app sugiere APPROVED cuando hay un ciclo de prueba con casos, todas las pruebas pasaron y no hay una issue S1 abierta; una S2 abierta solo genera un aviso.
