Por que seu token falha: escopos que não seguem o nome do recurso
Existe uma categoria de falha de integração que consome uma tarde inteira e termina numa linha de configuração: o token não sai, o erro fala de autorização, e a suspeita natural recai sobre o client_id e o client_secret — que estão certos. A causa costuma ser outra e é fácil de descartar cedo, desde que você saiba de antemão de duas coisas: a API tem exatamente seis escopos, e os nomes deles não acompanham os nomes dos recursos. Quem deduz o escopo a partir da rota — /v1/envelopes ⇒ envelopes:write — pede algo que não existe, e a resposta a isso não diz "esse escopo não existe".
As duas frases que resolvem 90% dos casos: a API tem seis escopos e nenhum deles leva o nome de um recurso — /v1/envelopes, /v1/signing-sessions e /v1/users/…/enrollment autorizam todos em transactions:*. E pedir apenas escopos que não existem devolve 400 no endpoint de token, com uma mensagem que parece problema de credencial.
Os seis escopos
| Escopo | Permite |
|---|---|
transactions:write |
Criar e alterar fluxos de assinatura: transações, sessões, envelopes, upload de documento, cadastro biométrico, cancelamentos |
transactions:read |
Consultar transações, sessões, envelopes, status e download do documento |
steps:write |
Listar, iniciar e concluir etapas de verificação |
evidence:read |
Obter o pacote de evidências de uma transação |
webhooks:write |
Cadastrar, listar, remover e testar webhooks |
verification:write |
Verificação autenticada de documento |
É a lista inteira. Não existem envelopes:read, envelopes:write, signing-sessions:read, signing-sessions:write, documents:write, templates:*, audit:read ou org:admin. Todos esses parecem plausíveis, e é exatamente por isso que aparecem em integrações — deduzidos da rota, não lidos da documentação.
O mapa: rota por rota
A tabela abaixo é o que evita a dedução errada. Repare quantas famílias de rota terminam em transactions:*:
| Rota | Escopo exigido |
|---|---|
POST /v1/transactions |
transactions:write |
GET /v1/transactions · GET /v1/transactions/{id} |
transactions:read |
POST /v1/transactions/{id}/document · /presign · /confirm |
transactions:write |
GET /v1/transactions/{id}/download |
transactions:read |
GET /v1/transactions/{id}/evidence |
evidence:read |
GET /v1/transactions/{id}/steps · /steps/{id}/start · /complete |
steps:write |
POST /v1/signing-sessions · /cancel · /link |
transactions:write |
GET /v1/signing-sessions · /{id}/status |
transactions:read |
POST /v1/envelopes · /sessions · /combined-stamp · /cancel |
transactions:write |
GET /v1/envelopes/{id} |
transactions:read |
PUT · DELETE /v1/users/{id}/enrollment |
transactions:write |
GET /v1/users/{id}/enrollment |
transactions:read |
POST · GET · DELETE /v1/webhooks… |
webhooks:write |
POST /v1/verify/document |
verification:write |
As linhas em destaque são as que quebram a intuição. Um recurso pode ter caminho próprio, verbos próprios e documentação própria — e nenhum escopo próprio.
Duas leituras que também surpreendem
Listar webhooks exige webhooks:write. Não há webhooks:read: as quatro rotas de webhook ficam sob o mesmo escopo de escrita.
Listar etapas exige steps:write. GET /v1/transactions/{id}/steps é uma leitura sob escopo de escrita, porque as três rotas de etapa foram agrupadas por atividade: quem lista etapas está conduzindo o fluxo do signatário, não observando de fora. Se a sua integração só quer acompanhar o andamento, use GET /v1/transactions/{id} com transactions:read — mais barato em permissão e suficiente para monitorar.
Por que o desenho é assim
Vale entender a lógica, porque ela torna o mapa memorizável. Os escopos descrevem capacidades, não caminhos de URL. "Iniciar ou alterar um fluxo de assinatura" é uma capacidade — e criar uma transação, criar uma sessão, montar um envelope, subir um documento e cadastrar uma biometria são todos exercícios dela.
Isso tem uma consequência prática boa: quando a API ganha uma rota nova dentro de uma capacidade existente, as credenciais já emitidas continuam funcionando. Um escopo por recurso obrigaria a reemitir credenciais a cada endpoint novo, e integrações antigas quebrariam a cada evolução da API.
O custo desse desenho é justamente a surpresa que este artigo resolve: o nome do escopo não é dedutível do nome da rota. Por isso o mapa acima.
O erro que parece credencial errada
O pedido de token é assim:
Agora o caso ruim. Suponha uma integração que só cria envelopes e deduziu o escopo do nome da rota:
Leia a mensagem com atenção: ela é verdadeira e enganosa ao mesmo tempo. Nenhum dos escopos pedidos está autorizado — porque nenhum deles existe. Mas o erro chega no passo da autenticação, fala em autorização e não menciona escopo inválido, então a investigação começa pelo client_id e pelo client_secret, que estão corretos. Quando a emissão de token falha com 400, confira a string de escopos antes de desconfiar da credencial.
Que a documentação também erre isso não é hipótese: guias nossos chegaram a publicar signing-sessions:read e signing-sessions:write em sete lugares, incluindo trechos de copiar e colar, e um README de SDK trazia um documents:write inexistente. Foi corrigido — mas se você copiou um snippet antigo, é um candidato imediato.
O caso pior: o escopo errado que não dá erro
O filtro do endpoint de token é por interseção, e ele só recusa quando o resultado é vazio. Isto passa sem reclamar:
Um escopo digitado errado no meio de uma lista boa não gera erro nenhum na emissão. A falha aparece depois, como 403 na rota que dependia do escopo descartado — longe, no tempo e no código, da linha que a causou.
Daí a rotina de depuração: quando uma rota devolve 403 com credencial válida, não olhe primeiro para a rota. Decodifique o token emitido e compare a lista de escopos que ele efetivamente carrega com a que você pediu. A diferença entre as duas é o defeito.
Conjuntos que funcionam
Duas observações sobre o menor privilégio aqui. steps:write só é necessário se a sua integração conduz as etapas por conta própria — no fluxo hospedado, quem conduz é a página de assinatura, com outro token, o de embed, e o seu Bearer não participa. E webhooks:write pertence à rotina de configuração, não ao serviço que roda em produção: separar as duas credenciais custa pouco e evita que um token de aplicação possa reapontar os seus webhooks.
Roteiro de depuração
- 400 na emissão do token? Compare a string de escopos com a lista de seis. Um nome fora dela é a causa mais provável — não a credencial.
- 403 numa rota específica? Decodifique o token e verifique quais escopos ele realmente carrega. Um escopo inválido pedido junto de válidos é descartado em silêncio.
- Copiou de um exemplo antigo? Procure por
signing-sessions:,envelopes:edocuments:no seu código e na sua configuração. Nenhum dos três existe. - 401, e não 403? Aí o problema é outro: credencial, token expirado (a vida útil é de 900 segundos) — ou uma rota que não aceita Bearer, tema do guia sobre os dois tokens da API.
Para o mecanismo de autenticação em si — client_credentials, formato do token, rotação de segredo —, veja OAuth2 e autenticação segura e como obter as credenciais. Para as demais armadilhas de primeira integração, os 12 erros mais comuns.
Perguntas Frequentes
Quais são os escopos da API?
São seis, e apenas seis: transactions:write, transactions:read, steps:write, evidence:read, webhooks:write e verification:write. Não existem envelopes:*, signing-sessions:*, documents:*, templates:*, audit:read nem org:admin. Qualquer nome fora dessa lista é um escopo inventado.
Por que /v1/envelopes usa transactions:write?
Porque os escopos foram desenhados em torno de capacidades, não de caminhos de URL. Criar um envelope, criar uma sessão de assinatura, cadastrar uma biometria e enviar um documento são todos "iniciar ou alterar um fluxo de assinatura" — a mesma capacidade, o mesmo escopo. Novas rotas passam a existir sem exigir um escopo novo em cada credencial já emitida.
O que significa o erro "None of the requested scopes are authorized"?
Que nenhum dos escopos que você pediu está entre os concedidos à sua credencial. O endpoint de token filtra o pedido contra a lista concedida e, quando a interseção é vazia, responde 400. Como o erro chega no passo da autenticação, ele parece problema de credencial — mas o client_id e o client_secret podem estar perfeitos. Confira primeiro a string de escopos.
E se eu pedir um escopo válido junto de um inválido?
O pedido passa. O filtro é por interseção, não exige que todos os escopos sejam reconhecidos: se ao menos um for concedido, o token sai — com os escopos válidos. Isso é conveniente e traiçoeiro, porque um escopo digitado errado no meio de uma lista boa não gera erro nenhum, e a falha só aparece depois, como 403 na rota que dependia dele.
Por que GET /v1/transactions/{id}/steps exige steps:write?
É a exceção que confunde: uma rota de leitura exigindo um escopo de escrita. As três rotas de etapas — listar, iniciar e concluir — ficam juntas sob steps:write, porque quem lista etapas na prática está conduzindo o fluxo do signatário. Uma integração que só monitora status deve usar GET /v1/transactions/{id} com transactions:read, não a listagem de etapas.
Como peço os escopos na requisição de token?
No corpo, em application/x-www-form-urlencoded, com grant_type=client_credentials e os escopos separados por espaço no campo scope. O token resultante expira em 900 segundos (15 minutos), então a integração precisa renová-lo — não guarde um token entre execuções longas.
Qual conjunto mínimo devo pedir?
Depende do que a integração faz. Só criar e acompanhar: transactions:write transactions:read. Acrescente evidence:read se for baixar o pacote de evidências, steps:write se conduzir as etapas você mesmo (não é necessário no fluxo hospedado), webhooks:write só no momento do cadastro dos webhooks e verification:write apenas se usar a verificação de documentos autenticada.
Comece com o token certo na primeira tentativa
Seis escopos, um mapa de rotas e o princípio do menor privilégio aplicado sem quebrar a integração. Crie credenciais de homologação gratuitas e teste a emissão do token em minutos.
Criar credenciais de homologação Fale com o time comercial