Instalação e OAuth
A instalação é o OAuth 2.0 authorization code com PKCE (S256). O resultado é um par de tokens daquela empresa.
Seu backend Botzetta (admin logado) API da Plataforma
1. GET /botzetta/instalar
gera verifier, challenge e state
302 → <Botzetta>/apps/instalar?… ──► 2. tela de consentimento
3. admin autoriza
4. GET <redirect_uri>?code&state ◄──── 302 de volta
5. confere o state
POST /plataforma/v1/oauth/token ─────────────────────────────────────► 6. access + refresh da empresa
1. URL de instalação
É o endpoint do seu backend cadastrado como URL de instalação (o portal e os clientes abrem ele). A cada acesso:
- gere o
code_verifier: 32 bytes aleatórios em base64url (43 caracteres); - calcule o
code_challenge= base64url(SHA-256(code_verifier)), sem=; - gere um
statealeatório; - guarde
verifierestatedo lado do servidor por 10 minutos, e amarre ostatea este navegador (cookieHttpOnly); - redirecione para a tela de consentimento:
GET <URL do Botzetta>/apps/instalar
?client_id=bzt_meuapp_…
&redirect_uri=https://meuapp.exemplo.com/botzetta/callback
&response_type=code
&state=<state>
&code_challenge=<challenge>
&code_challenge_method=S256
import { createHash, randomBytes } from "node:crypto";
const verifier = randomBytes(32).toString("base64url");
const challenge = createHash("sha256").update(verifier).digest("base64url");
const state = randomBytes(24).toString("base64url");
2. Consentimento (no Botzetta)
O Botzetta pede login, se preciso, e mostra ao admin o app, a empresa, os escopos com rótulo e risco, o seu e-mail de suporte e a sua política de privacidade. O consentimento é tudo ou nada.
| O admin | O Botzetta redireciona para |
|---|---|
| Autoriza | <redirect_uri>?code=bzc_…&state=<state> |
| Cancela | <redirect_uri>?error=access_denied&state=<state> |
Link inválido (client_id desconhecido, redirect_uri fora do cadastro, PKCE ausente) não
redireciona: a tela mostra o erro. Também não instala se o plano da empresa não inclui apps de
parceiros, se o app privado já atingiu o limite de empresas ou se a
versão ativa ainda depende de revisão.
3. Callback: trocar o código
No redirect_uri, confira o state (o mesmo que você guardou e o do cookie) antes de qualquer
coisa. Depois troque o código em POST /plataforma/v1/oauth/token (postOauthToken):
POST https://api.botzetta.com/plataforma/v1/oauth/token
Content-Type: application/json
{
"grant_type": "authorization_code",
"code": "bzc_…",
"redirect_uri": "https://meuapp.exemplo.com/botzetta/callback",
"code_verifier": "<verifier>",
"client_id": "bzt_meuapp_…",
"client_secret": "bzcs_…"
}
{
"access_token": "eyJhbGciOiJSUzI1NiIs…",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "bzr_…",
"escopos": ["usuarios:ler", "pessoas:ler"],
"companyId": 88,
"instalacaoId": 412
}
O endpoint aceita JSON ou application/x-www-form-urlencoded. Guarde os tokens por companyId,
cifrados, junto com instalacaoId e a validade (agora + expires_in).
Regras do código: uso único, vale 10 minutos, o redirect_uri tem de ser o mesmo da autorização e o
code_verifier tem de conferir. Errar o PKCE ou o redirect invalida o código. Reusar um código já
trocado revoga os tokens daquela instalação.
4. Renovar o access token
O access token vale 1 hora. Renove com o refresh:
POST https://api.botzetta.com/plataforma/v1/oauth/token
Content-Type: application/json
{ "grant_type": "refresh_token", "refresh_token": "bzr_…", "client_id": "bzt_meuapp_…", "client_secret": "bzcs_…" }
A resposta tem o mesmo formato, com um refresh novo. O refresh é rotativo:
- salve o par novo antes de usar o access token novo. Se a gravação falhar depois de o Botzetta ter rotacionado, o refresh antigo já não vale;
- reusar um refresh já usado revoga a família inteira (
ERR_OAUTH_REFRESH_INVALIDO) e o admin precisa reinstalar. A exceção são 30 segundos depois do uso: aí o Botzetta entende como renovação concorrente e recusa sem revogar. Releia o par salvo e use o novo; - faça uma renovação por empresa por vez (trava no banco ou fila por
companyId); - renove um pouco antes de expirar (ex.: faltando 1 minuto), ou quando uma chamada voltar
401.
O refresh vale 90 dias. Uma empresa que não usa o app por mais tempo precisa reinstalar.
5. Revogar
Para encerrar a integração com uma empresa do seu lado, revogue o refresh. A resposta é sempre 200
(não revela se o token existia):
POST https://api.botzetta.com/plataforma/v1/oauth/revogar
Content-Type: application/json
{ "token": "bzr_…", "client_id": "bzt_meuapp_…", "client_secret": "bzcs_…" }
Desinstalação e reinstalação
Quando a empresa desinstala o app no Botzetta:
- todos os refresh da instalação são revogados e o access token deixa de valer (
403 ERR_APP_NAO_HABILITADO); - o app recebe o webhook
app.desinstalado; - um token emitido antes da desinstalação nunca volta a valer, mesmo que a empresa reinstale: a reinstalação passa pelo fluxo inteiro de novo e gera tokens novos.
Erros do OAuth
O formato é o da API ({ "error": "ERR_…", "message": "…" }), não o do RFC 6749.
| Código | HTTP | Quando |
|---|---|---|
ERR_CREDENCIAIS_INVALIDAS | 401 | client_id ou client_secret errados ou revogados |
ERR_OAUTH_PARAMETRO | 400 | Parâmetro faltando ou inválido |
ERR_OAUTH_CODIGO_INVALIDO | 400 | Código expirado, já usado, ou PKCE/redirect que não conferem |
ERR_OAUTH_REFRESH_INVALIDO | 400 | Refresh inválido, expirado, revogado ou reusado |
ERR_GRANT_NAO_PERMITIDO | 400 | grant_type que apps de parceiro não usam (client_credentials) |
ERR_LIMITE_REQUISICOES | 429 | Muitas chamadas. Respeite o Retry-After |
O /oauth/token tem limites por IP e por client_id. Renovações (refresh_token) têm um teto
próprio de 300 por minuto, pensado para quem renova os tokens de muitas empresas.