Dois tokens, dois públicos: Bearer do integrador e token de embed do signatário
Quase toda API tem uma credencial só: o seu token, em toda chamada. A API de assinatura tem duas, e a diferença não é organizacional — é de público. O seu token de aplicação representa a empresa que pediu a assinatura; o token de embed representa a pessoa que vai assinar. São interesses distintos e, em alguns momentos, opostos: o remetente não deveria conseguir avançar as etapas em nome de quem assina. Daí a consequência que aparece como bug: algumas rotas recusam o seu Bearer com 401, e o erro é indistinguível de credencial errada. Este guia traz o mapa completo e o que fazer quando um link de assinatura já foi consumido.
Se você chegou aqui por um 401: confira se a rota é uma das seis que só aceitam token de embed. Elas são as que o signatário usa — carregar a sessão, avançar etapas, reenviar código — e recusam o token de aplicação por desenho, não por defeito.
Duas credenciais, dois interesses
O seu token de aplicação, obtido em POST /oauth2/token, representa a empresa que pediu a assinatura. Ele vale para o tenant inteiro, atravessa todas as transações e vive 15 minutos — independentemente do que acontece com qualquer sessão específica.
O token de embed representa a pessoa que vai assinar. Ele nasce preso a uma sessão, morre com ela e não serve para mais nada.
A separação existe porque os dois papéis não são a mesma pessoa e, em alguns momentos, têm interesses opostos. Se o token de aplicação pudesse avançar as etapas de uma sessão, quem enviou o documento poderia concluir a assinatura em nome de quem deveria assinar — e a evidência registraria, com todos os carimbos de tempo em ordem, um ato que a pessoa nunca praticou. Restringir as rotas de avanço ao token do signatário é o que mantém essa fronteira de pé.
Dito de outro modo: o 401 que incomoda é a fronteira funcionando.
O mapa das seis rotas
| Rota | Quem autentica | Quem usa na prática |
|---|---|---|
GET /v1/signing-sessions/{id} |
Token de embed | A página de assinatura, ao carregar |
POST /v1/signing-sessions/{id}/advance |
Token de embed | A página, a cada etapa concluída |
POST /v1/signing-sessions/{id}/resend-otp |
Token de embed | O signatário, ao pedir outro código |
GET /v1/trust-sessions/{id} |
Token de embed | A página de autenticação do ato |
POST /v1/trust-sessions/{id}/advance |
Token de embed | A página, a cada etapa |
POST /v1/trust-sessions/{id}/resend-otp |
Token de embed | A pessoa autenticando |
E as rotas vizinhas, que usam o seu Bearer normalmente:
| Rota | Escopo | Para quê |
|---|---|---|
POST /v1/signing-sessions |
transactions:write |
Criar a sessão |
GET /v1/signing-sessions/{id}/status |
transactions:read |
Consultar o andamento |
POST /v1/signing-sessions/{id}/cancel |
transactions:write |
Cancelar |
POST /v1/signing-sessions/{id}/link |
transactions:write |
Emitir um link novo |
A confusão mora nestas duas linhas: GET /v1/signing-sessions/{id} e GET /v1/signing-sessions/{id}/status. A primeira parece a rota óbvia para "consultar a sessão" — é o padrão REST que todo mundo espera — mas ela é a que a página de assinatura usa para carregar o estado, e por isso só aceita token de embed. A rota do integrador é /status. Um snippet que chama a primeira com Bearer devolve 401 e não é bug.
De onde vem o token de embed
Ele começa como o clientSecret devolvido na criação da sessão:
O link entregue à pessoa é a url com o segredo anexado como parâmetro cs. A página de assinatura lê esse parâmetro, obtém o token de embed e passa a usá-lo nas chamadas de advance.
Na prática, você raramente manipula esse token: entrega o link e acompanha por /status ou por webhook. Ele só aparece explicitamente em dois casos — integração headless, que conduz as etapas por conta própria, e modo embarcado, em que o segredo é passado ao componente de checkout.
Uma consequência que vale registrar: o clientSecret é uma credencial, ainda que de vida curta e escopo mínimo. Ele não deveria aparecer em log de aplicação, em URL registrada por um proxy, nem em mensagem encaminhável para quem não é o signatário.
Uso único: o link que já foi usado
O link de assinatura vale uma vez. Depois que a pessoa conclui — ou que o token embutido é consumido de outra forma —, reabrir a mesma URL devolve:
Isso costuma soar como problema até se pensar na alternativa: um link reutilizável seria uma credencial permanente circulando por e-mail, capaz de reabrir a sessão de outra pessoa. O uso único é o que impede isso.
O efeito colateral é real, porém. A pessoa fecha a aba sem querer, o e-mail é encaminhado e alguém abre antes, o aplicativo de mensagens pré-carrega o link. Nada disso deveria custar uma transação nova — e não custa:
Três propriedades desta rota, cada uma respondendo a uma pergunta previsível:
- Não consome cota. Reemitir um link não é enviar um documento novo, e o plano não é debitado outra vez.
- Não estende o prazo. O
expiresAtdevolvido é o da sessão original. Relinkar não é uma forma de esticar o prazo indefinidamente — se a sessão venceu, o caminho é outro. - Só em sessões
ACTIVE. Uma sessão concluída ou cancelada responde409 Session cannot be linked in status: <status>. Faz sentido: um link para uma sessão encerrada não autenticaria coisa alguma.
Vale para sessões avulsas e para sessões dentro de um envelope, indistintamente.
O que fazer quando a sessão já terminou
Se o pedido é "o cliente quer ver o que assinou", relinkar é a ferramenta errada. O que ele quer é o documento:
| Situação | Rota |
|---|---|
| Sessão ativa, link perdido ou consumido | POST /v1/signing-sessions/{id}/link |
| Envelope concluído, quer o PDF final | POST /v1/envelopes/{id}/combined-stamp |
| Transação avulsa concluída, quer o PDF | GET /v1/transactions/{id}/download |
| Quer a prova, não o documento | GET /v1/transactions/{id}/evidence |
| Sessão expirada de vez | Criar nova — e aí sim consome cota |
Depurando um 401
- A rota está na lista das seis? Se sim, ela nunca vai aceitar o seu Bearer. Para consultar, troque
GET /{id}porGET /{id}/status. - A mensagem diz
Embed token has been consumed? O link foi usado. Emita outro com/link, sem custo. - É uma rota de Bearer mesmo assim? Verifique a validade do token — ele dura 900 segundos e integrações que o guardam entre execuções longas o usam vencido.
- É 403, e não 401? Então a credencial foi aceita e o problema é de permissão: consulte o mapa de escopos, onde um escopo inválido descartado em silêncio é a causa mais comum.
Para os prazos que governam cada token e cada sessão, veja todos os prazos da API em uma página. Para o fluxo embarcado, em que o segredo é passado ao componente de checkout, assinatura incorporada. E para o runbook de sessões vencidas, sessão expirada: reenviar, cancelar ou recriar.
Perguntas Frequentes
Quais rotas não aceitam o meu token Bearer?
Seis, e todas são rotas que a pessoa que assina usa: GET /v1/signing-sessions/{id}, POST /v1/signing-sessions/{id}/advance, POST /v1/signing-sessions/{id}/resend-otp e as três equivalentes em /v1/trust-sessions. Todas as demais rotas da API usam o Bearer normalmente.
Como consulto o andamento de uma sessão, então?
Com GET /v1/signing-sessions/{id}/status, que é rota de Bearer e exige transactions:read. A confusão é natural: GET /v1/signing-sessions/{id} parece a rota óbvia para consultar, mas ela é a que a página de assinatura usa para carregar a sessão — e por isso só aceita o token de embed.
De onde vem o token de embed?
Do clientSecret devolvido na criação da sessão. Ele é anexado à URL de assinatura como parâmetro, e a página de assinatura o troca pelo token que usa nas chamadas seguintes. Na prática, você raramente o manipula: entrega o link e a página cuida do resto.
Por que essa separação existe?
Porque o token de aplicação vale para o tenant inteiro e não expira junto com a sessão. Se ele pudesse avançar etapas, quem enviou o documento poderia concluir a assinatura em nome do signatário — e a evidência registraria um ato que a pessoa não praticou. Restringir as rotas de avanço ao token do signatário é o que mantém a separação entre quem pede e quem assina.
O link de assinatura pode ser reutilizado?
Não: ele é de uso único. Depois que o signatário conclui, ou que o token embutido é consumido de outra forma, reabrir a mesma URL devolve 401 Embed token has been consumed. É o comportamento pretendido — um link permanente seria uma credencial permanente circulando por e-mail.
Como emito um link novo sem recriar a transação?
Com POST /v1/signing-sessions/{id}/link, que gera uma URL nova para a sessão existente, sem consumir cota. Vale para sessões avulsas e de envelope. Duas condições: a sessão precisa estar ACTIVE — uma concluída ou cancelada devolve 409 — e o prazo original não é estendido, porque o link herda o expiresAt da sessão.
E se a sessão já estiver concluída?
Aí não há o que relinkar: um link para uma sessão encerrada não autenticaria nada. Para dar acesso ao documento assinado, use POST /v1/envelopes/{id}/combined-stamp no caso de envelope, ou GET /v1/transactions/{id}/download no caso de transação avulsa.
Entenda quem autentica o quê antes de depurar um 401
Duas credenciais, dois públicos e um mapa de rotas que evita a tarde perdida. Teste os dois fluxos no sandbox gratuito.
Criar credenciais de homologação Fale com o time comercial