← Documentação da API

Começando

A API pública da Guarita serve para automatizar a segurança dos seus apps a partir das suas próprias ferramentas: disparar uma ronda no fim do deploy, ler o relatório no CI, falhar o build quando aparece um crítico, alimentar um painel interno com o histórico.

A URL base em produção é https://api.guarita.dev. Todas as rotas desta documentação ficam sob /v1/public e são autenticadas por uma chave de API (gsk_...), nunca pela sessão do painel.

O que a API faz

São seis rotas. Nenhuma delas é "a mesma rota que a tela usa": a API pública é um contrato separado, com resposta própria e versionada.

MétodoRotaEscopo exigidoO que faz
GET/v1/public/mescans:readConfirma que a chave funciona e mostra o que ela pode
GET/v1/public/appsapps:readLista os apps monitorados
GET/v1/public/scansscans:readHistórico de rondas, paginado por cursor
GET/v1/public/scans/:idscans:readEstado e resumo de uma ronda
GET/v1/public/scans/:id/reportscans:readRelatório completo de uma ronda
POST/v1/public/scansscans:writeDispara uma ronda (responde 202)

O que a API não faz — e por quê

Não existe rota de cobrança, de gestão de time, de dados fiscais, de troca de senha, de 2FA nem de exclusão de conta. Isso não é backlog: é decisão de projeto.

A superfície da API é mínima de propósito, e o critério é um só — chave vazada não pode virar dano irreversível. Uma chave é uma credencial de máquina: fica em variável de ambiente, passa por log de CI, é copiada para um runner que você não administra. Se essa credencial pudesse cancelar a assinatura, remover pessoas do time ou apagar a conta, um vazamento deixaria de ser um incidente recuperável (revogar a chave, rotacionar) e viraria perda definitiva. Entra na API só o que faz sentido automatizar; o resto continua exigindo uma sessão de verdade, no painel, com senha e 2FA.

Duas consequências do mesmo princípio, que valem antes de você escrever a primeira linha:

  • A chave muda quem chama, nunca o que é permitido. Disparar uma ronda pela API passa pelo mesmo caso de uso da tela: cota de análises com IA do plano, limite de apps e declaração de autorização do alvo continuam valendo. Não há caminho pela API que contorne uma regra do painel.
  • O relatório passa pelo mesmo preparo do painel e do PDF. O que o seu plano não destrava na tela também não sai por aqui. Uma porta nova não pode ser a porta por onde o paywall vaza.

A API exige plano Pro ou superior

A API pública e os webhooks de saída são recursos dos planos Pro e Business. Nos planos Free e Starter, o painel não emite chave.

O plano é verificado em toda requisição, não só na hora de criar a chave. Se a conta cair para um plano sem API, as chaves existentes param de autenticar no mesmo instante — e a resposta é 401, não 403. Vale entender a diferença antes de investigar o incidente errado:

  • 401 unauthorized — a Guarita não reconhece essa chave. Formato errado, chave inexistente, chave revogada ou conta sem plano com API. As causas não são distinguidas na resposta de propósito: dizer "essa chave existe, mas foi revogada" ajuda mais quem está sondando do que quem esqueceu de atualizar a variável de ambiente.
  • 403 insufficient_scope — a chave é válida, e falta a ela a permissão que a rota exige.

Trocar os dois na sua automação leva a rotacionar uma chave que estava boa atrás de um problema que era de permissão ou de plano.

Criar uma chave

No painel, vá em Configurações > Desenvolvedores e crie uma chave. Você escolhe um nome (2 a 40 caracteres) e ao menos uma permissão.

Só quem é dono da conta gerencia chaves. Para um membro do time, a rota responde 403 not_owner — uma chave dá acesso programático à conta inteira, então ela está na mesma família de cobrança e dados fiscais, não na de "coisa que membro mexe".

O nome existe para você reconhecer a chave depois. Prefira algo que diga onde ela roda (ci-producao, deploy-vercel) em vez de chave 1: quando precisar revogar, é por esse nome que você vai decidir qual.

O limite é de 10 chaves ativas por conta. Ao tentar criar a décima primeira, a resposta é 409 api_key_limit — chave esquecida é chave que ninguém vigia.

Escopos

EscopoPermite
scans:readLer rondas e relatórios
scans:writeDisparar novas rondas
apps:readListar os apps monitorados

Peça só o que a integração usa. Uma chave de leitura vazada não dispara ronda nem consome a sua cota de análises com IA — é a diferença entre um susto e uma fatura. Um bot que publica o resumo da última ronda no Slack precisa de scans:read e nada mais; um passo de CI que dispara a ronda precisa de scans:write.

O segredo aparece uma vez

Na criação, o painel mostra o segredo em claro. É a única vez que ele existe: a Guarita guarda só um hash. Não há tela de "ver de novo", nem suporte que consiga recuperá-lo — se você perder, revogue a chave e crie outra.

Copie e guarde no cofre de segredos do seu CI ou da sua hospedagem antes de fechar a tela.

O formato é gsk_ (de Guarita Secret Key) seguido de 43 caracteres, com 256 bits de aleatoriedade. O corpo é base64url, sem +, / ou =, então a chave atravessa header HTTP, URL e variável de ambiente sem precisar de escape:

gsk_a1b2c3d4KZ8mQvR7tYw2NpX5LhJ0eSg6UdF9BcA3iOk

