Um cliente precisava automatizar a gestão de assinaturas eletrônicas: criar e administrar usuários, disparar envelopes para assinatura e consultar o status — tudo a partir de um sistema interno, sem ninguém clicando em telas. Esse “sem ninguém clicando” é a parte importante: define como a aplicação se autentica.

Por que JWT Grant (e não o fluxo com login)

O DocuSign oferece alguns fluxos OAuth. O mais comum, Authorization Code Grant, exige que um humano abra o navegador e autorize o acesso. Serve para apps onde o próprio usuário final loga.

No nosso caso era uma integração servidor-a-servidor: um backend agindo em nome de um usuário, sem interface de login. Para isso existe o JWT Grant — a aplicação assina um token JWT com uma chave privada RSA e o troca por um access token, impersonando um usuário previamente autorizado.

O fluxo, no papel, é curto:

  1. Você cria uma Integration Key (o client id) no painel do DocuSign.
  2. Gera um par de chaves RSA; a pública vai no painel, a privada fica no servidor.
  3. O backend monta um JWT, assina com a privada e pede o access token, informando qual usuário quer impersonar.
  4. Uma única vez, um admin precisa dar consent ao escopo impersonation.

A documentação termina mais ou menos aqui. A realidade tem mais seis capítulos.

1. ActiveOnly não é um status válido

Ao listar usuários, é intuitivo filtrar pelos ativos com algo como status=ActiveOnly. A API aceita a chamada e devolve erro — porque o valor correto é simplesmente Active. Pequeno, mas custa uma sessão de debug achando que o problema é de permissão.

2. O container não conseguia ler a chave privada

Rodando a aplicação em container, a chave privada estava montada com permissão 600 e dono root — o reflexo de segurança correto. Só que o processo dentro do container roda com um UID não-root (no nosso caso, 10001), que não é dono do arquivo e portanto não consegue lê-lo.

O ajuste: dar a posse do arquivo ao UID do processo e manter a permissão restrita — chown 10001:10001 + chmod 400. Continua protegido, e agora quem precisa, lê.

3. Redirect URI é obrigatório — mesmo sem redirecionamento

JWT Grant não usa redirecionamento de navegador. Mesmo assim, o painel do DocuSign exige um Redirect URI cadastrado na aplicação. Sem ele, o fluxo de consent quebra. Cadastre uma URL qualquer válida e siga em frente.

4. Configurações de homologação NÃO migram para produção

Esse é o que mais dói. Toda a integração foi construída e testada no ambiente de demonstração (account-d.docusign.com). Quando o DocuSign promove sua aplicação para produção (account.docusign.com), o código da aplicação migra — mas as configurações (Redirect URI, chave pública RSA, tipo de integração) não.

Resultado: você jura que está tudo configurado, porque configurou — só que em demo. Em produção, a aplicação chega “pelada”. É preciso reconfigurar tudo manualmente no painel de produção depois do go-live.

5. no_valid_keys_or_signatures quase nunca é sobre a assinatura

Em produção, batemos no erro invalid_grant: no_valid_keys_or_signatures. O nome sugere problema na chave ou na assinatura do JWT — e você vai perder tempo conferindo a RSA.

Na prática, esse erro aparece quando falta a chave pública cadastrada OU falta o consent no ambiente de produção (lembra do capítulo 4?). Não confunda com consent_required, que é o erro “limpo” de consent pendente. O no_valid_keys_or_signatures é o sintoma indireto da configuração que não migrou.

6. Deletar um usuário não o apaga

Por fim, um detalhe de modelagem: um DELETE em um usuário não remove o registro — ele passa para o status Closed. O e-mail daquele usuário fica “reservado”, o que significa que recriar alguém com o mesmo e-mail tem comportamento próprio. Útil saber antes de assumir que “deletar” zera tudo.

O que fica de lição

Integração de API raramente falha na “lógica” — ela falha nos detalhes de ambiente, permissão e provisionamento que a documentação trata como óbvios. O maior buraco aqui foi o demo → produção: tudo funcionava, e ainda assim nada funcionava no dia da virada, porque metade da configuração mora num lugar que não viaja junto com o código.

Quando montamos uma integração para um cliente, é justamente esse trecho — a passagem para produção e os modos de falha silenciosos — que tratamos com mais cuidado. É onde o “funciona na minha máquina” cobra o preço.