# tracker by aquarela

> Aquarela's tracker for issues, tests and releases, with separate organizations (companies) and projects. It records bugs and improvements with evidence, test cycles and the Release Gate of each version, and keeps each company's knowledge base (documents). The interface speaks Portuguese, English and Spanish.

The whole app requires sign-in (aquarela ID). Agents connect through the MCP server below, and scripts and CI through the REST API, always on behalf of a person and their organizations: the ones they choose when authorizing the MCP (one, several or all), or the personal token's one.

Live version: 0.2.0 · 0cc7eb9 (semantic versioning; the commit identifies the deploy). Also in `GET /api/v1/version`, in whoami and in the MCP serverInfo.

The same manual, one part per page, is on the developer portal: https://developers.tracker.aquarela.app/en

This manual in other languages: Português: https://tracker.aquarela.app/llms.txt · Español: https://tracker.aquarela.app/llms.txt?lang=es

## MCP server

- URL: https://tracker.aquarela.app/mcp
- Transport: Streamable HTTP, stateless, JSON responses (POST).
- Authentication: OAuth 2.1 with PKCE (S256) and dynamic client registration (RFC 7591). The client discovers everything from the /mcp 401 (RFC 9728).
- Resource metadata: https://tracker.aquarela.app/.well-known/oauth-protected-resource/mcp
- Authorization server metadata: https://tracker.aquarela.app/.well-known/oauth-authorization-server
- The access token lasts 1 hour; the refresh token lasts 30 days and is rotated on every use.

## How to connect

- Claude (web and desktop): Settings > Connectors > Add custom connector, with the URL https://tracker.aquarela.app/mcp.
- Claude Code: `claude mcp add --transport http tracker https://tracker.aquarela.app/mcp` and then `/mcp` to authorize.
- Codex: `codex mcp add tracker --url https://tracker.aquarela.app/mcp` and then `codex mcp login tracker`.
- ChatGPT: Settings > Apps and connectors > Create, with the URL https://tracker.aquarela.app/mcp and OAuth authentication.

On the authorization screen the person picks the organizations (one, several, or all of them, including the ones they join later) and whether the app can create issues. With more than one, every tool accepts the `organization` argument (the company slug or name, see whoami); without it, the company comes from the issue or project in the request when only one of them has that code, whoami and list_projects show all of them, and search_issues without `project` searches all of them (each issue with `organization`). Connections are listed at https://tracker.aquarela.app/account/connections, where they can be revoked.

Without a browser (CI, an agent running in a pipeline), /mcp also accepts the REST API personal token in the `Authorization: Bearer <token>` header, for example `claude mcp add --transport http tracker https://tracker.aquarela.app/mcp --header "Authorization: Bearer $TRACKER_TOKEN"`.

## Permissions

- `tracker:read`: Read projects, issues, releases, test cycles and documents
- `tracker:write`: Create issues, change their status and write documents on your behalf

Inside the organization, the connection sees only the projects the person is a member of (organization admins and owners see all of them) and every action follows their role in the project (PO/PM, QA, dev, stakeholder).

## Tools

Tool descriptions are in Portuguese, exactly as the MCP server sends them to agents.

