Sessão de Assinatura Expirada: Reenviar, Cancelar ou Recriar (Runbook da API)
Toda sessão de assinatura tem prazo. Quando ele vence, o link deixa de funcionar, a plataforma marca a sessão como EXPIRED e avisa o seu sistema por webhook. A partir daí só existe um caminho: uma sessão nova. Este runbook organiza as decisões que a sua integração precisa tomar antes e depois desse momento: quando reenviar o convite, quando cancelar, quando recriar, o que dizer ao signatário e como detectar que a taxa de expiração está alta antes que vire um problema comercial.
O erro típico não é deixar sessões expirarem, isso é normal em qualquer fluxo com pessoas. O erro é o sistema não saber o que fazer quando isso acontece: jobs que tentam reenviar um link morto, contratos que ficam "pendentes" para sempre no CRM, signatários que recebem um link vencido. Cada um desses tem uma ação correta, e ela depende do estado exato da sessão.
O ciclo de vida, em uma linha
Uma sessão nasce ACTIVE na resposta de POST /v1/signing-sessions, com um campo expiresAt. Por padrão a janela é de 72 horas; você pode definir expiresInMinutes entre 5 minutos e 7 dias (10080) na criação, tanto em sessões quanto em envelopes. A partir daí há exatamente quatro desfechos, e os três últimos são finais:
| Estado | Como chega lá | Webhook | Reversível? |
|---|---|---|---|
ACTIVE |
Criação; o link está válido. | SIGNING_SESSION.CREATED |
É o único estado em que você ainda age. |
COMPLETED |
O signatário concluiu todas as etapas do perfil. | SIGNING_SESSION.COMPLETED |
Não. Baixe o documento e as evidências. |
CANCELLED |
Você chamou POST /v1/signing-sessions/{id}/cancel. |
SIGNING_SESSION.CANCELLED |
Não. |
EXPIRED |
expiresAt passou e a varredura da plataforma marcou a sessão. |
SIGNING_SESSION.EXPIRED |
Não. Só uma sessão nova. |
A expiração não é instantânea no segundo exato de expiresAt: um processo agendado roda a cada 15 minutos, encontra as sessões vencidas, marca-as como EXPIRED, invalida o link e dispara o webhook. A transação correspondente também passa a EXPIRED. Na prática isso significa duas coisas: o webhook pode chegar até um quarto de hora depois do prazo, e o seu sistema não deve calcular "expirou" só pelo relógio; a fonte de verdade é o status na API.
Por que EXPIRED é final
Um link de assinatura é, ao mesmo tempo, o endereço da página e a credencial que dá acesso a ela (o clientSecret vai na URL). Reabrir uma sessão vencida significaria reativar uma credencial que já circulou por e-mail, WhatsApp ou SMS por dias, e que pode ter sido encaminhada, copiada ou vazada. A plataforma prefere o comportamento previsível: o prazo que você definiu na criação é o prazo, e o que vem depois é uma sessão nova, com link novo, secret novo e trilha de auditoria própria. O mesmo raciocínio vale para CANCELLED.
Não existe "estender" nem "reativar". Nenhum endpoint altera expiresAt de uma sessão existente. Se a janela padrão de 72 horas está curta para o seu caso (contratos que dependem de uma reunião, signatários que viajam), a solução é criar a próxima sessão com expiresInMinutes maior, até o limite de 7 dias, e não tentar prolongar a atual.
Árvore de decisão
Antes de qualquer ação, consulte o estado real com GET /v1/transactions/{id} (ou o status da sessão). Depois siga o ramo correspondente.
Ainda ACTIVE: o signatário não assinou, mas o prazo não venceu
- Reenviar o convite:
POST /v1/signing-sessions/{id}/resend-invite. Dispara novamente o e-mail com o mesmo link. Exige que a sessão tenha o e-mail do signatário registrado (sem ele, a API devolve 409 dizendo isso) e é limitado a uma chamada por minuto: uma segunda tentativa antes disso devolve 409 com a mensagem "Aguarde Ns antes de reenviar". Trate o 409 como informação, não como erro a retentar em loop. - Entregar o link por outro canal: se o convite por e-mail não está chegando, o link (
url?cs=clientSecret) continua válido e pode ser enviado pelo seu próprio canal. Lembre-se de que, no perfilCLICK_ONLY, o link é a autenticação inteira: só entregue por um canal que você já sabe ser do signatário. - Desistir:
POST /v1/signing-sessions/{id}/cancel. Use quando o documento mudou, o negócio caiu ou você vai recriar com outro perfil de autenticação. Cancelar antes de recriar evita duas sessões vivas para o mesmo contrato.
EXPIRED: o prazo venceu
- Recriar a sessão. Uma nova chamada a
POST /v1/signing-sessions, com umX-Idempotency-Keynovo. Se você reaproveitar a chave da sessão original, a API devolve a resposta antiga (a sessão expirada) em vez de criar outra: a chave é lembrada por 24 horas. Uma convenção que funciona écontrato-{id}-tentativa-{n}. - O documento precisa ir de novo. A sessão nova gera uma transação nova, e ela precisa do seu próprio documento: reenvie o mesmo PDF em
document.contentou pelo fluxo de upload pré-assinado. Reenvie o arquivo original que você guardou, e não um PDF baixado de algum lugar; o hash que você registrou no primeiro envio deve ser o mesmo. - Considere uma janela maior ou um perfil diferente. Se a sessão expirou porque o signatário não conseguiu completar a biometria, por exemplo, a segunda tentativa pode ser criada com
BIOMETRIC_DOCUMENT_FALLBACKou, para casos de menor risco,CLICK_PLUS_OTP. A escolha do perfil é sua, por signatário. - Feche o registro antigo. No seu sistema, a tentativa expirada deve ficar registrada como tal (com o
transactionId), e o contrato deve apontar para a nova. Não sobrescreva; o histórico de tentativas é útil quando o cliente pergunta "por que recebi dois links".
CANCELLED ou COMPLETED
Nada a reenviar. Em COMPLETED, o próximo passo é o download do documento e das evidências. Em CANCELLED, o cancelamento foi seu; se foi indevido, o caminho é o mesmo do expirado: sessão nova.
Envelopes: expiração do conjunto versus de um signatário
Em um envelope (POST /v1/envelopes), o expiresInMinutes vale para o envelope inteiro. Quando ele vence, a plataforma emite ENVELOPE.EXPIRED, e as sessões que ainda estavam ACTIVE deixam de valer. Quem já tinha assinado não perde a assinatura como evidência, mas o documento consolidado com todas as partes não existe: o envelope não chegou a ENVELOPE.ALL_SIGNED.
As decisões mudam um pouco:
- Um signatário atrasado, envelope ainda válido:
resend-invitena sessão dele, com a mesma regra de um reenvio por minuto. No modoSEQUENTIAL, lembre-se de que o convite do próximo só sai quando o anterior assina; reenviar para quem ainda não é a vez não adianta. - Envelope expirado: um envelope novo, com todos os signatários de novo, inclusive os que já tinham assinado. É o preço de um documento único assinado por todos. Dimensione
expiresInMinutespensando no signatário mais lento da cadeia, não no primeiro; em fluxos sequenciais com três ou mais partes, os 7 dias costumam ser necessários. - Cancelar cedo: se um signatário informa que não vai assinar, cancele o envelope em vez de esperar a expiração. Isso libera o seu fluxo e evita convites a quem não deveria mais recebê-los.
Os detalhes de ordem, signerIndex e comportamento de cada modo estão em múltiplos signatários e ordem de assinatura.
O que dizer ao signatário
A mensagem que acompanha o segundo link decide se ele será assinado. Três regras:
- Diga que o link anterior venceu e que este é o novo. Quem recebe dois e-mails sem explicação tende a abrir o primeiro, ver um erro e desistir.
- Informe o prazo novo em data e hora locais, calculado a partir do
expiresAtdevolvido na criação. "Válido até sexta, 18h" converte melhor que "72 horas". - Não peça para "tentar de novo o mesmo link". Ele está morto por desenho; a página hospedada informa isso ao signatário, mas o convite do seu sistema não deve contradizer.
Se o mesmo signatário expira duas vezes, a causa raramente é técnica: e-mail errado, spam, ou a pessoa não reconhece o remetente. Vale um contato humano antes da terceira sessão.
Monitoramento: descobrir antes do cliente
Dois sinais valem o alerta:
- Taxa de
SIGNING_SESSION.EXPIREDsobre o total de sessões criadas, por dia e por tipo de documento. Um salto costuma indicar problema de entrega de e-mail (o seu domínio caiu em spam, o remetente mudou) ou um perfil de autenticação mais exigente do que o público consegue cumprir, e não signatários desinteressados. - Sessões
ACTIVEcom mais de dois terços da janela consumida. É o momento de reenviar o convite ou fazer contato, enquanto ainda há o que fazer. Como a plataforma não emite um aviso por sessão prestes a vencer, esse alerta é seu: um job periódico que lêexpiresAtdas sessões pendentes no seu banco.
Há também o evento TRANSACTION.DEADLINE_APPROACHING, emitido por uma verificação diária quando a transação tem um prazo de submissão registrado e faltam menos de 48 horas para ele. Ele vale para os fluxos que carregam esse prazo; não o use como substituto do seu próprio monitoramento de expiresAt.
Código: o ramo de reconciliação
O trecho abaixo é o worker que trata um contrato pendente, seja porque chegou SIGNING_SESSION.EXPIRED, seja porque o job de reconciliação encontrou um contrato parado há tempo demais. Ele consulta o estado real e escolhe o ramo. O desenho de fila e dedupe que o envolve está em webhooks em fila e reprocessamento.
Repare que o worker nunca decide pelo relógio local: ele lê o status na API e só então age. Isso o torna seguro para reprocessamento e indiferente ao atraso de até 15 minutos da varredura de expiração. Os códigos 409 aparecem em três lugares (reenvio dentro do minuto, sessão sem e-mail, sessão que não está mais ACTIVE), e nos três a resposta certa é registrar e seguir, não retentar; a lista completa está em códigos de erro e tratamento de falhas.
Checklist
- Guardo
expiresAtde cada sessão no meu banco e tenho um job que olha para ele. - Meu tratamento de
SIGNING_SESSION.EXPIREDeENVELOPE.EXPIREDfecha a tentativa e abre a próxima, sem sobrescrever o histórico. - Toda recriação usa um
X-Idempotency-Keynovo e reenvia o documento original guardado. - Reenvio de convite respeita o intervalo de um minuto e trata 409 como informação.
- Envelopes são criados com janela pensada para o signatário mais lento; em sequenciais longos, 7 dias.
- A mensagem ao signatário diz que o link anterior venceu e informa o prazo novo em hora local.
- Tenho um alerta para a taxa diária de expiração por tipo de documento.
Perguntas Frequentes
Quanto tempo um link de assinatura fica válido?
Por padrão, 72 horas a partir da criação da sessão. Na chamada POST /v1/signing-sessions (e em POST /v1/envelopes) você pode definir expiresInMinutes entre 5 minutos e 7 dias (10080). A resposta devolve expiresAt, que é o valor a guardar no seu banco e a mostrar ao signatário em hora local. Passado o prazo, um processo agendado que roda a cada 15 minutos marca a sessão como EXPIRED, invalida o link e dispara o webhook SIGNING_SESSION.EXPIRED; por isso o aviso pode chegar alguns minutos depois do horário exato.
Dá para estender ou reativar uma sessão que expirou?
Não. Nenhum endpoint altera o expiresAt de uma sessão existente, e uma sessão EXPIRED (ou CANCELLED) não volta a ACTIVE. O link carrega a credencial de acesso na própria URL e já circulou por e-mail ou mensagem; reativá-lo seria reativar uma credencial potencialmente encaminhada ou vazada. O caminho é criar uma sessão nova, com link e secret novos, e, se a janela padrão foi curta para o seu caso, criá-la com expiresInMinutes maior, até 7 dias.
Quando devo usar o resend-invite?
Somente enquanto a sessão está ACTIVE e o signatário ainda não assinou. POST /v1/signing-sessions/{id}/resend-invite reenvia o e-mail de convite com o mesmo link. Ele exige que a sessão tenha o e-mail do signatário registrado e aceita no máximo uma chamada por minuto; fora dessas condições a API devolve 409 com a explicação. Trate o 409 como informação e não retente em loop. Se a sessão já expirou, o reenvio devolve 409 também: o remédio é recriar, não reenviar.
Como recrio a sessão sem duplicar por engano?
Use um X-Idempotency-Key novo para a nova sessão. A chave da chamada original é lembrada por 24 horas: se você a reaproveitar, a API devolve a resposta antiga (a sessão expirada) em vez de criar outra. Uma convenção que funciona é contrato-{id}-tentativa-{n}. A nova sessão gera uma transação nova, então o documento precisa ser enviado de novo, de preferência o mesmo arquivo original que você guardou no primeiro envio, para que o hash registrado continue batendo.
O que acontece com um envelope quando ele expira?
O expiresInMinutes vale para o envelope inteiro. Ao vencer, a plataforma emite ENVELOPE.EXPIRED e as sessões ainda ACTIVE deixam de valer. Quem já assinou não perde a assinatura como evidência, mas o documento único com todas as partes não existe, porque o envelope não chegou a ENVELOPE.ALL_SIGNED. Para concluir, é preciso um envelope novo com todos os signatários. Por isso a janela deve ser dimensionada pelo signatário mais lento da cadeia; em fluxos sequenciais longos, os 7 dias costumam ser necessários.
Como percebo que muitas sessões estão expirando?
Acompanhe a taxa diária de SIGNING_SESSION.EXPIRED sobre o total de sessões criadas, por tipo de documento. Um salto costuma apontar entrega de e-mail (domínio em spam, remetente alterado) ou um perfil de autenticação mais exigente do que o público consegue cumprir. Complemente com um job que lê o expiresAt das sessões pendentes e alerta quando dois terços da janela passaram: é a hora de reenviar o convite ou fazer contato, enquanto ainda há o que fazer. A plataforma não emite um aviso por sessão prestes a vencer; esse monitoramento é seu.
Simule expiração e reenvio no sandbox
Crie sessões com expiresInMinutes de 5 minutos no ambiente de homologação, receba o SIGNING_SESSION.EXPIRED e teste o seu worker de reconciliação de ponta a ponta, sem cartão. Em produção, o plano é dimensionado com o time comercial.
Criar credenciais de homologação Fale com o time comercial