Depois da criação, a lista mostra só os 8 primeiros caracteres do corpo (gsk_a1b2c3d4). É o bastante para você reconhecer qual chave é qual na hora de revogar, e não o bastante para adivinhar o resto. Sem esse trecho, quem tem três chaves acaba revogando as três "por segurança", que é pior.

Como enviar a chave

A chave vai no header Authorization, com o esquema Bearer:

Authorization: Bearer gsk_a1b2c3d4KZ8mQvR7tYw2NpX5LhJ0eSg6UdF9BcA3iOk

Não há autenticação por query string nem por header alternativo. Chave em URL vaza em log de servidor, em histórico de shell e em referrer — por isso não existe essa opção.

Primeiro request

GET /v1/public/me é o ping autenticado: ele confirma que a chave chegou, mostra a conta a que ela pertence, os escopos que ela carrega e o plano em vigor. É o primeiro request a fazer em qualquer integração nova, e o primeiro a rodar quando algo parar de funcionar.

bash
export GUARITA_API_KEY="gsk_a1b2c3d4KZ8mQvR7tYw2NpX5LhJ0eSg6UdF9BcA3iOk"

curl -sS https://api.guarita.dev/v1/public/me \
  -H "Authorization: Bearer $GUARITA_API_KEY"

Resposta:

json
{
  "account_id": "acc_9f31c2ab",
  "key_id": "key_4b7e1d20",
  "scopes": ["scans:read", "scans:write"],
  "plan": "pro"
}

O key_id é o mesmo identificador que aparece em Configurações > Desenvolvedores: é por ele que você liga um comportamento estranho na API à chave que o causou, e é ele que você revoga.

Repare que /v1/public/me exige scans:read. Não é uma rota "sem escopo": uma chave criada só com apps:read recebe 403 aqui, mesmo sendo uma chave perfeitamente válida.

json
{
  "error": "insufficient_scope",
  "message": "Esta chave não tem a permissão \"scans:read\".",
  "required": "scans:read"
}

Sem chave, ou com uma chave que a Guarita não reconhece:

json
{
  "error": "unauthorized",
  "message": "Chave de API ausente ou inválida. Envie `Authorization: Bearer gsk_...` — crie a sua em Configurações > Desenvolvedores."
}

Todo erro da API tem a mesma forma: error com um código estável, feito para o seu código ler, e message em português, feito para uma pessoa ler no log. Trate o error; mostre a message.

Em Node, o mesmo request lendo a chave do ambiente:

js
const res = await fetch("https://api.guarita.dev/v1/public/me", {
  headers: { Authorization: `Bearer ${process.env.GUARITA_API_KEY}` },
});

const body = await res.json();
if (!res.ok) {
  throw new Error(`Guarita ${res.status} ${body.error}: ${body.message}`);
}

console.log(body.plan, body.scopes);

A chave é uma senha

Trate o valor gsk_... como trataria a senha do banco de produção. Na prática:

  • Não vai para o repositório. Nem em código, nem em arquivo de exemplo, nem em commit que você pretende reescrever depois. Git guarda tudo, e um repositório que ficou público por dez minutos já é um vazamento. Use variável de ambiente e mantenha o .env fora do controle de versão.
  • Não vai para o front-end. Qualquer segredo em código que roda no navegador é público: está no bundle, aparece no DevTools, é lido por qualquer visitante. Chame a API do seu servidor, do seu CI ou de uma função serverless — nunca do cliente.
  • Não vai para chamado de suporte, print de tela ou log. Se a chave apareceu em algum desses lugares, considere que vazou e rotacione.

Por que a API não é chamável do navegador

Mesmo que você tentasse, não funcionaria: o CORS da Guarita libera uma única origem, a do painel. Um fetch para api.guarita.dev a partir do seu site é bloqueado pelo navegador antes da resposta chegar ao seu código.

Isso é deliberado, e não uma configuração esquecida. Liberar * transformaria "usar a chave no front-end" numa coisa que funciona — e integração que funciona é integração que fica. O bloqueio faz o erro aparecer no seu ambiente de desenvolvimento, no minuto zero, em vez de aparecer meses depois como uma chave publicada no bundle do seu site.

Requisições que não têm origem de navegador (servidor para servidor, CI, curl) não passam por essa checagem e funcionam normalmente. A regra prática é: se o código que monta o header Authorization pode ser lido por um visitante, ele está no lugar errado.

Rotacionar e revogar

Revogar vale imediatamente. A chave não é um token auto-contido que continua válido até expirar: a Guarita resolve a chave a cada requisição consultando as chaves ativas da conta, então não há cache nem token em circulação que sobreviva à revogação. Foi por isso que a chave não é um JWT.

Revogar não apaga a linha da listagem — ela vira histórico, com a data. E a listagem mostra o último uso de cada chave, que é como você acha aquela criada há um ano para um projeto que não existe mais.

Para rotacionar sem derrubar a integração: crie a chave nova, atualize a variável de ambiente, confirme com um GET /v1/public/me usando a nova, e só então revogue a antiga. As duas convivem enquanto durar a troca.

Próximo passo

Com GET /v1/public/me respondendo 200, o resto da integração é escolher as rotas. Os próximos capítulos cobrem os endpoints de rondas e relatórios em detalhe e os webhooks de saída, que invertem o sentido da conversa: em vez de você perguntar se a ronda terminou, a Guarita chama a sua URL com uma entrega assinada quando algo acontece.