- `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.

## Has this happened before? (before creating an issue)

tracker keeps a context card for each issue, written by aqua (the problem in neutral words, where it happens and the terms someone would use to describe the same problem another way). "Has this happened before?" cross-checks those cards, and aqua says, for each similar one, whether it's the same problem or just related, with the reason in one sentence. Agents use the same path to avoid opening duplicates:

1. Before creating, ask: `search_similar` in the MCP (or `GET /search`) with `query` = the problem's title and description (the error message, the customer's report). To also see Help desk tickets, `kinds: ["issue", "ticket"]`.
2. `relation: "same"` (the same problem): don't create another one. Comment on the existing issue (`add_comment`) with what's new (another customer, another environment, the evidence) and tell the person which one it is. If the existing one was already closed and the problem came back, create the new one and link it with `link_issues` (`duplicates`), saying it came back.
3. `relation: "related"`: create it and link it with `link_issues` (`relates_to`); the `reason` explains the link and fits in the comment.
4. After creating, `create_issue` returns `similar` with the new issue's similar ones that aqua confirmed (in the API, `POST /issues?similar=true`): link the ones that make sense.

Without aqua turned on for the company, `explained` comes back `false` and the search's `likely_duplicate` applies. For a batch (import, CI), use `external_id`, which already prevents repeating the same issue.

## REST API

For CI, scripts and imports from other tools. JSON both ways.

- Base: https://tracker.aquarela.app/api/v1
- Authentication: a personal token in the `Authorization: Bearer <token>` header. The person creates the token at https://tracker.aquarela.app/account/tokens, picks how long it lasts (30, 90, 180, 365 days) and whether it can create issues; the token works for the organization it was created in, is shown only once and can be revoked on the same screen (up to 20 active per person). MCP OAuth tokens don't work in the API.
- Same rules as the interface: the token sees only the person's projects and creates issues only where their role allows. A person deactivated or removed from the organization turns the token off immediately.
- Links: an issue's `url` is the canonical link by code, `https://tracker.aquarela.app/issues/MOB-0012?org=<company slug>`, which is readable, typeable and keeps working after the issue moves to another project (the old code still opens it). The prefix is unique only within a company, so `org` says which one: the app switches to it when the person is a member. Without `org`, `/issues/MOB-0012` looks first in the selected company and then in the person's other companies (if the code exists in more than one, it asks which one to open); `/issues/<id>` also works, in any of the person's companies.
- Limit: 120 calls per minute per token (429 with Retry-After).
- Errors: `{"error": "<code>", "message": "<text>"}`, with 400 (invalid data, plus `details` per field), 401 (missing or invalid token), 403 (role or token without permission), 404 (also for a project in the trash and its issues: deleting and restoring projects happens in the app, admins only), 409 (already exists), 413, 415, 422 (refused by the project's rules), 429. `message` comes in the token owner's language (`locale` in `GET /me`; on Automatic, the request's `Accept-Language`); `error` doesn't change.
- `GET /version` (public, no token): the version of tracker that's live, `{"version": "0.2.0", "commit": "5f83c7b"}` (`commit` is the deploy, `null` outside one). The `v<version>` tag on GitHub marks each version. `GET /me` and whoami return the same in `server`.

Markdown: every long text is markdown. On an issue, `description` and the other long texts (`preconditions`, `steps_to_reproduce`, `actual_result`, `expected_result`, `impact`, `suggested_solution`, `root_cause`, `fix_notes`) and the text of comments; outside issues, release notes, the precondition, data, steps and expected result of test cases, the comment of each test result, the description and replies of ideas, status page incident updates, the comment on logged hours and the one on approval decisions. You can use `**bold**`, `*italic*`, `~~strikethrough~~`, lists with `-` or `1.` (sublists indented two spaces), code between backticks (one for inline, three for a block), `[link](https://...)`, `> quote` and headings with `##`. The API returns the text as written (raw markdown); tracker's screens (web and app) and e-mails show it formatted. HTML isn't interpreted.

### Account, people and brand

- `GET /me` (tracker:read): the person (`user`, with `id`, `name`, `email`, `locale` and `avatar_url`), the organization, the projects with the role in each one, the token and the version of tracker (`server`). Each company in `organizations` has `slug`, `public_url` (the company's public address, like `https://suaempresa.centraldeajuda.ia.br`: the root is the help center, `/status` the published status page and `/suporte/...` and `/ideias/...` the help desk portal and the idea boards; `null` with the public address turned off), `app_url` (the company in tracker, `null` without a per-company subdomain) and `brand` with `square_logo_url` and `wide_logo_url` (public, `null` without the logo). whoami returns `public_url` too.
- Language: `user.locale` (in `GET /me` and whoami) is the language the person chose in Your account: `pt-BR`, `en` or `es`; `null` is Automatic, following the browser's or device's language (with none of the three, Portuguese). `PATCH /me` (tracker:write) with `{"locale": "en"}` changes it (`null` goes back to Automatic) and returns `{"locale": ...}`. It applies to the screens, e-mails, device notifications and apps, in all of the person's companies (on Automatic, e-mails and notifications follow the last language the person's browser or device asked for); it doesn't translate what was already written in tasks and chats. An app that shows its interface in the device's language uses `user.locale` when it isn't `null`. The ready-made sentences the API returns (the `text` of `GET /notifications`, of `GET /inbox`, of `GET /issues/{ref}/history` and the activity of `GET /people/{ref}`, also in `list_inbox`, `get_issue_history` and `get_person`; the profile's `role_label` too) come in that language, with template status and type names translated; on Automatic, by the request's `Accept-Language`. In the MCP: `set_language`.
- `GET /me/deletion` (tracker:read): what deleting the account would do now: `email`, `can_delete` (with `blocker`, the reason, when it can't: the platform owner hands over the role first) and `organizations`, each with `outcome` (`deleted`: only this person is a member and the whole company goes away; `owner_transferred`: they're the only owner and `new_owner` becomes the owner; `left`: they just leave). `DELETE /me` (tracker:write) with `{"confirm_email": "<account e-mail>"}` deletes it, with no way back: it erases name, e-mail, photo, login, tokens, connections and preferences and the aquarela ID account (`aquarela_id`: `deleted`, or `pending` when support finishes it there); what the person wrote stays with each company as "Deleted account". The token stops working immediately. Not available in the MCP: deleting the account belongs to the person alone, on the website, in the app or in the API.
- `GET /people/{me, id, e-mail or name}` (tracker:read): the profile of a person in the company, as on the /people/<id> screen. `person` (`id`, `name`, `email`, `active`, `organization_role` and `organization_role_label`, `member_since`, `url`, the profile on screen, and `avatar_url`), `all_projects` (`true` for company admins and owners, who count in every project), `projects` (`name`, `prefix`, `slug`, `role`, `role_label`), `counts` (`open_assigned`, `open_qa`, `done_last_30_days`), `open_issues` (the most recent open ones with them as assignee: `code`, `title`, `status`, `severity`, `project`, `updated_at`, `url`) and `activity` (up to 20 comments and changes, most recent first: `kind` `comment` or `history`, `at`, `issue`, `text` and, when a value changed, `from` and `to`). Only the projects and issues the token sees are included; logged hours, only for those who see hours. Someone who only follows the projects (stakeholder) sees only the people in them, with `email` `null`. In the MCP: `get_person`.
- Person's photo: `avatar_url` (in `GET /me`, `GET /people/{ref}` and `author_avatar_url` in chat messages) is `GET /people/{id}/avatar` (tracker:read, the image, with the token; 404 without a photo), `null` without a photo. The address changes (`?v=`) on every swap, so it can be cached by address: with the photo changed on screen, the new `avatar_url` arrives on the next read. `PUT /me/avatar` (tracker:write) changes your own photo: `multipart/form-data` with the image in the `avatar` field, or JSON `{"image_base64": "...", "file_name": "photo.jpg"}` (images over 1.5 MB go as multipart). PNG, JPG or WebP up to 2 MB; the screen and the apps crop to a square and send a JPG of up to 512 px, and it's worth doing the same (the photo shows in a circle). `DELETE /me/avatar` removes it (the initials remain). The response has `id`, `name` and `avatar_url`. Anyone who shares a company with the person sees the photo. In the MCP: `set_avatar`.
- `GET /brand` (tracker:read): the brands for the apps: the company (`organization`, with `slug`, `public_url` and `app_url` as in `GET /me`) and its logos (`organization.square_logo_url` and `wide_logo_url`), the "tracker by aquarela" signature with its files (`product`; `product.by_url` is where "by aquarela" leads, Aquarela's website, in a tab or the browser), the rules and `placements`, where each one goes (`place`, `organization`: `square`, `wide` or `null`; `product`: `icon`, `signature`, `signature_large`, `tab_title`, `header` or `null`). tracker by aquarela never disappears: where the company takes the top, the signature moves to the footer.
- `PUT /brand/logos/{square|wide}` (tracker:write, admins and owners): changes the square logo (icon, company switcher, collapsed menu) or the wide one (top of the menu, sign-in, invites, e-mails, portal). `multipart/form-data` with the image in the `logo` field, or JSON `{"image_base64": "...", "file_name": "logo.png"}` (the JSON body goes up to 2 MB, so images over 1.5 MB go as multipart). PNG, JPG or WebP up to 2 MB; the square one with similar width and height (at least 64 px), the wide one wider than tall. `DELETE /brand/logos/{square|wide}` removes it. The response has the company and both addresses. In the MCP: `set_organization_logo`.

### Projects

- `GET /projects` (tracker:read): projects with the project type (`kind`: `software`, `design`, `admin` or `support`, and the name in `kind_label`), issue types (each one's level and, when there is one, the `template` for the text fields), status workflow, custom fields, labels, modules, the project color (`color`, `#rrggbb`) and icon (`icon_url`, with the same token, or `null` without an icon: show the prefix in a box in the color) and the planned start and end (`start_date`, `target_date`, YYYY-MM-DD or `null`). Use it before creating issues.
- `GET /projects/{prefix, slug or id}` (tracker:read): the project Summary, its first tab on screen. `project` (as in `GET /projects`), `url`, `dates` (`start_date`, `target_date`, `days_elapsed` since the start, `days_remaining` to the end, negative once it's passed, and `time_progress`, the fraction of the planned time already gone, from 0 to 1, with both dates), `issues` (`total`, `open`, `done`, `late`), `active_sprint` (or `null`, with `days_left`), `next_release` (the one with the nearest planned date that hasn't shipped, or `null`), `late` and `upcoming` (up to 5 open issues past due and with a due date ahead: `code`, `title`, `due_date`, `assignee`, `url`) and `people` (`name`, `role`). In the MCP: `get_project`.
- `POST /projects` (tracker:write; whoever configures some project, or an admin): creates a project, `{"name": "Driver app", "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` and `target_date`, planned start and end, optional). `kind`: `software` (default), `design`, `admin` or `support`; `workflow` (software): `software_qa` (default), `software` or `qa`. Name and prefix are unique in the company (422 when they already exist, including in the trash). The creator joins as PO/PM. Returns `project` as in `GET /projects` (and `warning` if the color or icon couldn't be saved; try again with `PATCH /projects/{ref}/brand`). Creating from another project happens in the app. In the MCP: `create_project`.
- `PATCH /projects/{prefix, slug or id}` (tracker:write; PO/PM or admin in the project): planned start and end, `{"start_date": "2026-11-03", "target_date": "2027-02-26"}`; only what's sent changes and `null` clears it. The start can't come after the end (422). Returns `project`, `start_date` and `target_date`. In the MCP: `plan_project`.
- `PATCH /projects/{prefix, slug or id}/brand` (tracker:write, PO/PM or admin in the project): color and icon; only what's sent changes. JSON `{"color": "verde", "library_icon": "carro-eletrico"}`: `color` is a palette name, in Portuguese (azul, violeta, rosa, vermelho, laranja, âmbar, verde, turquesa, grafite), or `#rrggbb`, and `null` goes back to the automatic color; the icon is one of three: `library_icon` (a ready-made icon, painted in the project color), `image_base64` (your own square icon, PNG, JPG or WebP up to 2 MB; or `multipart/form-data` with the image in the `icon` field and the other fields as text) or `"remove_icon": true` (back to the prefix in the colored box). The ready-made icon is painted when it's set: changing only the color doesn't repaint it, so send `library_icon` again along with the new color. In multipart, an empty `color` goes back to automatic. Returns `color`, `color_chosen` and `icon_url`. In the MCP: `set_project_brand`.
- `GET /project-icons` (tracker:read): the library of 108 ready-made icons, by group, with `slug`, `name` and `url` (the drawing in black, transparent background); `?query=eletrico` filters by name, slug or group, without accents (the names are in Portuguese). In the MCP: `list_project_icons`.
- `GET /projects/{prefix, slug or id}/people` (tracker:read): the project's active people, with id and role. Use it to pick the assignee.
- `GET /projects/{prefix, slug or id}/sprints` (tracker:read): the project's planning: open sprints with their issues and points, the top of the backlog (`backlog.total` and the first issues, in priority order) and the velocity of the last sprints.
- `GET /projects/{prefix, slug or id}/timeline` (tracker:read): the project Timeline. `project` (with `start_date` and `target_date`), `url`, `can_plan` (whether the person changes the issues' dates), `items` (issues with a start or due date, up to 300, open ones and those with the date closest to today first: `code`, `title`, `status`, `category`, `closed`, `group` and `group_kind`, the row's epic or module, `is_epic`, `sprint`, `assignee`, `start_date`, `due_date`, `first_day` and `last_day` of the bar, `late`, `estimate_minutes`, `spent_minutes`, `url`), `truncated`, `dependencies` (`blocker`, `blocked` and `conflict`, when the blocking one ends on or after the start of the blocked one), `sprints`, `releases` (with a planned date) and `unscheduled` (`total` and up to 20 open ones without a start or due date). To change dates: `PATCH /issues/{ref}/plan` (the issue) and `PATCH /projects/{ref}` (the project). In the MCP: `get_timeline`.
- `GET /projects/{prefix, slug or id}/repositories` (tracker:read): connected GitHub repositories and GitLab projects, with the status each event applies, the last event received and `connection`: `github_app` (the company's GitHub app), `gitlab_token` (tracker created and maintains the webhook in GitLab) or `webhook` (set up by hand). Connecting and disconnecting happens in the app.
- `GET /panel?project=` (tracker:read): a project's wall panel in one call (board, help desk, releases, the week, charts and `aqua.topics`, aqua's ready-made questions, or `null` without AI). `GET /panel/aqua?project=&topic=` (tracker:read): aqua's answer to one of those questions (`overview`, the default, `attention`, `week`, `releases` and `support`, the last one only in a project with a help desk), as bullet points, with `generated_at`; the same question repeats the answer for 30 minutes.

### Issues

- `GET /issues` (tracker:read): search. Query: `project`, `status` (keys of the project's workflow), `category` (`todo`, `in_progress`, `done`), `severity`, `priority`, `type`, `nature` (`problem`, `improvement`, `task`: the type's nature, which works in any project) (comma-separated lists), `label`, `assignee` (`me`, `none` or id), `open_only` (true), `blocked_only` (true: only those with the blocked flag), `mine` (true: only those with the token's person as assignee or QA, like "My tasks" on screen), `watching` (true: only those they watch), `q` (free text: code, title, description and comments), `query` (advanced query, written in Portuguese, e.g. `projeto = MOB e status = Aberto e responsavel = eu`), `saved_filter` (the name of a saved filter), `created_from` and `created_to` (YYYY-MM-DD), `limit` (up to 50), `offset`, `sort` (`code`, `title`, `status`, `severity`, `assignee` or `updated`), `order` (`asc` or `desc`) and `done_last` (true: completed ones, group `done`, at the end, before the requested order). Without `sort`, most recent first. The API hides nothing by default: without a status filter, completed ones come too. The task list on screen hides completed ones until the person asks to "Show completed" (and then shows them at the end); to reproduce that, use `open_only=true` or `done_last=true`. In the MCP, the same `open_only` and `done_last` of `search_issues`. Each issue comes with `close_reason` (in Closed), `blocked` and `blocked_reason` (the blocked flag), `retest_failed` (failed the last retest and not approved yet), `assignee`, `qa_owner`, `sprint`, `release`, `releases` (every delivery as `"GOP 1.4.8-ota002"`, the main one first), `module`, `parent` and `epic` (codes), `labels`, `start_date`, `due_date`, `external_id` and `edit_version` (for `PATCH` and the batch, without opening each issue); `include=description,custom_fields` adds the description and custom fields.
- `GET /search?q=<text>` (tracker:read): "has this happened before?". Hybrid search, by meaning and by words, in issues (with their comments) and, with `kinds=issue,ticket`, in Help desk tickets (only for agents). `issue=<code>` instead of `q` looks for issues similar to that one, without it. `project`, `open_only` (true) and `limit` (up to 30, default 8). Each result has `kind`, `code`, `title`, `status_label`, `open`, `project`, `score` (0 to 1), `similarity` (from the vector, when found by meaning), `matched_by` (`text`, `meaning`), `likely_duplicate` and `url`; `mode` is `hybrid` or `text` (words only); `semantic` says whether search by meaning is on for the company: `on`, `indexed` (how many items are already in the index; it grows every minute while the index is being built) and, when off, `reason` (`no_vector`: search by meaning isn't available on this server; `no_key`: aqua off, the company's own key or no AI credits). With `on` true and `mode` `text`, search by meaning didn't answer in time or the person went over the limit of searches by meaning. With `issue`, the search starts from the context card aqua writes for each issue (`aqua_context` in `GET /issues/{ref}`). `explain` (default true; `false` returns only the search list, faster) makes aqua check each hit against the reference: it drops the ones that only share words and fills in `relation` (`same`: the same problem; `related`: related) and `reason` (the link in one sentence); `explained` says whether it checked (without aqua turned on, `false` and the search list). Before creating an issue, search here to avoid duplicates (see "Has this happened before?" above). In the MCP: `search_similar`.
- `GET /issues/{code or id}` (tracker:read): the issue with `status` (`key`, `label`, `category`, `close_reason` in Closed with `key` and `label`, and `retest_failed_at` when it failed the last retest), `blocked` (`since` and `reason`, or `null`), `releases` (every delivery, the main one first with `primary: true`), parent and `epic` (the parent, or the parent's parent for a subtask), children, sprint, points, estimate, dates, custom fields, labels, links, watchers, comments, history (`from`/`to` with the stored values and `text`, as on screen: names of releases, people and sprints; `from_label`/`to_label` when the screen shows from/to), `aqua_summary` (the last conversation summary aqua made on screen: `happened`, `pending`, `ball` with whose court the ball is in and `outdated` when there were comments or changes after it; `null` without a summary), `aqua_context` (the context card aqua writes on its own, in the background, for "Has this happened before?": `summary` with the problem in neutral words, `area`, `terms`, `generated_at` and `outdated` when the issue changed and the card is yet to be redone; `null` with aqua off or before the first card) and `edit_version` (for `PATCH`). `?fields=status,releases` returns only those parts (`id`, `code`, `url` and `edit_version` always): good for checking many issues without fetching comments and history.
- `GET /issues/{code or id}/comments` (tracker:read): every comment, each with `id`, `author`, `text`, `at`, `edited_at`, `deleted` (who deleted it and when; the text comes empty), `mentions` and the replies (`replies`).
- `POST /issues/{code or id}/comments` (tracker:write): comments, `{"text": "...", "parent_id": "<id of the comment being replied to>"}`. `@Nome` mentions someone in the project. `"attachments": [{"file_name": "report.md", "content_base64": "..."}, {"url": "https://..."}]` attaches up to 5 files to the comment (14 MB combined), with the attachment rules below; one bad attachment refuses the whole comment. 201 with the `id` and the `attachments`. In the `GET`, each comment carries its `attachments` (`id`, `name`, `type`, `size_bytes`, `download_url`).
- `GET /issues/{code or id}/evidences` (tracker:read): the issue's attachments (evidence), oldest to newest, each with `id`, `name`, `type`, `size_bytes`, `description`, `comment_id` (when it came in a comment) and `download_url`. `GET /evidences/{id}` (tracker:read) downloads the file, with the same token (accepts `Range`). Over the MCP, whose token doesn't work here, `get_attachment` reads the content: text (md, csv, txt, log, json) and images (png, jpg, webp, gif up to 5 MB); chat attachments too.
- `POST /issues/{code or id}/evidences` (tracker:write): attaches a file, three ways: `multipart/form-data` with the file in the `file` field (and `description` and `comment_id` optional, as text); JSON `{"file_name": "review.md", "content_base64": "...", "description": "..."}` (accepts the `data:...;base64,` prefix); or JSON `{"url": "https://example.com/screenshot.png"}`, which tracker downloads (https and public addresses only, up to 3 redirects and 20 s; the URL isn't stored; the optional `file_name` changes the name). Formats by the name's extension: png, jpg, jpeg, webp, gif, mp4, mov, pdf, json, txt, log, md, csv, zip; up to 15 MB per file and 60 files per hour per person. `comment_id` puts the file under one of your comments on the issue. PO/PM, QA, dev or admin (403 for others). 201 with `attachment` (as in the `GET`). There's no pre-signed URL: the bucket is private and every file goes through tracker, which checks the format and size. `DELETE /issues/{code or id}/evidences/{id}` removes it (the uploader or a project admin; the file of an approval version stays). In the MCP: `attach_file`, `remove_attachment` and `attachments` in `add_comment`; attachments show up in `get_issue` (`evidences`).
- `PATCH /issues/{code or id}/comments/{id}` (tracker:write): fixes the text, `{"text": "..."}`. Only the author (403 for others); the previous text is kept for the admin. `DELETE` on the same address deletes it: the comment stays in place, without the text (the author or a company admin).
- `GET /issues/{code or id}/history` (tracker:read): the full history, most recent first (`limit` up to 200, `offset`), with `total` and each event with `at`, `by`, `action`, `field`, `text` and, when present, `from` and `to` already with names.
- `POST /issues/{code or id}/move` (tracker:write): takes the issue to another project in the same organization, `{"project": "CHG", "confirm": true}`. Without `confirm`, it only returns the preview (`moved: false`, warnings about what has no equivalent in the target, subtasks that go along); with `confirm: true`, it moves and returns the new code (`code`) and the old one (`previous_code`), which still finds the issue. 409 if the issue changed in the meantime: read it again and repeat. Requires editing the issue and creating issues in the target.
- `POST /issues/{code or id}/links` (tracker:write): links to another issue, `{"target": "MOB-0007", "type": "blocks"}` (`blocks`, `relates_to`, the default, or `duplicates`). Requires editing the issue. 201 with the issue's links (`links`, each with `id`). `DELETE /issues/{ref}/links/{id}` removes a link.
- `GET /issues/{code or id}/proofs` (tracker:read): proof approval (Design). The versions sent, newest to oldest, with `status` (`pending`, `approved`, `changes_requested`, `superseded`), note, decision with comment and the file (`download_url`). `pending` is the id of the version awaiting a decision.
- `POST /issues/{code or id}/proofs` (tracker:write): sends the next version, as multipart/form-data with `file` and an optional `note` (same formats and size as evidence). The one awaiting a decision is superseded. PO/PM, QA, dev or admin. 201 with `version` and the versions.
- `POST /issues/{code or id}/proofs/{id}/decision` (tracker:write): `{"decision": "approve"}` or `{"decision": "changes", "feedback": "what to change"}`. PO/PM, QA, admin or the project's client (stakeholder). With the Design workflow, sending moves the issue to In approval, approving to Approved and requesting changes to Revisions. 422 when the version was already decided or superseded.
- `GET /issues/{code or id}/watchers` (tracker:read): who watches the issue (gets status and new comment notifications), with `watching` for the token's person. `POST` adds and `DELETE` removes (tracker:write): with nothing, the person themselves; `{"person": "ana@company.com"}` in the POST or `?person=...` in the DELETE for another person in the project, which requires editing the issue. The response has `person` (who was added or removed), `watching` and `watchers`. `GET /issues/{ref}` also returns `watchers`.
- `POST /issues/{code or id}/status` (tracker:write): moves the issue to another status, `{"status": "<key>"}`. The project's workflow and the person's role in it apply (422 when the transition isn't allowed). For `closed` (Closed, QA template), also send `close_reason`: `duplicated`, `cancelled`, `not_reproduced` or `wont_fix`; with the issue already in `closed`, the same request changes only the reason.
- `POST /issues/{code or id}/block` (tracker:write): flags the issue as blocked, `{"reason": "what's in the way"}` (optional; sending it again changes the reason). The flag works in any status and goes away on its own when the issue is completed. `DELETE` on the same address removes it. Every project role except stakeholder; a completed issue can't be blocked (422). Returns `blocked` (`since`, `reason`) or `null`. In the MCP: `block_issue`.
- `DELETE /issues/{code or id}` (tracker:write): deletes the issue (admin or the project's PO/PM; 403 for other roles). It disappears from lists, search and the API and can be restored for 30 days (on screen, in Issues → Deleted, or with `POST /issues/{ref}/restore`). An issue with issues inside it (epic, subtasks) isn't deleted (422).
- `POST /issues/{code or id}/restore` (tracker:write): restores an issue deleted in the last 30 days, with comments, evidence and history. Admin or the project's PO/PM. `GET /issues/deleted` (tracker:read, optional `project`) lists the ones the person can restore (the 200 most recent), with `restore_until`. After 30 days restoring returns 422.
- `POST /issues/{code or id}/duplicate` (tracker:write): creates a copy in the same project, like Duplicate on screen (content, classification, parent, points, fields and labels; no comments, evidence, links, people, sprint, release or technical notes). 201 with the copy's `code`. Requires creating issues in the project.
- `POST /issues` (tracker:write): creates an issue. 201 when it creates; 200 when one with the same `external_id` already existed in the project. `assignee` and `qa_owner` (`me`, id, e-mail or name of someone in the project) assign at creation: PO/PM and admins assign anyone, devs take it as assignee and QA as QA owner (403 otherwise; 422 when the person isn't in the project, without creating the issue). Devs create only subtask-level types, with the parent. With `?similar=true`, the created issue's response also has `similar`: the similar ones aqua confirmed (`code`, `title`, `status_label`, `open`, `relation`, `reason`, `likely_duplicate`, `url`), to link.
- `PATCH /issues/{code or id}` (tracker:write): edits the issue. Only the fields sent change; `null` clears the value. Accepts the text fields, `title`, `type`, `severity`, `priority`, `module`, `custom_fields` (only the keys sent change; a key that doesn't exist in the project is refused), `assignee` and `qa_owner` (`me`, id, e-mail or name of someone in the project), the technical notes (`root_cause`, `fix_notes`, `fixed_version`, `fixed_build`), `parent`, `story_points`, `sprint` (name or id of an open sprint; `null` goes back to the backlog), `release` (the main one: version or id; from another project in the company, `"PLT backend-1a2b3c"`, when the fix ships only there), `add_releases`/`remove_releases` (other deliveries of the same fix, including in another project: id, version in the issue's project, `"GOP 1.4.8-ota002"` or `{"project": "GOP", "version": "1.4.8-ota002"}`, as a list or a comma-separated text; requires linking releases in both projects) and `add_labels`/`remove_labels`. Each group requires the right role in the project (403 when it can't). The groups are saved in sequence: if one fails, the response has `applied` with the ones already saved. To avoid overwriting someone else's edit, send `edit_version` (the value read in `GET /issues/{ref}`): if someone changed the issue after that (fields, people, technical notes, parent, points, sprint, release, labels, estimate or dates; status, comments and hours don't count), nothing is saved and the response is 409 (`conflict`); read it again and reapply. The success response has the new `edit_version`.
- `GET /issues/{code or id}/time` (tracker:read): estimate, start, due date, recurrence (`recurrence` and `next_occurrence`), total logged, remaining and the time entries.
- `POST /issues/{code or id}/time` (tracker:write): logs hours worked, `{"duration": "1h30", "work_date": "YYYY-MM-DD", "comment": "..."}` (or `minutes` instead of `duration`; without `work_date`, today). PO/PM, QA, dev and admin roles.
- `PATCH /issues/{code or id}/plan` (tracker:write): `{"estimate": "8h", "start_date": "YYYY-MM-DD", "due_date": "YYYY-MM-DD", "recurrence": "monthly"}`. An omitted field stays as it is; `null` clears the value. `recurrence` (`weekly`, `monthly`, `quarterly`, `yearly`) makes the issue repeat: when it's completed, the next occurrence is born with start and due date moved forward, the same assignees and labels; `time.next_occurrence` points to it. Also works in `POST /issues` and in each item of `POST /issues/import`; `null`, `""` or `"none"` removes the recurrence.
- `GET /issues/{code or id}/code` (tracker:read): PRs (GitHub), MRs (GitLab) and commits linked to the issue, with `id`, repository, state (`open`, `draft`, `merged`, `closed`), branch, author and link.
- `POST /issues/{code or id}/code` (tracker:write): links a PR or commit by its link, `{"url": "https://github.com/owner/repo/pull/12", "title": "..."}` (GitLab MRs or commits too, including self-hosted). Doesn't change the status. 409 when it's already linked (commits by short or full sha). In a connected repository, the webhook later updates the title and state. PO/PM, QA, dev and admin roles.
- `DELETE /issues/{code or id}/code?link_id=<id>` (or `?url=<link>`, or the same in the JSON body) (tracker:write): unlinks. 404 when it wasn't linked.
- `GET /time` (tracker:read): time report. Query: `from` and `to` (YYYY-MM-DD; default, the current month), `project` (prefix, slug or id) and `person` (`me` or id); a filter that doesn't match is an error. Totals per person, per project and per issue, and the entries.
- `POST /issues/bulk` (tracker:write): the same action on up to 100 issues, `{"issues": ["MOB-0012", "MOB-0013"], "action": "status", "value": "done"}`. `action`: `status`, `assignee` (PO/PM or admin only; `me`, id, e-mail or name; `none` clears), `priority`, `severity`, `label`, `unlabel`, `release` (version or id; `none` clears), `watch`, `unwatch` and `delete` (the last three without `value`). Each issue follows the rule of the individual action; the response has `requested` (distinct issues requested), `updated` and `failed` (`code` and `error` for each one that didn't change). Without `value` on an action that needs one: 422.
- `POST /issues/batch` (tracker:write): a per-issue batch, up to 50, to close out a delivery in one call: `{"items": [{"issue": "MOB-0045", "fixed_version": "backend 4e8998ac", "fix_notes": "...", "code_links": ["https://github.com/org/repo/pull/251"], "status": "ready_for_qa"}]}`. Each item accepts the fields of `PATCH /issues/{ref}` (including `edit_version`), `code_links` (links, or `{url, title}`; an already-linked link isn't an error) and `status`, applied in that order with the rules of the individual calls. An item that fails doesn't stop the others; the response has `requested`, `succeeded`, `failed` and `results` (per item: `ok`, `code`, `updated`, `linked`, `status` and, if it failed, `failed_at` with `issue` (not found, or changed after the `edit_version`), `fields`, `code_links` or `status`, and `error`; earlier steps stay saved). Without other fields, `edit_version` protects the links and the status.
- `POST /issues/import` (tracker:write): up to 100 issues per request, `{"project": "MOB", "issues": [...]}` (each item can carry its own `project`). Item by item: a refused one doesn't undo the others. The response has `created`, `existing`, `failed` and `results` (`index`, `ok`, `code`, `url` or `error`).

Issue fields: `project` (prefix, slug or id), `title` (required), `type` (the type key), `severity` (S1 to S4, default S3), `priority` (urgent, high, medium, low; PO/PM and admins only, others get medium), `module` (name), `description`, `environment`, `app_version`, `build`, `device`, `operating_system`, `preconditions`, `steps_to_reproduce`, `actual_result`, `expected_result`, `impact`, `suggested_solution`, `parent` (code or id of the parent issue: the epic of a regular issue, or the issue of a subtask; the level comes from the type), `story_points` (a number from 0 to 1000), `start_date` and `due_date` (start and due date, YYYY-MM-DD; the start doesn't come after the due date), `custom_fields` (an object keyed by the field key), `labels` (up to 10 names; one that doesn't exist is created) and `external_id` (the id at the source, up to 200 characters).

Templates per type: `description`, `preconditions`, `steps_to_reproduce`, `actual_result`, `expected_result`, `impact` and `suggested_solution` when omitted come filled with the type's template (the `template` in `GET /projects`). Send `""` to leave one of those fields empty.

Use `external_id` in CI: with the name of the failed test, for example, running the pipeline again doesn't duplicate the issue.

### CI examples

Example (the token lives in a CI secret):

```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": "Login fails on Android 15", "severity": "S2", "external_id": "e2e/login.spec.ts", "labels": ["CI"]}]}'
```

Results of an automated suite in a cycle (the id comes from `POST /test-cycles` or `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": "Login timeout", "open_issue": true}]}'
```

### Chats

- Chats (`/chat`, one channel per project, like Slack): for the people who work on the project (admin, PO/PM, QA and dev; stakeholders don't see it, 403). `GET /chat` (tracker:read) lists the token's person's channels (the active projects): `unread` and `mentions` totals and `channels`, each with `project` (`id`, `slug`, `name`), `unread` (new messages in the main conversation), `mentions` (mentions and replies in their threads not yet read), `last_message_at` and `url`. `GET /projects/{prefix, slug or id}/chat` (tracker:read) returns `project`, `url`, `people` (who can be mentioned with @Name: `id`, `name` and `me`, the token's person; with `aqua` on in the company, aqua comes last, with `app` `aqua`; also returned with `?thread_id`), `unread` (the token's person's unread ones), `last_read_at`, `has_more` and `messages`, from the main conversation in the order they were written (without a cursor, the last 50; `?before=<id>` the earlier ones, `?after=<id>` the ones that arrived later, `?limit=` up to 200, `?mark_read=1` marks as read up to the last one returned). With `?thread_id=<id>` (the first message of a thread) it returns `thread` (that message) and `replies` (the replies, up to 500; `?after=<id>` only the new ones; `?mark_read=1` marks the thread's mentions and replies as read). Each message: `id`, `thread_id` (the thread it replies in, or `null`), `in_channel` and `thread_root` (`author`, `text`: the thread reply that also went to the main conversation and the start of the message it replies to), `author`, `app` (a message written by an app, with `key` and `name`; `null` for a person's), `author_id`, `author_avatar_url` (the photo, `null` without a photo), `text` (markdown; empty when deleted or attachment-only), `attachments` (the attached files, in order: `id`, `name`, `type`, `kind` (`image`, `video` or `file`), `size_bytes` and `download_url`; empty when deleted), `at`, `edited_at`, `deleted` (`by`, `at`), `mine` (written by the token's person), `can_edit` and `can_delete` (what they can do with the message), `mentions`, `mentions_me`, `issues` (the tasks cited by code that the person sees: `code`, `title`, `open`, `url`), `reactions` (`emoji`, `count`, `mine`, `people`), `thread` (in the main conversation: `replies`, `last_reply_at` and `people`, who replied; `null` without replies) and `url`. `POST /projects/{ref}/chat` (tracker:write) sends `{"text": "..."}` in the main conversation or `{"text": "...", "thread_id": "<id>"}` in that message's thread (with a reply's id, it goes into the same thread; `"also_in_channel": true` also puts the reply in the main conversation, like "Also send to the channel" on screen; `reply_to_id` is the old name; up to 10,000 characters; @Name mentions someone in the channel, and whoever is in the thread gets the reply, in the Inbox, the bell and on their device) and returns `message` (`id`, `thread_id`, `url` and `aqua_replying`: the message called @aqua and it will reply in the thread), 201. Attachments (photos, PDF, zip, spreadsheets, logs, videos: the task attachment formats, png, jpg, jpeg, webp, gif, mp4, mov, pdf, json, txt, log, md, csv, zip): in JSON, `"attachments": [{"file_name": "screenshot.png", "content_base64": "..."}]` or `[{"url": "https://..."}]`, as in comments; or `multipart/form-data` with `text`, `thread_id`, `also_in_channel` and one `files` field per file. Up to 5 per message, 15 MB each and 14 MB combined, and 60 files per hour per person; with an attachment, `text` can be left out. `GET /chat/attachments/{id}` (tracker:read) downloads the file, with the same token (accepts `Range`), for whoever reads the message's conversation. Deleting the message deletes its attachments. `POST /projects/{ref}/chat/{id}/reactions` (tracker:write) with `{"emoji": "👍"}` adds the token's person's reaction and `DELETE` with the same body removes it (each person, each emoji once; up to 20 different emojis per message); returns `id`, `emoji` and `reacted`. `PATCH /projects/{ref}/chat/{id}` (tracker:write, the author only) fixes it with `{"text": "..."}`; `DELETE /projects/{ref}/chat/{id}` (tracker:write, the author or an admin) deletes it (it stays in place as deleted; the previous text goes to the company history). `POST /projects/{ref}/chat/read` (tracker:read) with `{"message_id": "<id>"}` (or `{}`, up to now) marks the main conversation as read and returns `unread`. `GET /projects/{ref}/chat/issues?q=` (tracker:read) returns `issues`, the tasks to cite in a message, like the list that opens when typing `#` in the field on screen (up to 8; only the ones the token's person sees): without `q`, by context, first the ones cited last in the conversation (`reason` `cited`), then the open ones assigned to the token's person (`mine`) and the project's open ones touched last (`project`); with the start of a code (`MOB-` or `MOB-12`, which finds MOB-0012), it completes by code, the project's first; with words, the text and meaning search of `/search` (`reason` `null`). Each one: `id`, `code`, `title`, `open`, `project` (the name), `reason` and `url`; a `code` written in the `text` becomes a link and a card. Archived project: read-only (422 when writing). In the MCP: `list_chat_channels`, `list_chat_messages`, `send_chat_message`, `react_to_chat_message`, `edit_chat_message`, `delete_chat_message` and `suggest_chat_issues`.
- Direct messages (`/chat/direct/<person id>`): the private conversation between the token's person and another team member in the same company (anyone who works on one of its projects, except stakeholders, plus admins and owners). Only the two of them read it; not even the admin sees it. `GET /chat` also returns `directs` (the conversations with messages, most recent first: `id`, `person` (`id`, `name`), `unread`, `last_message_at`, `url`) and `people` (who they can start one with: `id`, `name`, `url`); the top-level `unread` adds up channels and direct conversations. `GET /chat/direct/{person}` (tracker:read; the person by id or e-mail; opens the conversation the first time) returns `person`, `url`, `people` (empty: there are no @mentions), `unread`, `last_read_at`, `has_more` and `messages`, with the same parameters and fields as the project conversation, without threads. `POST /chat/direct/{person}` (tracker:write) sends `{"text": "..."}`, with attachments as in the channel (the other person gets a device notification; `thread_id` gives 422); `PATCH`/`DELETE /chat/direct/{person}/{id}` fixes and deletes (the author only; outside the company history), `POST`/`DELETE /chat/direct/{person}/{id}/reactions` reacts, `POST /chat/direct/{person}/read` marks as read and `GET /chat/direct/{person}/issues?q=` suggests tasks to cite (without a channel project: the ones cited in the conversation and the open ones assigned to the token's person). A person outside the team: 404; someone who left the team later leaves the conversation read-only (422 when writing). In the MCP, the same conversation tools with `person` instead of `project`.
- Chats apps: whoever writes in the channel without being a person (the message comes with `app` and `author_id` `null`; nobody edits it, the admin deletes it). The text apps write and their catalog (`name`, `summary`, `description` and the permission descriptions) come in the reader's language: the one chosen in Your account or, on automatic, the one in `Accept-Language`; what people write (names, subjects, aqua's answer) stays as written. Each app has a manifest: name, what it asks for (`permissions`: required ones, always granted, and optional ones, which the admin picks when installing), the events it listens to and what each channel chooses. The permissions: `chat:write` (write in the conversation), `issues:read` (see the project's tasks), `comments:read` (see comments, for the new comment notice), `meetings:create` (create Google Meet meetings) `chat:direct` (work in direct conversations) and `ai:use` (reply with aqua, spending the company's AI credits). The official ones: `task_updates` (Task updates: posts in the channel when a project task is created, changes status, changes assignee or gets a comment (the last one only with `comments:read`, optional), with its card; each channel picks the events), `google_meet` and `aqua` (see below) and `automations` (Automations: comes with tracker and writes when an automation rule has the "Send a channel message" action). The company installs (admins and owners only) and the project's PO/PM or an admin turns it on in each channel, or the admin turns it on in every channel at once, including channels created later. In a direct conversation, only apps with `chat:direct` granted, when one of the two people turns it on; either one turns it off, and the app tells the conversation who turned it on or off. `GET /apps` (tracker:read) returns `apps`, the company catalog: `key`, `name`, `summary`, `description`, `official`, `built_in`, `company_wide` (on across the whole company: no install and no per-channel switch; `installed` is false when an admin turned it off), `permissions` (`key`, `description`, `required`: mandatory, and `granted`: the company granted it), `events`, `installed`, `installed_at`, `channels` (how many channels it's on in) and `all_channels` (on in every channel, including new ones). `POST /apps/{key}` (tracker:write) installs, with `{"permissions": ["chat:write", "meetings:create", "chat:direct"]}` (the required ones always go in; without a body, only those); `PATCH /apps/{key}` with `{"permissions": [...]}` changes what's granted (removing `chat:direct` turns the app off in every direct conversation); `{"all_channels": true}`, in the `POST` or the `PATCH`, turns the app on in every channel, including new ones (then the channel can't turn it off, only pick the updates), and `false` goes back to the channels that turned it on one by one (not for personal apps); `DELETE /apps/{key}` uninstalls (turns it off in every channel and direct conversation; the messages stay). They return `key`, `installed` and `permissions` (the granted ones); a permission the app doesn't ask for gives 422. `GET /projects/{ref}/chat/apps` (tracker:read, conversation members) returns `project`, `can_manage` (can turn on and off), `can_install` and `apps` (`key`, `name`, `summary`, `built_in`, `company_wide`, `installed`, `enabled`, `all_channels` and, for `task_updates`, `events` and `allowed_events`, the ones the permissions allow choosing). `PATCH /projects/{ref}/chat/apps/{key}` (tracker:write, PO/PM or admin) with `{"enabled": true, "events": ["issue.created", "issue.status_changed", "issue.assigned", "comment.created"]}` turns it on and picks the notices (without `events`, it keeps what it had or the default: task created and status changed) and `{"enabled": false}` turns it off (with `all_channels`, it gives 422); an app that isn't installed gives 422. In a direct conversation: `GET /chat/direct/{person}/apps` (tracker:read; the person by id or e-mail) returns `person`, `active`, `can_install` and `apps` (`key`, `name`, `summary`, `installed`, `allowed`: allowed in direct conversations, `enabled` and `enabled_by`: who turned it on); `PATCH /chat/direct/{person}/apps/{key}` (tracker:write, either of the two people) with `{"enabled": true}` turns it on and `{"enabled": false}` turns it off. In the MCP: `list_chat_apps` (with `project`, the channel's; with `person`, the direct conversation's), `install_chat_app` (with `permissions` and `all_channels`) and `set_chat_app` (with `project` or `person`).
- Google Meet (`google_meet`, official app): creates the meeting in the channel, in the caller's Google account, and posts the card with the join link. Each person connects their own Google account once, through the browser (Google's screen doesn't open through the API). In the channel, the app comes with `account` (`configured`, `connected` and `email`, the token's person's account). `GET /apps/google_meet/account` (tracker:read) returns `configured`, `connected`, `email` and `connect_url` (open it in the browser, signed in to tracker); `DELETE /apps/google_meet/account` (tracker:write) disconnects and revokes access at Google. `POST /projects/{ref}/chat/meet` (tracker:write, conversation members) with `{"title": "Sprint planning"}` (optional) and `"thread_id"` (optional: creates it inside that message's thread) creates the room and returns `project`, `message` (`id`, `thread_id`, `url`) and `meeting` (`url` to join, `code`, `title`, `starts_at` and `ends_at`), 201. To schedule, also send `"starts_at"` (ISO 8601 with the offset, e.g. `2026-10-22T14:00:00-03:00`), `"duration_minutes"` (15 to 480, default 30), `"time_zone"` (IANA, default `America/Sao_Paulo`) and `"guests"` (up to 50 e-mails or ids of people in the conversation): tracker creates the event in the caller's Google Calendar, with the Meet room, Google sends the invite to the guests and `meeting` also has `guests` (how many were invited). Scheduling requires the Google account to have allowed calendar access; whoever connected before that gets 422 with the link to connect again. `GET /apps/google_meet/meetings` (tracker:read) returns `meetings`, the meetings with Meet in the next 14 days of the token's person's calendar: `title`, `url`, `code`, `starts_at`, `ends_at` and `calendar_url`. In a direct conversation, `POST /chat/direct/{person}/meet` with `{"title": "..."}` (no thread), with the app on in the conversation. App off in the channel or conversation, or without `chat:direct`: 403; without the account connected: 422, with the link to connect. The message has `card`: `type` `meeting`, `provider` `google_meet`, `url`, `code`, `title`, `started_by` (the caller's name) and, when scheduled, `starts_at`, `ends_at` and `event_id` (the event in the scheduler's Google Calendar), plus `meeting_responses` (when scheduled: `yes`, `maybe`, `no`, how many replied through tracker, and `mine`, the token person's reply; `null` otherwise). On screen, the camera button next to the field (Start now, Schedule meeting… and Upcoming meetings), `/meet subject` or `/meet lista`. In the MCP: `start_meeting` (with `project` or `person`; with `starts_at`, it schedules) and `list_meetings`.
- Google Calendar (`google_calendar`, official app): personal. The company installs it and each person uses it with their own Google account (the same Google Meet connection, with calendar access), with no per-channel setting: in the catalog, `channels` is `null`. Permissions: `calendar:read` (required: read the calendar of whoever uses it) and `calendar:respond` (optional: reply to invitations through tracker). `GET /apps/google_calendar/agenda` (tracker:read), with `?date=YYYY-MM-DD` or `?days=1` (from today; default today) and `?time_zone=` (default `America/Sao_Paulo`), returns `date`, `time_zone`, `can_respond`, `reminder_minutes` and `events`: `id`, `title`, `starts_at`, `ends_at`, `all_day`, `meeting_url` (the Meet room), `calendar_url` and `response` (`yes`, `maybe`, `no`; `pending` with no reply; `null` when the person organized it or isn't invited). `GET /apps/google_calendar/settings` (tracker:read) returns `installed`, `can_respond`, `account` (`configured`, `connected`, `email` and `calendar`), `connect_url` and `reminder_minutes`; `PATCH` (tracker:write) with `{"reminder_minutes": 15}` changes the reminder before each meeting (5, 10, 15 or 30; 0 turns it off). The reminder arrives in the bell and on the phone (notification type `meeting`, "Meetings", in the preferences; no email) and opens the Meet room. Replying to the invitation (with `calendar:respond`): `POST /projects/{ref}/chat/{id}/response` or `POST /chat/direct/{pessoa}/{id}/response` with `{"response": "yes"}` (`yes`, `maybe`, `no`) on the card of a meeting scheduled through tracker returns `responses`; `POST /apps/google_calendar/events/{id}/response` replies to an agenda event. The reply goes to the Google Calendar of whoever answers and Google notifies the organizer; the organizer doesn't reply (422). Without the app: 403; without the permission to reply: 403; without the account connected with calendar access: 422, with the link to connect. On screen, `/agenda` (or `/agenda amanhã`) and the agenda button next to the field show, only to whoever asked, the day's agenda, with Join, Yes/Maybe/No and the reminder; the scheduled meeting card gets Yes, Maybe and No and the count. In the MCP: `get_agenda`, `respond_to_meeting` (with `message` and `project` or `person`, or with `event_id`) and `set_meeting_reminder`.
- aqua (`aqua`, official app, `company_wide`): on in every channel of the company, with no install and no per-channel switch (`PATCH /projects/{ref}/chat/apps/aqua` gives 422). An admin turns it off for the company with `DELETE /apps/aqua` and back on with `POST /apps/aqua`; it also needs aqua (the AI) on in the company. When on, a message with @aqua (in the main conversation or a thread, on screen, through the API or the MCP) gets aqua's reply in that message's thread. aqua immediately writes "Consultando o projeto…" (an app message, with `app` `aqua`) and replaces the text with the answer when it's done, without marking it as edited: whoever reads through the API sees the change on the next read of the thread (`GET /projects/{ref}/chat?thread_id=<id>`, with the id of the calling message, or its `thread_id`). It looks things up as the caller, with that person's role in the project; since the answer is for the whole channel, only the channel's project (tasks, sprints, releases, tests, hours, people and the conversation) and the company's documents count: other projects, the Inbox and direct messages stay out. When asked to create or change something (create a task, edit, change status, comment, block, watch, plan, log time, link), aqua proposes and nothing is saved right away: her reply comes with `aqua_actions` (`asked_by`, the caller and the only one who decides; `mine`, whether it's the token's person; and `actions`, each with `id`, `tool`, `title`, `details`, `state` — `pending`, `running`, `done`, `cancelled` or `failed` —, `replaced`, `result` and `error`; `null` on other messages), and the screen shows a card with Confirm and Cancel. `POST /projects/{ref}/chat/{id}/aqua-actions/{action}` (tracker:write, only the person who called aqua; 403 for others) with `{"decision": "confirm"}` makes the change on that person's behalf, with their role, and returns `{"action": ...}` (`"cancel"` discards it); the same action never runs twice (422). Actions also stay in the channel's project: the new task goes there and only its tasks can be changed. In a thread, aqua rereads the conversation (what she wrote, the replies and what was decided on each card): called again with the decisions, she merges everything and proposes again, and her proposals still waiting on the same person in that thread stop counting (`replaced` `true`, `state` `cancelled`). In the MCP: `decide_aqua_chat_action`. Each answer counts as one AI use for the caller, with the company's credits; without credits, the answer says when they renew. The caller gets the reply notification (the Comments preference). The calling message's attachments go along with the question: aqua sees screenshots and PDFs and reads text files; for other messages she only sees the file names. The app asks for `chat:write`, `issues:read` and `ai:use`.

### aqua and Inbox

- `GET /aqua` (tracker:read): whether aqua (the AI assistant) is on for the company, `{"available": true, "reason": null, "can_review": true}` (`reason`: `disabled` or `no_key`; `can_review`: the person edits issues in some project and sees the aqua screen, `GET /aqua/recommendations`). `POST /aqua` (tracker:read): the app's help conversation, `{"messages": [{"role": "user", "content": "How do I create an issue?"}], "path": "/issues", "app": "ios"}` (up to 20 messages, starting and ending with `user` and alternating; `app`: `ios` or `mac`, optional). aqua answers usage questions and also questions about the data: it looks up, read-only and as the token's person, what that person sees in the company (their tasks and the ones they watch, projects, sprints, releases, tests, people, hours, inbox, Chats, documents, ideas and status), with the same read queries as the MCP; it doesn't change anything. It also opens task and comment attachments when the question calls for it: it sees screenshots (png, jpg and webp up to 3.7 MB) and PDFs (up to 10 MB) and reads text files (md, csv, txt, log and json), up to 4 images or PDFs per question; no video. aqua also proposes changes when the person asks (create a task, edit or assign, comment, change status, block, watch, plan, log hours, link tasks), but nothing is saved by the question: each proposal comes back in `actions` for the person to confirm. The links in the answer are app addresses (`/issues/MOB-0012?org=...`). When the question is about a task or a project, send the open screen in `path` (`/issues/MOB-0012`, `/projects/mobilidade`): aqua understands "this task". A question that looks up data takes a little longer and counts as one AI use. aqua answers in the token's person's language (the `locale` in `GET /me`; on Automatic, the request's `Accept-Language`), and the actions' `title`, `details` and `result` and the `status` texts come in the same language. Every conversation is saved: send `conversation_id` (returned in the first answer) to continue the same one; without it, a new conversation (or the recent one with exactly the same messages). Returns `{"answer": "...", "conversation_id": "...", "actions": [...]}`, each action with `id`, `tool` (the MCP tool it runs), `input`, `title` (the card's sentence, written by tracker), `details`, `state` (`pending`, `running`, `done`, `cancelled` or `failed`), `result` (`text` and `url`, once done) and `error`. With `"stream": true`, the answer arrives bit by bit as NDJSON (`application/x-ndjson`, one JSON per line): `{"type": "status", "text": "Checking the tasks…"}` while it looks things up (text received before a `status` no longer counts), `{"type": "text", "text": "..."}` with each piece of the answer, `{"type": "ping"}` to keep the connection alive and, at the end, `{"type": "done", ...}` with the same JSON as without streaming (the answer that counts) or `{"type": "error", "message": "..."}`; the HTTP status is 200 even on error. `POST /aqua/conversations/{id}/actions/{action}` (tracker:write) with `{"decision": "confirm"}` makes the change with the token's person's role (or `"cancel"` discards it) and returns `{"action": ...}`; the same action doesn't run twice (422 when it was already decided). `GET /aqua/conversations` (tracker:read, `?limit=` up to 50) lists the person's conversations (`id`, `title`, `messages`, `created_at`, `updated_at`; the 50 most recent are kept), `GET /aqua/conversations/{id}` returns the messages (`role`, `content`, `at`, `actions`) and `DELETE /aqua/conversations/{id}` (tracker:write) deletes it. In the MCP: `list_aqua_conversations` and `get_aqua_conversation`. 422 when the AI refuses or is off, without a key or at the limit.
- `GET /aqua/credits` (tracker:read): the company's AI credits. Without its own key, aqua uses tracker's key and spends credits from a package per 30-day cycle (the one in the company's plan), which renews on its own; with the company's own key, it doesn't spend any. Returns `available`, `reason`, `key_source` (`server`: credits; `organization`: own key; `null` without AI), `credits` (`null` with the own key): `state` (`ok`, `low` with 20% or less, `empty` when it ran out and aqua pauses until it renews, `expired`), `remaining`, `used`, `total` (`per_cycle` plus `granted`, the credits added by the platform team in the cycle), `cycle_days`, `cycle_start`, `cycle_end`, `auto_renew` and `renews_at`; and `usage` (admins and owners only, `null` for others): `since` (the start of the cycle; with the own key, 30 days ago) and `by_action`, from the most used actions to the least (`action`, `label`, `calls`, `failures`, `credits`). Always in credits, never in money. When an AI call returns 422 for lack of credits, the message says when it renews. In the MCP: `get_ai_credits`. The company's own key (Claude only) is set, changed and removed only on screen, in Company → Integrations → AI assistant (admins and owners): neither the API nor the MCP reads or writes the key.
- Inbox (the one on the /inbox screen): `GET /inbox` (tracker:read, `?view=unread` only the unread ones or `?view=hidden` the hidden ones; without it, the ones not hidden; `?before=<id>` pages, `?limit=` up to 100) returns `unread` and `items`, the comments that reached the token's person, most recent first: `kind` (`mention`: someone wrote @their name; `reply`: a reply in a conversation they wrote in), `own_thread` (the conversation started with a comment of theirs), `id` (of the comment), `text`, `author`, `excerpt`, `issue` (`id`, `code`, `title`, `project`), `url`, `at`, `read` and `hidden`. Writing on the task after the comment, opening the task or reading its notification also count as read. Mentions and replies in Chats threads come separately, in `chat_items` (with `chat_unread`): `id` (of the notification), `kind` (`mention` or `reply`, a reply in a thread they're in), `own_thread` (the thread started with a message of theirs), `thread` (it came from a thread), `text`, `author`, `excerpt` (the snippet of the message), `project`, `url` (straight to the message), `at`, `read` and `hidden`; opening the thread or reading the channel counts as read. `POST /inbox` (tracker:read) changes the state (of ids from `items` and `chat_items`): `{"action": "read", "ids": [...]}` or `{"action": "read", "all": true}`, `{"action": "hide", "ids": [...]}` or `{"action": "hide", "older_than_days": 30}` (hiding counts as read) and `{"action": "unhide", "ids": [...]}`; returns `updated`, `unread` and `chat_unread`. aqua's summary of the day and the month (the one at the top of the inbox): `GET /aqua/summary` (tracker:read, `?period=day`, the default, or `month`, in the Brasília time zone) returns `available`, `reason`, `period` and `summary` (`null` before asking): `headline`, `highlights` (what happened in the person's tasks and the ones they watch), `attention` (what needs their attention), `issues` (the ones cited, with `code`, `id` and `url`), `generated_at` and `outdated` (news arrived after it). `POST /aqua/summary` (tracker:read) with `{"period": "month"}` asks for a new one, written in the token's person's language (it spends AI credits; a period with nothing doesn't); 422 with the AI off, without a key or without credits. In the MCP: `list_inbox`, `update_inbox` and `get_aqua_summary`.
- aqua on issues (the same as on screen): `GET /issues/{ref}/aqua` (tracker:read) returns `available`, `can_review` (whether the person can ask for the review), `summary` (like `aqua_summary` in `GET /issues/{ref}`), `review` (`summary`, `reviewed_at`, `outdated`), the open `recommendations` (`id`, `kind`: `comment`, `status`, `field`, `custom_field`, `label`, `duplicate` or `move`; `title`, `before`, `after`, `reason` and `comment`, the text of the request for information) and `snooze_options`. `POST /issues/{ref}/aqua/summary` (tracker:read) summarizes the conversation (from `summary_min_comments` comments on) and returns `{"summary": ...}`. `POST /issues/{ref}/aqua/review` (tracker:write, whoever edits the issue) reviews it and returns the same as the `GET`. The review (this one, the batch one and the daily one) also reads the attachments of the issue and its comments: up to 3 screenshots, 1 PDF (up to 5 MB) and the text of up to 2 text or log files, newest first; no video. The cost of reading goes into the credits like the rest of the review. `POST /aqua/recommendations/{id}/apply` (tracker:write) applies the suggestion (an optional `{"comment": "..."}` changes the text of the request for information); `POST /aqua/recommendations/{id}/dismiss` (tracker:write) with `{"for": "7"}` (`1`, `7`, `30` days or `forever`) ignores it. The whole aqua screen: `GET /aqua/recommendations` (tracker:read, optional `?project=MOB`) returns `available`, `reason`, `can_review`, `daily_review` (`enabled`, `hour`, `limit`, `last_on`, `last_count`, `last_error`), `sentry_waiting` (Sentry errors awaiting a decision, in `GET /sentry/errors?waiting=true`), `total` and `issues`, the issues with open suggestions (from the ones stalled longest to the recent ones), each with `id`, `code`, `title`, `project`, `status` (`name`, `color`, `category`), `summary`, `reviewed_at`, `outdated` and `recommendations` (as above), and `snooze_options`. "Review open issues" runs on the server: `POST /aqua/review-runs` (tracker:write, optional `{"project": "MOB"}`) starts the review of every open issue aqua hasn't seen yet or that changed since the review (up to 500, one at a time; one review at a time per company, and asking again returns the one already running) and returns `{"run": ...}` (`null` with nothing pending); `GET /aqua/review-runs` (tracker:read) returns the one running or the last one, `GET /aqua/review-runs/{id}` the progress (`status`: `running`, `done`, `stopped` or `failed`; `total`, `reviewed`, `failed`, `suggestions`, `error`, `created_at`, `finished_at` and `issues`, each with `id`, `code`, `title`, `state` — `waiting`, `running`, `done` or `failed` —, `suggestions` and `error`) and `POST /aqua/review-runs/{id}/stop` (tracker:write) stops it. `GET /aqua/review-queue` (tracker:read, `?project=` and `?limit=` up to 25) still returns the queue (`id`, `code`, `title`) to review one by one with `POST /issues/{ref}/aqua/review` and `{"batch": true}` (the batch review model). In the MCP: `list_aqua_recommendations`, `start_aqua_review`, `get_aqua_review`, `stop_aqua_review`, `list_issues_to_review`, `review_issue_with_aqua`, `apply_aqua_recommendation` and `dismiss_aqua_recommendation`. `POST /aqua/draft` (tracker:read) with `{"text": "free-form report", "project": "MOB"}` (`project` optional) returns the fields of `POST /issues` filled in and `missing` (what the report doesn't say), in the report's language, without creating anything. `POST /aqua/improve` (tracker:read) with `{"project": "MOB", "type": "bug", "title": "...", "description": "..."}` (and the form's other texts) returns a rewritten title and texts and `notes`, without saving. 422 when the AI is off, without a key or at the limit.

### Plan, currency and billing

- Plan and currency (the Company → Plan screen; admins and owners only, 403 for others). Nothing is charged during the beta. Each company has a currency (`BRL`, `USD`, `EUR`, `GBP`, `MXN`, `ARS`, `CLP` or `COP`), set at creation from the country it was created in (without one, BRL), and each plan has a price defined in each currency, monthly and yearly, with no conversion. Amounts come in `amount_minor`, an integer in the currency's smallest unit (4990 is R$ 49.90; the Chilean peso has no decimals), with `amount` and `formatted` (as the screen shows it) next to it.
- `GET /billing` (tracker:read): `currency`, `currency_locked` (with billing on, only the Aquarela team changes the currency), `currencies`, `plan` (`key`, `name`, `description`, `ai_credits_per_cycle`, `null` for the server default, `storage_gb`, file storage in GB, `null` for the default, `features`, what the plan includes, and `prices` in the company's currency), `plan_source` (`subscription`, or `default` when the default plan applies), `price` (the current one), `subscription` (`status`: `trialing`, `active`, `past_due` or `canceled`; `interval`: `month` or `year`; `current_period_start`, `current_period_end`, `trial_ends_at` and `billing_provider`, `null` while there's no billing), `plans` (the ones the company can have, with the price in its currency), `billing_details` and `plan_request` (the paid plan request waiting for the Aquarela team: `plan`, `interval`, `requested_by` and `created_at`; `null` without a request).
- `PATCH /billing` (tracker:write), in JSON: `{"currency": "USD"}` changes the currency; `{"billing_details": {...}}` changes the billing details, only the fields sent (`null` clears): `kind` (`company` or `individual`), `legal_name`, `email`, `country` (two-letter ISO), `address_line1`, `address_line2`, `city`, `state`, `postal_code`, `tax_id_type` and `tax_id`. The tax id follows the country, with Stripe's types: `br_cnpj` (company) and `br_cpf` (individual), with the check digits verified, `us_ein`, `eu_vat`, `gb_vat`, `mx_rfc`, `ar_cuit`, `cl_tin` and `co_nit`; without `tax_id_type`, the country's applies. Returns the same as the GET; a refused rule returns 422.
- `POST /billing/plan` (tracker:write), in JSON: `{"plan": "pro", "interval": "year"}` (`plan` is the `key` or `id` of one of the `plans`; `interval`, `month` by default). A free plan in the company's currency changes immediately (`outcome`: `changed`); a paid plan, or one without a price in the currency, becomes a request to the Aquarela team (`requested`), who arranges payment and puts the company on the plan; the current one returns `unchanged`. Asking for another plan replaces the request. Returns `outcome`, `plan`, `interval` and `billing` (the same as the GET). `DELETE /billing/plan-request` (tracker:write) cancels the request (`{"cancelled": true}`; `false` without a request). In the MCP: `get_billing`, `update_billing`, `change_plan` and `cancel_plan_request`.
- File storage: each plan gives the company space for attachments (`storage_gb`; the default is 10 GB), adding up the attachments on tasks (including the ones on comments and the approval versions), the help desk and Chats, direct messages too. Profile photos, logos and project icons don't count. When the storage runs out, sending a new attachment (screen, API and MCP) returns 422 with the limit message; files a customer sends to the help desk still go in, so no ticket is lost. `GET /billing` brings `storage`: `used_bytes`, `limit_bytes`, `plan_storage_gb` (`null` on the default), `state` (`ok`, `warning` at 90% or more, `full`) and the total per place in `tasks`, `tickets`, `chat` and `direct_messages` (`bytes` and `files`).
- Company files (the Company → Files screen; admins and owners only, 403 for others): `GET /files` (tracker:read) returns `storage` (as in `GET /billing`), `total`, `has_more` and `files`, newest first (`?sort=size`, largest first), each with `id`, `origin` (`task`, `ticket` or `chat`), `name`, `type`, `size_bytes`, `created_at`, `uploaded_by`, `project` (`id`, `slug`, `name`), `issue` (`id`, `code`, `title`, `comment_id`), `ticket` (`id`, `number`, `subject`) or `chat_message` (`id`, `thread_id`), depending on the origin, `url` (where it is in tracker), `download_url`, `can_delete` and `locked` (`proof`: a version sent for approval; `trash`: a task or project in the trash; `null` when it can be deleted). Filters: `?origin=`, `?query=` (part of the name), `?limit=` up to 100 and `?offset=`. Files in direct messages count toward the storage but aren't listed: only the two people in the conversation can see them. `GET /files/{id}` (tracker:read) downloads the file; `DELETE /files/{id}` (tracker:write) deletes the file from where it is and frees the space (`{"deleted": true, "origin": ..., "name": ...}`; a locked file returns 422). In the MCP: `list_files` and `delete_file`.

### Releases and tests

- `GET /releases` (tracker:read): releases with status, `kind` (`version` or `ota`), `stage` (`current` is the one that's live, one per track: the project's version and OTA each have their own; `previous`, `approved`, `testing`, `development`, `planned`, `cancelled`), `stage_label` (the name on screen), `is_next` (the next to go live), dates, open issues and the last Release Gate decision. Query: `project` and `status` (comma-separated list).
- `GET /releases/{id}` (tracker:read): the release with `kind`, `stage`, `stage_label`, `is_next`, the screen's summary (`summary`: `headline`, `context` and `next_step`), the metrics and the gate suggestion (`gate.applies`; on an OTA without a BLOCKED recorded, `gate.suggestion.decision` is `not_applicable` and instead of the metrics comes `gate.validation`, the issue count: `ready_for_qa`, `testing`, `approved`, `failed`), the last decision, the issues, the test cycles and the release notes.
- `POST /releases` (tracker:write): creates a release, `{"project": "MOB", "version": "2.7.0", "build": "...", "environment": "...", "planned_date": "YYYY-MM-DD", "notes": "...", "kind": "version"}`; `kind` is `version` (the default) or `ota`. A release that already went live (an OTA already sent, a backend deploy, a version published outside tracker) can be born in production, without the gate: `"status": "production"` and `released_date` (default: today). PO/PM or admin. The version is unique in the project: repeating the request returns the release that already exists (200, `already_existed: true`), without changing anything in it. For CI to record a delivery: `POST /releases`, then `POST /issues/bulk` with `"action": "release"` and the issues, and `POST /releases/{id}/status`.
- `POST /releases/{id}/status` (tracker:write): `{"status": "in_development"}`. The path is planned → in_development → staging (testing), and from approved to production; cancelled leaves the path. An OTA (`kind: "ota"`) goes to `production` directly, from planned, in_development, staging or approved, without the gate decision, unless the last decision is BLOCKED; validation follows afterwards through the linked issues (the `summary.next_step` of `GET /releases/{id}` says how many are in Ready for QA, testing, approved and failed). Other releases reach approved through the gate's APPROVED decision, recorded by a person on screen, and production requires that decision. Outside that path (approved directly, included) only an admin, with `override_note`.
- `POST /releases/{id}/move` (tracker:write): `{"to_project": "PLT"}` moves the release to another project in the company (e.g. backend deploys born in the app go to the platform project). It takes history, gate decisions and notes along; the issues stay linked, even those from other projects. PO/PM or admin in both projects; the version can't exist in the target and a release with a test cycle doesn't move. MCP: `move_release`.
- `PUT /releases/{id}/notes` (tracker:write): saves the release notes, the customer-facing text the screen copies and sends by e-mail: `{"release_notes": "# App 2.7.0\n\n## What's new\n\n- You can now..."}`. Simple markdown (`# ` title, `## ` section, `- ` item; no bold or links). Replaces the whole text; empty or `null` goes back to the draft generated from the resolved issues. PO/PM or admin. The response has `release_notes` and `release_notes_saved`. Doesn't send the e-mail.
- `GET /test-cases` (tracker:read): active test cases. Query: `project`, `q` (part of the code or title) and `include_archived`.
- `POST /test-cases` (tracker:write): creates a case, `{"project": "MOB", "title": "...", "module": "...", "priority": "high", "steps": [{"action": "...", "expected": "..."}]}`. QA or admin. The code (TC-MOB-0001) is generated.
- `GET /test-cycles` (tracker:read): most recent cycles with their progress. Query: `project` and `status`.
- `GET /test-cycles/{id}` (tracker:read): the cycle with each case and its result (`pass`, `fail`, `blocked`, `in_progress`, `not_started`), the comment, who tested and the issue opened from the fail.
- `POST /test-cycles` (tracker:write): creates a cycle, `{"project": "MOB", "name": "2.7.0 regression", "release": "2.7.0", "environment": "...", "cases": ["TC-MOB-0001"]}`. Without `cases`, all the project's active cases go in. QA or admin.
- `POST /test-cycles/{id}/results` (tracker:write): CI results, up to 200 per request, `{"results": [{"test_case": "TC-MOB-0001", "status": "fail", "comment": "...", "open_issue": true}]}`. `status`: `pass`, `fail`, `blocked` or `in_progress`; a comment is required for fail and blocked. With `open_issue`, the fail opens the issue already filled in with the case (or returns the one that already exists); if the issue doesn't open, the result stays saved and the item comes back with `warning`. Item by item, with `recorded`, `failed` and `results`. QA or admin.
- `POST /test-cycles/{id}/status` (tracker:write): `{"action": "start" | "finish" | "cancel" | "reopen"}`. A closed cycle doesn't accept results until it's reopened.

### Documents

- `GET /docs` (tracker:read): knowledge base documents the person can read, without the text (title, `slug`, summary, project, visibility). Query: `project` (prefix, slug or id, or `empresa` for the ones for the whole company).
- `GET /docs/search?q=<question>` (tracker:read): RAG search in the published documents. Returns the most relevant passages (up to 20, default 5; `limit`), each with the document, the section and the text. `project` narrows it to a project (the whole-company ones come along).
- `GET /docs/{slug or id}` (tracker:read): the document with its markdown text (`body`). With `?format=markdown` (or `Accept: text/markdown`), only the markdown.
- `POST /docs` (tracker:write): creates a document, or updates the one with the same `slug` (201 when it creates, 200 when it updates). `{"title": "...", "body": "<markdown>", "summary": "...", "project": "MOB", "slug": "...", "visibility": "draft|internal|public"}`. Without `project`, the document belongs to the whole company (admins only). Good for publishing a repository's documentation from CI.

### Sentry

- `GET /sentry/errors` (tracker:read): errors Sentry sent (`?project=MOB` for one project, with the integration and the last notice; `?waiting=true` only the ones awaiting a decision, as on the aqua screen). The same error shows up once, with `events`, `users`, releases and `state`: `pending` (gathering notices before triage), `suggested` (aqua did the triage), `linked`, `regressed` (came back after the issue was completed; tracker already commented on the issue), `merged`, `dismissed` or `muted`. `triage.issue` is the issue aqua wrote and `candidate`, the open issue that looks like the same one.
- `POST /sentry/errors/{id}` (tracker:write): decides as on the aqua screen, `{"action": "create_issue", "title": "..."}` (creates the issue filled in, with link, reach, release, environment and a short stack), `{"action": "link", "issue": "MOB-0012"}` (without `issue`, aqua's candidate; comments on the issue), `{"action": "reopen"}` (an error that came back) or `{"action": "dismiss", "snooze": "7"}` (`"1"`, `"7"`, `"30"` or `"forever"`). Requires editing issues in the project.
- `GET /sentry/connections` (tracker:read, admins and owners): the company's Sentry connections, each with `project` (the project that receives whatever is left over), `webhook_url`, `secret_saved`, `sentry_project_targets` (the table: `{"sentry_project": "my-backend", "target": "MOB"}`, with `target` the project prefix, `rules` or `ignore`), `new_sentry_projects` (`rules` or `ignore`), `rules` (in order, `"/v1/orders/* → API"` or `"project=APP → APP"`), `environments`, `min_level`, `start_after`, `ai_triage`, `seen` (Sentry projects and environments the notices have already shown), the last notice and `waiting`. `GET /sentry/connections/{id}` returns one.
- `POST /sentry/connections` (tracker:write, admins and owners): connects a Sentry, `{"project": "MOB"}`; the response has `webhook_url` to create the Internal Integration in Sentry (Settings → Custom Integrations), and the `client_secret` Sentry shows goes in later. `PATCH /sentry/connections/{id}`: only what's sent changes, `{"client_secret": "...", "sentry_project_targets": [...], "new_sentry_projects": "ignore", "rules": ["/v1/orders/* → API", "project=APP → APP"], "environments": ["prod"], "min_level": "error", "ai_triage": true, "active": true, "start_now": true}` (the table and the rules come whole; `project` changes who receives the leftovers). In a route, a trailing `*` is a prefix; without `*` it matches the route and what's below it. `DELETE /sentry/connections/{id}` disconnects (received errors go away, created issues stay).
- `POST /sentry/connections/{id}/test` (tracker:read, admins and owners): where the connection's latest errors would go (up to 20), with the saved settings or with `sentry_project_targets`, `new_sentry_projects`, `rules`, `environments` and `min_level` from the body, without saving. Each item has `result` (`project`, `ignored` or `outside_environment`), `project` (prefix), `by` (`table`, `rule` with `rule`, the rule number, or `leftover`), `below_min_level`, `now_in`, `route` and `tags`. In the MCP: `list_sentry_connections`, `save_sentry_connection` and `test_sentry_connection`.

### Status pages

- `GET /status` (tracker:read, company admins or owners): status pages. Services (`status` is what the customer sees now, the worst of `manual_status` and the open incidents; `version`, the project's version in production), pages (public `url`, `slug`, `published`, `primary`) and recent incidents and maintenance windows (`phase`: active, upcoming or closed).
- `POST /status/services` and `PATCH`/`DELETE /status/services/{id}` (tracker:write): `{"name": "Driver app", "project": "MOB", "description": "...", "status": "operational|degraded|partial_outage|major_outage|maintenance", "show_version": true, "check_url": "https://api.example.com/health", "sentry_alerts": true}`. In the PATCH, only what's sent changes. `check_url`: a healthcheck every minute (over 3 s is Slow; two failures in a row, Down); `sentry_alerts`: a new error in the project's Sentry sets Slow for 30 minutes. The response has `monitor` with the latest result.
- `GET /status/pages/{id}`, `POST /status/pages` and `PATCH`/`DELETE /status/pages/{id}` (tracker:write): `{"title": "Status", "intro": "...", "published": true, "blocks": [{"kind": "projects", "projects": ["MOB"]}, {"kind": "projects", "title": "Platform", "projects": ["PLT", "API"], "hidden_services": ["Internal queue"]}, {"kind": "services", "title": "Highlights", "services": ["Driver app"]}]}`. `blocks` replaces all blocks, in order; without it, they stay as they are. Services by id or name; projects by prefix, slug or id. `slug`: a readable address on the company's address (`/status/<slug>`; without it, it comes from the title); `primary: true`: the page that opens at `/status` (one per company). The page key keeps opening at any address. Appearance (whatever isn't sent stays as it is): `brand_name` (the page's brand; `null` goes back to the company name), `accent_color` (`#rrggbb`; `null` goes back to the default color), `links` (up to 4 buttons at the top, `[{"label": "Support", "url": "https://..."}]`, `https:` or `mailto:`; replaces the existing ones), `show_history` and `show_past_incidents` (`false` hides the 90-day bars and the closed incidents) and `remove_logo: true`; the logo is uploaded on screen and comes back in `logo_url`.
- `POST /status/incidents` (tracker:write): opens an incident or schedules maintenance. `{"kind": "incident|maintenance", "title": "...", "services": ["Driver app"], "message": "<what the customer reads>", "impact": "degraded|partial_outage|major_outage", "status": "...", "starts_at": "2026-10-18T23:00:00-03:00", "ends_at": "..."}` (maintenance requires `starts_at` and `ends_at`).
- `GET /status/incidents/{id}` (with the timeline), `POST /status/incidents/{id}/updates` (`{"message": "...", "status": "monitoring", "impact": "degraded"}`; `resolved` or `completed` closes it), `PATCH /status/incidents/{id}` (fixes `title`, `services`, `starts_at`, `ends_at` and `impact`, and with `message` also publishes an update) and `DELETE /status/incidents/{id}` (tracker:write).

### Idea boards

- `GET /ideas/boards` (tracker:read, company admins or owners): idea boards, where customers vote on what they want to see. Each one with `url` (the public address, which the customer opens without an account), `published`, `ideas` and `pending_suggestions` (customer suggestions waiting for the team).
- `GET /ideas/boards/{id}` (tracker:read), `POST /ideas/boards` and `PATCH`/`DELETE /ideas/boards/{id}` (tracker:write): `{"title": "Ideas for the app", "intro": "...", "projects": ["MOB"], "published": true, "access": "link|restricted", "allow_list": ["@customer.com", "ana@gmail.com"], "allow_suggestions": true}`. In the PATCH, only what's sent changes; `projects` and `allow_list` replace the whole list. `link`: anyone with the link sees it, and voting requires an e-mail confirmed by code; `restricted`: only those who confirm an e-mail on the list see the ideas. The GET returns `ideas` (published, most voted first, with the `status` the customer sees, `issue`, `votes` and `comments`) and `pending_suggestions` (with `suggested_by`, the customer's e-mail). An idea from a project that left the board disappears only from the public page.
- `POST /ideas/boards/{id}/ideas` (tracker:write): creates a published idea, `{"title": "Export to PDF", "description": "...", "project": "MOB", "issue": "MOB-0012", "status": "under_review|planned|in_progress|released|closed"}`. With `issue` (from a project on the board; without `title`, it uses the issue's), the status follows the issue's; without it, `status` applies. When the status changes to planned, in_progress, released or closed, whoever voted gets an e-mail, in the language of the page where they confirmed their e-mail.
- `GET /ideas/{id}` (tracker:read; the idea with its votes and comments, with the e-mail of who wrote them), `PATCH /ideas/{id}` (the creation fields, only what's sent changes; `"issue": null` unlinks the issue; `{"published": true}` publishes a customer's suggestion) and `DELETE /ideas/{id}` (deletes it; that's how a suggestion is declined) (tracker:write). `POST /ideas/{id}/comments` with `{"body": "..."}` replies as the team (the customer sees "Equipe <company>"); `DELETE /ideas/comments/{id}` deletes a comment.

### Beta invites

- Beta invites (only the platform team with the Beta invites section: view to list, manage to generate, resend and revoke; for others, 403): tracker is in beta with invite-only access, and the beta invite lets an outsider create an aquarela ID account and their own company, as owner (it's different from the invite to an existing company, which lives in Company → People). `GET /beta-invites` (tracker:read) lists the invites, newest first, with `status` (`active`, `used`, `expired`, `revoked`), `email`, `note`, `uses` out of `max_uses`, expiry, `created_by` (who generated it) and `redemptions` (who used it, with the company they created). `POST /beta-invites` (tracker:write) generates one, `{"email": "person@company.com", "note": "Ana, from Construtora Sul", "days": 14}`: with `email`, the link goes by e-mail and only works for that e-mail's account (one company); without it, `max_uses` says how many companies the link creates (default 1). 201 with `url` (the link, which isn't shown again), `expires_at` and `emailed` (`sent`, `disabled` or `failed`; `null` without e-mail). The e-mail goes in the language of the account with that e-mail, if there is one, or else in the language of the person generating it. `POST /beta-invites/{id}/resend` (tracker:write) sends it again with a new link (the previous one stops working) and `DELETE /beta-invites/{id}` revokes it; companies already created stay.

### Platform

- Platform (only the platform team, which runs tracker; for others, 403): the platform owner chooses, section by section, what each team member sees (`view`) or manages (`manage`): `overview` (overview), `organizations` (companies), `support` (sign in as support), `invites` (beta invites), `ai` (AI usage), `plans` (plans, prices and each company's plan) and `audit` (log). `GET /me` and whoami return `platform` (`{"role": "owner|staff", "sections": {"organizations": "manage", ...}}`) for team members who see at least one section, and the platform tools only show up with the section. Changing the team and signing in as support happen only in the app, under Platform.
- `GET /platform/overview` (tracker:read, overview section): tracker's numbers: companies (`active`, `suspended`, `archived`, `created_last_30_days`), people, projects and issues, AI usage in the last 30 days (calls, cost in USD, companies that used it), beta invites and the live version.
- `GET /platform/organizations` (tracker:read, organizations section): every company, with `status` (`active`, `suspended`, `archived`), owners, members, projects, issues, AI calls in 30 days, last activity and the suspension reason. Filters `?status=suspended` and `?query=` (name or slug).
- `POST /platform/organizations/{id}/suspend` (tracker:write, organizations to manage), with an optional `{"reason": "..."}`: the company is suspended, and its members stop getting into the app, the API, the MCP and Slack until someone reactivates it (`POST /platform/organizations/{id}/reactivate`). Nothing is deleted; the team with the support section keeps getting in.
- `POST /platform/demo-account` (tracker:write, organizations to manage), with `{"email": "review@example.com", "name": "Demo account"}`: prepares the account that App Store and ChatGPT reviewers use. Creates the Demo Company (`empresa-demo`) with two projects, 16 issues in various statuses, releases, a test cycle and a document, with the `email` account as owner and three fictional colleagues (@example.com, without e-mail or push). The reviewer signs in with that e-mail's aquarela ID account, created there with a password. Running it again only ensures the account is active and owner (`created: false`). Refuses (422) the e-mail of a platform team member and an account already in another company.
- `GET /platform/plans` (tracker:read, plans section): the plans, with `key`, `name`, `public`, `active`, `is_default` (the plan for those without a subscription), `ai_credits_per_cycle`, `storage_gb` (file storage in GB, `null` for the 10 GB default), `features` (what the plan includes, in the order of the cards and the comparison), `prices` (`currency`, `interval`, `amount_minor`) and `subscriptions` (how many companies are on it). `POST /platform/plans` (tracker:write, plans to manage) creates the plan or edits the one with the same `key`: `{"key": "pro", "name": "Professional", "ai_credits_per_cycle": 30000, "storage_gb": 50, "prices": [{"currency": "BRL", "interval": "month", "amount_minor": 4900}, {"currency": "USD", "interval": "month", "amount_minor": 900}]}`; `prices` and `features` (up to 12 items of up to 120 characters; write them the same way in plans that share an item, so the comparison checks both) are whole lists (whatever isn't sent goes away). There's always a default plan: `is_default` removes the flag from the other one.
- `GET /platform/subscriptions` (tracker:read, plans section): each company's plan, currency, subscription, billing details and plan request (`plan_request`). `PATCH /platform/organizations/{id}/subscription` (tracker:write, plans to manage) changes `plan` (key or id), `status`, `interval`, `currency`, `current_period_start`, `current_period_end`, `trial_ends_at` and `note`, only the ones sent. No charging: it records what was agreed with the customer. Putting the company on the plan it asked for fulfills the request. The plan's AI credits apply to companies without their own adjustment in AI credits.
- `GET /platform/team` (tracker:read, team members who see at least one section): the platform team, with each one's role and sections.
- `GET /platform/audit` (tracker:read, audit section): the platform log, newest first: who signed in as support to which company, companies created, suspended, reactivated, archived and deleted, changes to the team and to beta invites. Filters `?action=support_access`, `?organization_id=`, `?actor_id=`, `?limit=` (up to 200) and `?before=` (the `created_at` of the last row, for the next page). In the 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 and save_organization_subscription.

## Outgoing webhooks

tracker notifies another system when something changes in an issue: a signed JSON POST to the address the company registers. Company admins and owners create webhooks in the app, under Company → Integrations → Webhooks (up to 20 per company), choosing the events and whether the webhook covers every project or just one. The same screen shows the secret, the latest deliveries with the receiver's response, the resend button and the test delivery (`ping`). Creating and changing webhooks isn't available in the API or the MCP.

Events:

- `issue.created`: Task created. From the screen, by e-mail or through MCP.
- `issue.updated`: Task edited. Fields, technical log, release, labels, epic, sprint, estimate and due date. Includes what changed.
- `issue.status_changed`: Status changed. Including through a retest (passed or failed).
- `issue.assigned`: Assignee changed. Assignee or QA owner.
- `comment.created`: New comment. Including replies and email replies.
- `automation.triggered`: a project automation rule with the "Send webhook" action fired. It goes only to the webhook the rule picked, whether or not that webhook subscribes to the event, and carries `automation.rule` (`id`, `name`).
- `ping`: the test delivery from the screen, with `message`.

Body (JSON), the same for every event: `id` (of the event; the same across retries, use it to avoid processing twice), `event`, `created_at`, `organization` (`id`, `slug`, `name`), `actor` (`id`, `name`: who made the change) and `issue`, a snapshot of the issue at that moment: `id`, `code`, `title`, `url`, `type`, `status` (the workflow key), `status_name`, `status_category` (`todo`, `in_progress`, `done`), `severity`, `priority`, `project` (`id`, `name`, `key`), `assignee`, `qa_owner` and `reporter` (`id`, `name`, or `null`), `labels`, `release_id`, `fixed_version`, `description`, `custom_fields`, `created_at`, `updated_at` and `resolved_at`. `issue.updated`, `issue.status_changed` and `issue.assigned` carry `changes`, field by field, `{"status": {"from": "open", "to": "doing"}}` (the keys of the issue history). `comment.created` carries `comment` (`id`, `body` in markdown, `parent_id` when it's a reply, `author`, `mentions` and `url`).

Headers: `Content-Type: application/json`, `User-Agent: tracker-by-aquarela-webhooks/1.0`, `X-Tracker-Event` (the event), `X-Tracker-Delivery` (the delivery id) and `X-Tracker-Signature`.

Delivery: public `https` addresses only (internal networks are refused), without following redirects, with 10 s to respond. Any 2xx counts as delivered; any other response, or none, is retried up to 6 times, after 1 min, 5 min, 30 min, 2 h, 6 h. Respond quickly and process later: arrival order isn't guaranteed.

Signature: `X-Tracker-Signature: t=<unix>,v1=<hex>`, where `v1` is the HMAC-SHA256, in hex, of the text `<t>.<raw body>` with the webhook's full secret (`whsec_...`) as the key. Recompute it with the body exactly as it arrived (before parsing the JSON), compare in constant time and reject `t` more than 5 minutes off. While the secret is being rotated, the header may carry more than one `v1`: accept it if any of them matches. In 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);
}
```

## Concepts

- Organization: the client company. One organization's data never shows up in another.
- Platform: the Aquarela team that runs tracker. The platform owner defines what each person sees or manages (companies, support, invites, AI credits, log); a suspended company has no access until it's reactivated.
- Beta: tracker is in beta, with invite-only access. The platform team generates the beta invite (`create_beta_invite`, `POST /beta-invites`); whoever uses it creates their account and their own company, and the invite records who invited whom.
- Project: has a prefix (e.g. MOB) used in issue codes (MOB-0001), and its own issue types, labels, modules and custom fields.
- Status workflow: each project has its own statuses, each in a group (todo, in_progress, done), with the roles that can move issues into it and the statuses it can come from. Projects start on the Software + QA template (Triage, To do, Doing, In review, Testing, Done, Canceled), the Software template (To do, Doing, In review, Done) or the QA template (Draft, Open, Triage, Prioritized, In development, Ready for QA, Retesting, Approved, Failed, Completed and Closed). "Open" means everything that isn't in the done group.
- Retest: QA records the result with version and device. In Software + QA, passing moves it to Done and failing sends it back to To do with the failed flag (`retest_failed`), until it passes; in the QA template, it goes to Approved or Failed.
- Closed (QA template): a single status for what closes without being resolved, with the reason (`close_reason`: duplicated, canceled, not reproduced or won't fix).
- Blocked: a flag on the issue, with the reason, that works in any status and workflow (it isn't a column). Filter with `blocked_only` or `bloqueio = sim` in the advanced query.
- Projects created before these workflows keep their old statuses (Retesting, Approved, Failed, Blocked, Reopened, Duplicate...) until someone asks to "Simplify the workflow" in Settings > Status workflow.
- Severity: S1 (critical) to S4 (low), technical impact. Priority: urgent, high, medium, low, business importance.
- Hours: each issue has an estimate, a start and a due date, and time entries (who, day, how much, what they did). Remaining = estimate minus what's logged. Duration as text: "2h", "1h30", "45m", "1d" (8 h).
- Documents: the company's knowledge base (manuals, business rules, FAQs, processes), in markdown, for the whole company or one project. A draft (draft) is seen only by those who edit; internal is for the team (admins and the project's PO/PM, QA or dev; stakeholders don't see it) and for agents connected on their behalf; public also shows up in the company's public help center, which has its own llms.txt (and llms-full.txt, with all the texts) for chatbots and RAG without signing in. Search (search_docs, GET /docs/search) returns passages per section, accent-insensitive.

## Releases and tests

Full guide on the /releases/como-funciona screen ("How it works" button in the release list).
- Release: a version of a project (e.g. App 2.4.0), with a planned date, linked issues, test cycles and release notes (a draft generated from the resolved issues, editable, to copy or send to the customer).
- A release's path (status): Planned (planned: in the plan, nobody working on it yet) → In development (in_development: the team works on the issues) → Testing (staging: QA tests in test cycles; it used to be called Staging) → Approved (approved: the gate recorded APPROVED, ready to go live) → In production (production: it went live). Canceled (cancelled) leaves the path and can go back to Planned. The PO/PM moves the status; Approved normally comes from the gate's APPROVED decision; In production requires the gate APPROVED (without it, only an admin, with a justification), except for OTA.
- Which one is live and which is next (the release's moment): version and OTA are separate tracks: in each project, the most recent version in production is the live version and the most recent OTA in production is the live OTA (current, "In production"); the others in production on the same track are previous (previous). The next one (is_next, "Next" badge) is the one going live next on its track: the furthest along the path (Approved, then Testing, In development, Planned) and, on a tie, the one with the earliest planned date. The release list is split into In production, Testing (includes Approved), Next and Previous (includes Canceled).
- Update without the store (OTA): a release with kind "ota", for what reaches devices without going through the App Store or Play Store. Create a new release for each OTA, with the version it carries (e.g. 2.4.1) and the build. It can follow the path with a test cycle and gate, but doesn't have to: an OTA hotfix goes live in hours and QA validates afterwards, already in production. That's why an OTA goes to In production from any step, without a gate decision, and can even be created directly as In production (status "production") when it was already sent. Any release that already went live outside tracker (a backend deploy, a version published earlier) can also be born In production, with the date it went live. Its page shows validation through the linked issues: how many are in Ready for QA, Testing, Approved and Failed. Once published, it becomes the project's live OTA and the previous OTA moves to Previous; the store version stays live, because they're separate tracks. A normal version (store or deploy) is kind "version", the default.
- On the release page, the top says in one sentence where it is, which one is live and what the next step is (e.g. create a test cycle, finish the tests, fix what failed, record APPROVED, publish).
- Issue and release: the issue's Release field says which version it ships in (the main release), normally from its own project; when the fix ships only in another project (the platform deploy, for example), the main one can be that project's release; the board card and the issue show, for example, "Ships in 2.4.0 · testing". When the same fix ships in more than one place (backend deploy, the OTA of each runtime, console), the other releases go in "Also ships in", from any project in the company. The issue shows up in all of them: in the release's list, the gate, the release notes and the validation. The technical notes' "Fixed version" is just the dev's note about the build with the fix.
- Test case: a script with steps and expected result, reused in every version. Test cycle: one round of tests of a release; each case gets pass, fail or blocked, and a FAIL can open an issue already filled in.
- Release Gate: QA's decision to release (APPROVED) or hold (BLOCKED) the version. The app suggests APPROVED when there's a test cycle with cases, all tests passed and there's no open S1 issue; an open S2 only raises a warning.
