Começar
Login de usuário (OAuth2)
Quando o seu app entra com a conta da pessoa: OAuth2 com PKCE, a escolha do workspace e o token de 1 hora.
Há dois jeitos de autenticar na v1, e eles não competem. A chave de API é credencial do workspace: o dono cria, o seu servidor guarda, e ela não carrega pessoa nenhuma. O login de usuário é OAuth2 com redirect: a pessoa entra com o e-mail e a senha dela, na nossa tela, escolhe em qual workspace o seu app vai agir, e o seu app recebe um token que carrega quem ela é.
Use este caminho quando o seu app é de terceiro e cada usuário dele já tem conta no Growth OS — um app desktop, uma extensão, um produto que você vende pra vários clientes nossos.
Chave de API ou login de usuário
Chave de API (gos_…) | Login de usuário (OAuth2) | |
|---|---|---|
| Quem é o portador | o workspace | a pessoa que entrou |
| Onde a credencial mora | no seu servidor | no seu app, uma por usuário |
| Quem a emite | o dono do workspace, na tela de Acessos | a própria pessoa, fazendo login |
| Escopos | marcados na criação da chave | pedidos por você na URL de autorização, confirmados por ela na tela |
| Workspace | o da chave, fixo | escolhido por ela no consentimento |
| Validade | 1 ano por padrão | 1 hora, renovável por 30 dias |
| Allowlist de IP | opcional | não se aplica |
platform.read | só a conta Accelera | nunca, para ninguém |
| Se a pessoa sai do workspace | nada muda | o token para de funcionar na requisição seguinte |
Antes de começar: o registro do app
Não existe autosserviço: quem registra o app somos nós. Peça pelo seu canal de sempre, com o nome do app (é o que a pessoa vê na tela de consentimento) e as URLs de retorno (redirect_uri) que ele usa. Você recebe um client_id.
App que roda na máquina do usuário (desktop, mobile, CLI) é registrado como público: não recebe client_secret, porque não há onde guardar um segredo em código que o usuário tem na mão — a prova de posse é o PKCE. App que tem servidor próprio pode ser registrado como confidencial e aí sim ganha um client_secret. Trocar a URL de retorno depois não troca o seu client_id.
Os escopos que o app pode pedir
São os mesmos da chave de API — contacts.read, contacts.write, pipelines.read, opportunities.read, opportunities.write, calendars.read, calendars.write, users.read, webhooks.manage — menos platform.read, que é exclusivo da conta Accelera e não existe por este caminho: pedido ou não, a rota responde 403 forbidden_platform. Some a eles dois escopos do próprio protocolo:
openid— sempre; é o que faz o pedido ser um login.offline_access— só se você quiser o refresh token. Sem ele a resposta não trazrefresh_tokennenhum, e o seu app terá que mandar a pessoa fazer login de novo a cada hora. É o esquecimento mais caro deste guia.
Peça só o que o app usa: o que não foi consentido responde 403 forbidden_scope nomeando o que falta, igual à chave.
O fluxo, do botão ao token
- O seu app gera um
code_verifieraleatório, calcula ocode_challenge(SHA-256, base64url) e abre o navegador na URL de autorização abaixo.S256é obrigatório —plainé recusado. - A pessoa entra com e-mail e senha na nossa tela (e o 2FA dela, se estiver ativo). Se já houver sessão aberta no navegador, este passo não aparece.
- Ela vê a tela de consentimento: o nome do seu app, a lista do que ele está pedindo, e em qual workspace. O seletor só aparece quando ela é membro de mais de um; quem não é membro de nenhum não consegue autorizar.
- Ela clica em "Permitir" e o navegador volta pro seu
redirect_uricomcodee o seustatede volta — confira ostate, é ele que amarra a resposta ao pedido que você fez. - O seu app troca esse
codepelo token, no passo seguinte.
https://api.growthaisystem.com/api/auth/oauth2/authorize
?response_type=code
&client_id=SEU_CLIENT_ID
&redirect_uri=meuapp%3A%2F%2Foauth%2Fcallback
&scope=openid%20offline_access%20contacts.read%20opportunities.read
&state=UM_VALOR_ALEATORIO_SEU
&code_challenge=BASE64URL_DO_SHA256_DO_VERIFIER
&code_challenge_method=S256Trocar o code pelo access token
No formato de formulário (application/x-www-form-urlencoded), que é o que a RFC pede — não JSON:
curl -X POST "https://api.growthaisystem.com/api/auth/oauth2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "code=O_CODE_QUE_VEIO_NO_REDIRECT" \
-d "redirect_uri=meuapp://oauth/callback" \
-d "client_id=SEU_CLIENT_ID" \
-d "code_verifier=O_VERIFIER_QUE_GEROU_O_CHALLENGE"{
"access_token": "eyJhbGciOi...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "tok_...",
"scope": "openid offline_access contacts.read opportunities.read",
"id_token": "eyJhbGciOi..."
}refresh_token só vem se você pediu offline_access; id_token, se você pediu openid. Guarde os dois tokens como credencial da pessoa, no cofre do sistema operacional — não em arquivo de configuração em texto puro.
Chamar a v1 com o token da pessoa
Mesmo header de sempre, e é só isto que muda em relação à chave: o token não começa com gos_. As rotas são as mesmas, o envelope é o mesmo, os erros são os mesmos.
curl "https://api.growthaisystem.com/api/v1/contacts?limit=1" \
-H "Authorization: Bearer O_ACCESS_TOKEN"Os limites são os mesmos da chave (100 requisições por 10 segundos, 50 por rota, 10.000 por dia), só que contados por app + usuário — dois vendedores do mesmo cliente têm baldes separados, e renovar o token não zera balde nenhum. A idempotência funciona igual, e sobrevive à renovação: a mesma Idempotency-Key continua deduplicando depois de um refresh.
Duas coisas não se aplicam aqui: a allowlist de IP (um app instalado na máquina de alguém não tem IP fixo) e platform.read.
Renovar sem pedir login de novo
O access token vale 1 hora; o refresh token, 30 dias. Renove antes de expirar, ou quando levar 401:
curl -X POST "https://api.growthaisystem.com/api/auth/oauth2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "refresh_token=O_REFRESH_TOKEN" \
-d "client_id=SEU_CLIENT_ID"A renovação não passa por tela nenhuma e mantém o workspace que a pessoa escolheu. Se o refresh token também tiver expirado (30 dias sem uso), aí sim é login de novo — recomece pelo passo 1.
Em qual workspace o token entra
Um token vale para um workspace: o que a pessoa escolheu na tela. Na segunda vez que ela autoriza o mesmo app com os mesmos escopos, a tela de consentimento não aparece (é o padrão do OAuth2: não repergunta o que já foi respondido), e nós reaproveitamos a escolha anterior dela.
Se não houver escolha válida — a pessoa foi removida do workspace que tinha escolhido, por exemplo —, o token nasce sem workspace e a v1 responde 401 pedindo login de novo. Nunca chutamos um workspace no lugar dela.
Os erros que só existem aqui
Tudo o que a página de erros descreve continua valendo. O que muda são estes 401, todos com o mesmo remédio do lado do seu app — renovar ou refazer o login:
| Resposta | O que aconteceu | O que o app faz |
|---|---|---|
401 "Este token OAuth expirou" | passou de 1 hora | renova pelo refresh token |
401 "Token OAuth desconhecido, revogado ou o app está desativado" | token inválido, ou o app perdeu o registro | refaz o login; se persistir, fale conosco |
401 "…não tem workspace associado" | nenhuma escolha de workspace válida sobrou | refaz o login (com prompt=consent) |
401 "…não é mais membro do workspace" | a pessoa saiu ou foi removida — checado a cada requisição | refaz o login e escolhe outro workspace |
403 forbidden_scope | a rota pede um escopo que não foi consentido | autoriza de novo pedindo o escopo que falta |
403 forbidden_platform | /platform/* por este caminho | nada a fazer: é exclusivo da conta Accelera |
403 workspace_suspended | o workspace está pausado | nada a fazer: é a conta do cliente |