API REST: o SignSession Contract
O Servidor implementa o SignSession Contract, a API que uma aplicação cliente chama para abrir uma Sessão de Assinatura vinculada a um CPF, opcionalmente em nome de uma Empresa, e assinar documentos nela. Esta página trata da autenticação, do ciclo de vida da sessão, do PIN por transação e do PIN dispensado na criação, dos documentos assinados com o REST PKI Core, de cada operação (inclusive a Assinatura de hash), do envelope de erro e de qual documento é a especificação do contrato.
Com o Servidor em execução, o contrato legível por máquina é servido em /openapi/v1.json (OpenAPI
3.1) e uma referência navegável em /scalar. Os dois respondem sem credencial. Esta página é o
guia conceitual; o documento publicado é a fonte de verdade para os detalhes de cada campo.
Versão do contrato e caminho base
Toda operação é servida sob /v1, e nada mais responde: uma chamada a uma rota sem o prefixo é
recusada com 404 ENDPOINT_NOT_FOUND. v1 é a versão do contrato, a única à qual um cliente deve
se fixar. O campo version do documento OpenAPI é a versão do build do Servidor e não diz nada sobre
compatibilidade de protocolo.
Autenticação
Toda operação exige o header X-API-Key, com uma chave de API emitida para uma Aplicação
Cliente no console administrativo. Qualquer uma das chaves de uma aplicação autentica toda a
superfície; a chave não restringe nada dentro dela. Chaves ausentes, desconhecidas, revogadas,
expiradas e de aplicações desativadas são todas recusadas da mesma forma, com 401 UNAUTHORIZED.
Trate um sessionId e um signatureId como tokens de portador (bearer tokens) daquilo que
identificam: qualquer chave aceita pode usar uma sessão ou buscar um documento cujo id ela tenha.
Toda resposta, inclusive as de erro, traz um X-Correlation-Id gerado pelo Servidor. Informe-o
ao relatar um problema, junto com o code do erro; ele leva o operador a toda a trilha da requisição,
em que a Trilha de Auditoria registra o código com que cada chamada foi respondida (Servidor 1.24.0).
O ciclo de vida da sessão
POST /v1/sessions (cpf, pin[, cnpj]) ──▶ 201 { sessionId, expiresIn, transactionPin, certificate, chain, … }
│
├──▶ GET /v1/sessions/status?sessionId=… is it still active? (never extends it)
├──▶ GET /v1/certificates/info?sessionId=… which certificate, which verdict
├──▶ POST /v1/signatures (sessionId, document[, pin]) ──▶ 202 { signatureId, fileName }
├──▶ POST /v1/signatures/batch (sessionId, files[][, pin]) ──▶ 202 { results[] }
│ └──▶ GET /v1/signatures/{signatureId}/document ──▶ 200 PDF | 409 | 422
├──▶ POST /v1/signatures/hashes (sessionId, digestAlgorithm, hashes[][, pin]) ──▶ 200 { signatures[] }
│ (Client Applications granted Hash Signing only)
└──▶ POST /v1/sessions/deactivate (sessionId) ──▶ 200
- Crie uma sessão com o CPF e o PIN do titular, e com um CNPJ para assinar em nome de uma Empresa. O Servidor tenta primeiro os Certificados do Cofre guardados para esse CPF. Se nenhum servir, as estações de trabalho que têm um certificado do CPF são solicitadas a provar que têm o PIN em uso ali, e o Servidor confere cada prova contra o PIN que você enviou. O PIN não vai além do Servidor: nenhuma estação de trabalho o recebe, e ele nunca é apresentado ao token. Se nenhuma estação de trabalho tiver esse PIN em cache, o titular recebe uma solicitação no Tray App e esta requisição fica aberta até que ele responda ou a solicitação expire (60 s por padrão). Leve isso em conta no timeout do seu cliente. O certificado que confirmar o PIN é o que fica vinculado à sessão. Uma Aplicação Cliente que um administrador passou para o PIN dispensado na criação pode omitir o PIN, e então nada é pedido a ninguém na criação.
- Assine na sessão, um documento por vez ou em lote. A assinatura inteira acontece dentro da requisição e normalmente leva alguns segundos.
- Busque o PDF assinado pelo id da assinatura. É também aí que se lê o resultado de uma assinatura.
- Desative a sessão quando o trabalho terminar, ou deixe a janela de inatividade se esgotar.
Há no máximo uma sessão ativa por CPF, ou por CPF e CNPJ numa sessão em nome de uma Empresa; criar
outra para o mesmo par substitui a anterior. Assinar estende a janela de inatividade; consultar o
status, não. Uma sessão também termina quando a Franquia de Assinaturas dela se esgota: o número de
chamadas de assinatura que um administrador do Servidor pode definir para a Aplicação Cliente que
criou a sessão (Servidor 1.17.0; antes dele, um valor único para toda a implantação). Nada numa
resposta diz se uma sessão tem franquia, então esteja pronto para SESSION_EXPIRED antes de
expiresAt.
Uma sessão vinculada a um Certificado do Cofre se apoia em um segredo guardado apenas na memória
do Servidor, então reiniciar o Servidor a encerra: o cliente recebe SESSION_EXPIRED na chamada
seguinte e se recupera criando uma nova sessão com o mesmo PIN. Uma sessão vinculada a uma estação de
trabalho sobrevive ao mesmo reinício.
Uma sessão vinculada a uma estação de trabalho que um administrador remove da plataforma (Servidor
1.21.0) nunca mais assina: toda chamada de assinatura nela responde 422 WORKSTATION_OFFLINE com
details workstation removed, e ela expira sozinha, então crie uma nova. Uma chamada já em andamento
quando a máquina é removida pode responder sem esse details; a seguinte o traz. Uma máquina que só
está desconectada responde o mesmo código sem ele, e a sessão dela volta a assinar quando ela voltar.
A partir do Servidor 1.15.0, uma estação de trabalho precisa do Workstation 1.5.0 ou posterior
para se conectar, e do Workstation 1.6.0 para atender a uma sessão que pede o PIN a cada chamada
de assinatura (PIN por transação). Para uma Aplicação Cliente com esse modo
ligado, uma máquina ainda no 1.5.0 conta como offline, então um titular cujas máquinas tenham todas
essa versão recebe 422 WORKSTATION_OFFLINE até que uma delas seja atualizada. Todas as outras sessões
continuam sendo atendidas pelo 1.5.0, como antes. Veja
Instalação da estação de trabalho.
PIN por transação
Uma Aplicação Cliente pode ser configurada para pedir o PIN do titular a cada chamada de assinatura, e não só na criação da sessão. É uma extensão do TrustBridge (Servidor 1.15.0) e fica desligada até que um administrador do Servidor a ligue para a sua Aplicação Cliente: peça ao operador da implantação, informando o nome da aplicação. Ela é definida por Aplicação Cliente, nunca por chave de API, e vale só para as sessões criadas depois de ligada. Desligá-la de novo, da mesma forma, só muda as sessões criadas depois disso.
A criação da sessão não muda: POST /v1/sessions continua recebendo o pin do titular e conferindo-o
como antes, a menos que a aplicação também tenha o PIN dispensado na criação.
O que muda é a assinatura. Toda sessão responde transactionPin, em POST /v1/sessions
e em GET /v1/sessions/status enquanto está ativa, e o valor dele decide o que a sua tela mostra:
transactionPin | Onde está o certificado | O que a sua aplicação faz a cada chamada de assinatura |
|---|---|---|
null | Em qualquer lugar | Nada de novo. A sessão autoriza as próprias assinaturas, como toda sessão faz sem essa opção. Não envie pin. |
"workstation" | Na máquina do titular | Avise o usuário para olhar o Tray App e não envie pin. Cada chamada aguarda enquanto o Tray mostra o nome da sua Aplicação Cliente e o que a chamada assina (os nomes dos documentos, ou o número de hashes) e pede o PIN. Um lote pede uma única vez para a chamada inteira. |
"client" | No Cofre de Certificados do Servidor, onde não há a quem mostrar uma solicitação | Mostre um campo de PIN a cada transação e envie o que for digitado como pin em cada chamada de assinatura: um membro JSON, ou um campo de formulário pin ao lado dos documentos de um lote. Ele é conferido uma vez por chamada, antes de qualquer assinatura, e não é guardado para nada depois. |
Com "workstation", a espera pelo titular é limitada pelo Servidor (Signing:AuthorizationTimeout,
60 s por padrão) e não é descontada do tempo dado à própria assinatura, então considere as duas no
timeout do seu cliente. Uma máquina ausente quando a chamada chega é aguardada primeiro por um breve
período (Signing:WorkstationReconnectGrace, 15 s por padrão).
As recusas desse modo são respondidas antes de qualquer assinatura: nada é criado no serviço de assinatura, e a mesma requisição pode ser reenviada depois que a causa for tratada.
| Resposta | Significado | O que fazer |
|---|---|---|
400 INVALID_REQUEST | Um pin foi enviado numa sessão cujo transactionPin é null ou "workstation", ou nenhum foi enviado na chamada de uma sessão "client". | Corrija o cliente. Um pin numa sessão comum era ignorado e agora é recusado, para que um erro de integração apareça na hora. |
422 PIN_NOT_AVAILABLE, details declined | O titular recusou a chamada no Tray. | Não a reenvie sem que ele peça. |
422 PIN_NOT_AVAILABLE, details timed out | Ninguém respondeu a tempo. | Peça ao usuário que esteja diante da máquina dele e tente de novo. |
422 PIN_NOT_AVAILABLE, details no Tray at the console | Nada no console da máquina conseguiu exibir o pedido de PIN: o Tray App não está em execução para quem está conectado ali, é antigo demais para exibir uma chamada de assinatura, ou a máquina estava iniciando uma atualização. | Peça ao usuário que entre na máquina e abra o TrustBridge pelo menu Iniciar (Workstation 1.9.0), ou atualize o Tray App. Esses details não dizem qual foi a causa; o operador do Servidor a vê no console administrativo. |
422 PIN_NOT_AVAILABLE, details token locked | O PIN do token está bloqueado; só o PUK dele o desbloqueia. | Reenviar não muda nada. |
422 PIN_NOT_AVAILABLE, details no A1 PIN at the workstation | O certificado é um arquivo A1 e a máquina não tem um PIN do Certificado A1 definido para ele, ou não consegue ler os que tem. | Peça ao usuário que defina um no Tray App. |
422 PIN_NOT_AVAILABLE, details too many calls waiting | A máquina do titular já tem tantas chamadas aguardando no Tray quanto o Servidor permite (duas por padrão), então esta não foi exibida. | Envie uma chamada por vez por titular, e reenvie esta depois que aquelas forem respondidas. |
422 PIN_NOT_AVAILABLE com "client" | Não foi possível abrir o certificado: ele está em espera depois de PINs errados demais, o que passa com o tempo, ou não pode ser aberto neste Servidor, o que é assunto do operador. details diz qual. | O que details indicar. |
422 WORKSTATION_OFFLINE | A máquina do titular não está conectada, tem uma versão antiga demais para pedir o PIN, ou caiu enquanto o titular estava diante da solicitação. | A sessão continua válida; ela volta a assinar quando a máquina se reconectar. |
401 INVALID_PIN com "client" | O pin estava errado. | Deixe o usuário digitá-lo de novo. Ele conta para o bloqueio por PINs incorretos do CPF, como um pin errado na criação. |
429 TOO_MANY_PIN_ATTEMPTS com "client" | Esse bloqueio está em vigor. | details informa o limite e a janela; nenhum PIN é tentado até que as falhas mais antigas saiam da janela. |
Um caso é respondido mais tarde: uma chamada que perde a autorização no meio do caminho, porque a
máquina do titular caiu e voltou, ou porque o token dela recusou, na hora de assinar, o PIN digitado
para a chamada. Também é PIN_NOT_AVAILABLE, mas respondido onde qualquer falha nesse ponto é: depois
que o serviço de assinatura recebeu o documento (um documento individual é então respondido com 202
e o id dele, e um item de lote traz o erro), ou depois que alguns hashes foram assinados, o que a
recusa informa. O details dele é a frase da própria máquina.
Mantenha uma sessão de longa duração por usuário nesse modo, em vez de uma por transação. Peça um
sessionDuration até o máximo da implantação (Sessions:MaxDuration, 24 horas por padrão; leia a
janela concedida em expiresIn) e crie uma nova sessão só quando uma chamada responder
SESSION_EXPIRED. A sessão não autoriza mais nada sozinha, então uma sessão longa não deixa nada mais
aberto do que uma curta deixaria. Uma sessão curta, ao contrário, custa ao titular: com
"workstation", criar uma sessão num token cujo PIN a máquina não tem em cache também abre uma
solicitação de PIN, então uma sessão por transação pode significar duas solicitações para uma única
assinatura. Uma sessão criada sem PIN não pede nada a ninguém na criação, então ali o custo é só a
própria chamada de criação.
PIN dispensado na criação
Uma Aplicação Cliente com o PIN por transação ligado também pode ser configurada para criar as
sessões dela sem o PIN (Servidor 1.18.0). É uma segunda opção ao lado da primeira, também uma
extensão do TrustBridge, também definida por um administrador do Servidor para a sua Aplicação
Cliente, e só fica ligada enquanto o PIN por transação estiver: desligar o PIN por transação a desliga
junto. Com ela, o pin em POST /v1/sessions pode ser omitido, então, para um certificado na máquina
do titular, a sua aplicação só coleta o PIN quando mais de um certificado poderia atender a sessão
(abaixo); fora isso, o titular o digita no Tray App a cada chamada de assinatura, e em nenhum outro
lugar. Para qualquer outra aplicação, uma requisição sem pin é recusada com 400 INVALID_REQUEST,
pin is required., como sempre foi.
Uma criação sem pin se vincula ao único certificado que poderia atender a sessão, contando só as
máquinas conectadas naquele momento, sem pedir nada a ninguém: nenhum pedido de PIN, nenhum desafio,
nenhuma abertura do cofre, e nada conta para o bloqueio por PINs incorretos. Ela responde
transactionPin como qualquer outra sessão, "workstation" ou "client", e uma sessão "client"
continua precisando de pin em toda chamada de assinatura. As recusas dela vêm na ordem de sempre:
429 TOO_MANY_PIN_ATTEMPTS enquanto o CPF está bloqueado, com ou sem pin, depois
404 CERT_NOT_FOUND, 422 CERT_NOT_ACTIVE e 422 WORKSTATION_OFFLINE. 401 INVALID_PIN e
422 PIN_NOT_AVAILABLE só são respondidos a uma requisição que enviou um pin.
Quando mais de um certificado poderia atender a sessão, a requisição é recusada com
400 INVALID_REQUEST e este details:
pin is required. More than one certificate could serve this session, and the one the PIN opens is the one it binds to.
Isso acontece quando o certificado do titular está em duas máquinas, quando o Cofre de Certificados do
Servidor guarda um ao lado de outro numa máquina, ou quando um cartão antigo e a renovação dele ainda
estão válidos. Trate essa recusa pedindo o PIN: peça o PIN ao usuário e envie a mesma requisição
com ele, e o certificado que ele abrir é o que fica vinculado à sessão. Espere por ela pelo menos
enquanto o cartão antigo e o renovado de um titular estiverem válidos. Um pin enviado é conferido
exatamente como para qualquer outra aplicação. Omita o campo em vez de enviá-lo vazio, o que é
recusado.
Uma sessão vinculada dessa forma ainda pode falhar na primeira chamada de assinatura quando nada na
máquina consegue autorizá-la, por exemplo 422 PIN_NOT_AVAILABLE com details
no A1 PIN at the workstation ou no Tray at the console; a recusa diz o que corrigir. Veja o que essa
opção aceita: uma chave de uma aplicação assim pode criar uma sessão para qualquer CPF que tenha um
certificado na plataforma, ler esse certificado por GET /v1/certificates/info e substituir a sessão
ativa desse CPF, tudo sem o PIN, embora não consiga assinar nada sem o titular. Veja
Segurança.
Documentos assinados com o REST PKI Core
Um administrador do Servidor pode configurar uma Aplicação Cliente para que os documentos dela sejam
assinados com o REST PKI Core, o serviço de assinatura de PDF da Lacuna, no lugar do Lacuna Signer
(Servidor 1.18.0). A opção fica desligada até que o administrador a ligue para essa aplicação. O
protocolo é o mesmo nos dois casos, e nenhum campo diz qual serviço assinou: o mesmo 202, os mesmos
resultados de lote e a mesma busca. O que muda está por trás:
- Qual serviço assina é lido da aplicação cuja chave de API faz cada chamada de assinatura, e não
da que criou a sessão, então uma mudança vale a partir da próxima chamada de assinatura dessa
aplicação. Ela rege
POST /v1/signaturesePOST /v1/signatures/batch; a Assinatura de hash não é afetada. - O
signatureIdé o id do próprio REST PKI Core para o documento assinado, um GUID como o do Signer, e o REST PKI Core guarda esse documento: a busca o lê de lá, então o tempo durante o qual ele pode ser buscado é a retenção do REST PKI Core. Os ids que o Signer gerou, antes ou depois de uma mudança, continuam sendo buscados no Signer, como antes. - A chave do titular faz exatamente uma assinatura, como antes, sob as mesmas regras de custódia,
de PIN e do Tray App, e uma chamada gasta uma unidade da Franquia de Assinaturas. Nenhum endereço de
e-mail é necessário: um certificado que não traz nenhum assina, e um que não identifica titular
nenhum é recusado com
422 CERT_INCOMPLETE_IDENTITY. - O REST PKI Core também valida o certificado, com as próprias configurações de confiança, antes
que qualquer coisa seja pedida ao titular. Um certificado que ele não valida é
422 CERT_NOT_ACTIVE, comdetailsREVOKED,EXPIREDouUNTRUSTED; nada é assinado e nenhuma unidade da franquia é gasta. O veredito dele pode discordar do da plataforma. - Qual política de assinatura é aplicada, e se marcas são colocadas nas páginas, são configurações do REST PKI Core que a implantação usa, e não do TrustBridge.
O id só existe depois que o REST PKI Core responde o documento assinado, então nunca se responde
um id para um documento que ele não assinou, e toda falha antes disso é uma recusa, e não um 202,
mesmo uma que venha depois de a chave do titular ter assinado:
| Resposta | Significado |
|---|---|
422 SIGN_TIMEOUT | O tempo acabou. |
422 CERT_NOT_ACTIVE | O REST PKI Core não validou o certificado: antes de qualquer coisa ser pedida ao titular ou, raramente, para um certificado cuja situação mudou enquanto a chave do titular assinava. |
422 UNSUPPORTED_DOCUMENT | Um PDF que o REST PKI Core não consegue processar: malformado, protegido por senha, já assinado onde ele está configurado para recusar um assim, ou um que ele não consegue converter em PDF/A onde está configurado para isso. details diz qual, nas palavras do próprio REST PKI Core. |
502 SIGNER_ERROR | O REST PKI Core está inacessível, falhando, limitando requisições ou sem cota no plano contratado. |
500 INTERNAL_ERROR | Uma falha do próprio Servidor, ou da forma como ele está configurado para chegar ao REST PKI Core. |
Uma recusa depois de a chave do titular ter assinado não garante nada, num sentido ou noutro: a
unidade da Franquia de Assinaturas da chamada foi gasta, e o REST PKI Core pode ainda ter guardado um
documento assinado que nenhum id identifica, então reenviar o documento o assina de novo. Um
INTERNAL_ERROR diz isso na mensagem quando a chave já tinha assinado. Num lote, cada uma dessas é o
ERROR do item, exceto uma falha antes de qualquer assinatura (o certificado recusado, o REST PKI Core
falhando ou sem tempo enquanto valida o certificado, ou inacessível do jeito que o Servidor está
configurado para chegar a ele), que recusa a chamada inteira. Um item de lote só fica PROCESSING
depois que o REST PKI Core responde o documento assinado dele. A busca de um documento que o REST PKI
Core não tem mais responde 422 DOCUMENT_UNAVAILABLE sem details.
Operações
POST /v1/sessions: criar uma Sessão de Assinatura
curl -s https://trustbridge.example.com/v1/sessions \
-H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
-d '{ "cpf": "12345678909", "pin": "1234", "sessionDuration": 3600, "email": "holder@example.com" }'
| Campo | ||
|---|---|---|
cpf | obrigatório | Onze dígitos, sem pontuação. Os dígitos verificadores são conferidos, e os onze valores de dígitos repetidos são recusados, com 400 INVALID_REQUEST. Numa sessão em nome de uma Empresa, continua sendo o CPF da pessoa: o responsável pelo e-CNPJ da Empresa. |
pin | obrigatório* | O PIN do certificado, qualquer que seja a custódia. Para um Certificado do Cofre, a senha que abre o arquivo dele. *Só pode ser omitido por uma Aplicação Cliente com o PIN dispensado na criação ligado; nunca é enviado vazio. |
cnpj | opcional | A Empresa em nome da qual assinar, com o e-CNPJ dela. Catorze caracteres, sem pontuação e em maiúsculas: doze dígitos ou letras (o CNPJ alfanumérico é aceito tanto quanto o numérico) seguidos de dois dígitos verificadores. Os dígitos verificadores são conferidos, e doze repetições de um mesmo caractere são recusadas, com 400 INVALID_REQUEST. Se omitido, a sessão é da pessoa, como antes. |
sessionDuration | opcional | Em segundos. Se omitido, vale a janela padrão do Servidor; acima do máximo, o valor é reduzido ao máximo, não recusado. |
email | opcional | Um endereço alternativo do signatário, usado apenas quando o certificado vinculado não traz nenhum. Sem ele, um certificado assim é recusado no momento da assinatura com CERT_INCOMPLETE_IDENTITY. |
{
"sessionId": "8f3c…", "cpf": "12345678909", "expiresIn": 3600, "expiresAt": "2026-09-21T15:04:05Z",
"transactionPin": null, "certificate": "MIIH…", "chain": ["MIIG…", "MIIF…"]
}
expiresIn é sempre a janela efetivamente concedida. transactionPin diz se toda chamada de
assinatura precisa do PIN para aquela chamada (PIN por transação); ele está
sempre presente, como null quando não se aplica. certificate é a codificação DER do certificado
vinculado, em base64, e chain são os emissores dele, do mais próximo ao mais distante, até a raiz
inclusive, como a plataforma os registrou ao validar o certificado pela última vez (Servidor 1.14.0).
Com eles, um cliente consegue montar uma estrutura assinada antes de assinar qualquer coisa.
certificate é null quando a plataforma não tem a codificação do certificado, e a assinatura numa
sessão assim é então recusada com CERT_DATA_UNAVAILABLE. chain nunca é omitido nem null: é []
quando não há cadeia registrada, o que hoje sempre acontece com um certificado guardado numa estação de
trabalho. Recusas:
400 INVALID_REQUEST, comdetailscomeçando porpin is required.: nenhumpin, vindo de uma aplicação sem o PIN dispensado na criação, ou, vindo de uma com ele, mais de um certificado poderia atender a sessão.404 CERT_NOT_FOUND: nenhum certificado para o CPF (e CNPJ) em lugar nenhum.422 CERT_NOT_ACTIVE: todo certificado do CPF (e CNPJ) tem um veredito diferente de ativo.401 INVALID_PIN: todo certificado candidato foi tentado e nenhum aceitou o PIN. Só quando umpinfoi enviado.422 PIN_NOT_AVAILABLE: não foi possível fazer nenhuma comparação. Uma solicitação expirou, foi recusada ou não pôde ser exibida; um Certificado do Cofre não pôde ser aberto; ou tentativas recentes demais contra o certificado não conferiram, e ele só volta a ser consultado quando elas saírem da janela.detailsdiz qual. Só quando umpinfoi enviado: uma criação sem ele não compara nada.422 WORKSTATION_OFFLINE: toda máquina com um certificado ativo está offline ou, para uma Aplicação Cliente com o PIN por transação ligado, tem uma versão do Workstation anterior à 1.6.0. Comdetailsworkstation removed, a máquina escolhida foi removida da plataforma enquanto a sessão era criada: nenhuma sessão foi criada nem substituída, e a sessão que o CPF (e CNPJ) já tinha continua valendo.429 TOO_MANY_PIN_ATTEMPTS: bloqueio por CPF, com ou sempin. Uma criação sem ele nunca conta para o bloqueio.
Sessão em nome de uma Empresa
Envie cnpj junto com cpf para assinar com o e-CNPJ de uma Empresa. O cpf continua sendo o da
pessoa: o responsável por esse e-CNPJ, cujo PIN é este.
curl -s https://trustbridge.example.com/v1/sessions \
-H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
-d '{ "cpf": "12345678909", "cnpj": "12ABC34501DE35", "pin": "1234" }'
{ "sessionId": "c71d…", "cpf": "12345678909", "cnpj": "12ABC34501DE35", "expiresIn": 3600, "expiresAt": "2026-09-21T15:04:05Z" }
- A correspondência é exata. Com
cnpj, só um e-CNPJ que traga esse CNPJ e esse CPF pode vincular a sessão, nunca o e-CPF da própria pessoa. Sem ele, só o e-CPF da pessoa pode, nunca um e-CNPJ, mesmo que traga o mesmo CPF. - Uma sessão ativa por CPF e CNPJ. A sessão da pessoa e a da Empresa dela convivem, e as de dois responsáveis pela mesma Empresa também. Encerrar uma não afeta a outra.
- O bloqueio por PIN continua por CPF, qualquer que seja o
cnpjenviado, ou nenhum: um mesmo cartão costuma guardar os dois certificados atrás de um só PIN, então as falhas em qualquer um deles contam juntas. - As respostas devolvem o
cnpjsempre que trazem o CPF da sessão: na criação, na desativação e na descrição do certificado. Uma sessão sem ele responde sem nenhum membrocnpj(nem comnull), então uma integração que nunca o envia não vê nada de novo.
GET /v1/sessions/status?sessionId=…: verificar uma sessão
Responde o estado da sessão e quanto tempo ela ainda tem. Um id que não identifica nada ativo ainda
responde 200 com active: false; omitir o id resulta em 400. Pode ser consultado periodicamente
sem risco; nunca estende a janela. Não há como perguntar se um CPF tem uma sessão: um cliente que
perdeu o id cria uma nova sessão.
{ "active": true, "sessionId": "8f3c…", "expiresIn": 3412, "expiresAt": "2026-09-21T15:04:05Z", "transactionPin": null }
transactionPin é o valor que a criação respondeu. Ele está presente sempre que a sessão está ativa,
e ausente quando o id não identifica nada ativo.
POST /v1/sessions/deactivate: encerrar uma sessão
curl -s https://trustbridge.example.com/v1/sessions/deactivate \
-H "X-API-Key: $API_KEY" -H "Content-Type: application/json" -d '{ "sessionId": "8f3c…" }'
Encerra a sessão imediatamente e descarta o segredo em que ela se apoiava (o PIN que uma estação de
trabalho tinha em cache para ela, ou a senha que o Servidor guardava para abrir um Certificado do
Cofre). Não é idempotente: sem nada ativo, responde 401 SESSION_EXPIRED. A resposta informa o
CPF lido da sessão (e o cnpj dela, numa sessão em nome de uma Empresa), o id, active: false e
endedAt.
GET /v1/certificates/info?sessionId=…: descrever o certificado vinculado
O certificado ao qual a sessão está vinculada, como a plataforma o registrou pela última vez: cpf
(o titular do certificado, que não precisa ser igual ao CPF da sessão; num e-CNPJ, o CPF do
responsável pelo certificado em nome da Empresa), cnpj (só num e-CNPJ), subjectName, issuer,
serialNumber, notBefore, notAfter e status e, a partir do Servidor 1.14.0, a codificação DER
do certificado como certificate e os emissores dele como chain, como a criação da sessão os
responde. Verifique status antes de assinar: um veredito pode mudar enquanto a sessão está ativa,
e só ACTIVE pode assinar. Exige uma sessão ativa; nunca a estende. 404 CERT_NOT_FOUND significa
que o certificado vinculado não está mais disponível: um token foi retirado, o certificado foi
excluído, ou a estação de trabalho que o guardava foi removida da plataforma, caso que uma chamada de
assinatura na mesma sessão responde com WORKSTATION_OFFLINE.
POST /v1/signatures: assinar um documento
Há dois formatos de corpo. JSON, com o documento codificado em base64:
curl -s https://trustbridge.example.com/v1/signatures \
-H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
-d "{ \"sessionId\": \"8f3c…\", \"document\": \"$(base64 -w0 contract.pdf)\", \"fileName\": \"contract.pdf\" }"
Ou multipart, com um campo sessionId e o PDF como parte de arquivo (o nome do arquivo é tirado da
parte):
curl -s https://trustbridge.example.com/v1/signatures \
-H "X-API-Key: $API_KEY" \
-F "sessionId=8f3c…" -F "document=@contract.pdf"
Qualquer parte de arquivo é lida como o documento, seja qual for o nome do campo. Só uma é assinada:
quando a requisição traz várias, vence document, depois file, depois a primeira que chegou.
Responde 202:
{ "signatureId": "a1b2…", "fileName": "contract.pdf", "status": "PROCESSING", "acceptedAt": "2026-09-21T14:10:00Z" }
fileName é o nome com que o Servidor assinou o documento, que nem sempre é o enviado: o fileName
da requisição, ou o nome da própria parte de arquivo (filename* quando a parte traz um);
document.pdf quando esse nome está ausente ou vazio; e cortado em 260 caracteres, o que pode levar a
extensão junto. Quando uma requisição multipart trouxe várias partes de arquivo, ele indica qual foi
assinada.
Um 202 não significa "deu certo". Depois que o documento chega ao serviço de assinatura, esta
operação responde o id aconteça o que acontecer em seguida, porque o id é o único caminho de volta a
um documento que pode ter sido assinado. O resultado é lido na busca. signatureId é o id do
documento no próprio Lacuna Signer ou, para uma Aplicação Cliente cujos documentos são assinados com o
REST PKI Core, o do REST PKI Core, respondido só depois que
o REST PKI Core responde o documento assinado. Só PDFs são assinados (422 UNSUPPORTED_DOCUMENT);
um documento acima de Signing:MaxDocumentBytes (10 MB por padrão, medidos sobre os bytes
decodificados) resulta em 413 DOCUMENT_TOO_LARGE; nenhum documento resulta em
400 INVALID_DOCUMENT; um sessionId ausente resulta em 400 INVALID_REQUEST.
Numa sessão por transação, antes de o documento chegar ao serviço de
assinatura, a chamada também traz pin quando transactionPin é "client" (um membro JSON, ou um
campo pin ao lado do documento multipart), ou aguarda o titular no Tray quando é "workstation".
Duas respostas do serviço de assinatura significam coisas opostas. 422 SIGNER_REJECTED é o serviço
recusando a requisição (em geral o documento, às vezes outra coisa que a requisição indicou; details
diz qual): reenviá-la sem mudança traz a mesma resposta. 502 SIGNER_ERROR é o serviço inacessível,
limitando requisições ou falhando antes de o documento chegar a ele: nada foi assinado, e o mesmo
documento pode ser reenviado quando o serviço voltar. Essa última garantia é a do Signer: para um
documento assinado com o REST PKI Core, SIGNER_ERROR pode vir depois de a chave do titular ter
assinado, e então não garante nada.
POST /v1/signatures/batch: assinar vários documentos
Somente multipart/form-data: um campo sessionId e uma parte de arquivo por documento, até
Signing:MaxBatchItems (50 por padrão).
curl -s https://trustbridge.example.com/v1/signatures/batch \
-H "X-API-Key: $API_KEY" \
-F "sessionId=8f3c…" -F "files=@a.pdf" -F "files=@b.pdf" -F "files=@c.pdf"
Responde 202 com um resultado por documento, na ordem em que as partes chegaram:
{
"results": [
{ "fileName": "a.pdf", "signatureId": "a1b2…", "status": "PROCESSING", "error": null },
{ "fileName": "b.pdf", "signatureId": null, "status": "ERROR",
"error": { "code": "DOCUMENT_TOO_LARGE", "message": "…" } },
{ "fileName": "c.pdf", "signatureId": "c3d4…", "status": "PROCESSING", "error": null }
],
"acceptedAt": "2026-09-21T14:10:00Z"
}
Cada documento é independente: um que falhou traz status: "ERROR" e um código do mesmo vocabulário,
enquanto os demais trazem ids. Leia os resultados, não o código de status. Cada item, com falha ou
não, recebe o nome pela mesma regra do fileName da assinatura individual. A chamada inteira só falha
por algo que valha para todos eles: nenhum sessionId (400 INVALID_REQUEST), nenhuma sessão ativa,
um certificado que não pode assinar, mais documentos que o limite (400 INVALID_REQUEST) ou nenhum
(400 INVALID_DOCUMENT) e, para documentos assinados com o REST PKI Core, uma falha antes de qualquer
assinatura (acima). Os documentos são assinados vários
de cada vez, até Signing:SignerConcurrency, e a chamada inteira é aguardada, então um lote grande leva o tempo de
assinar todos os documentos dele. Um lote conta como uma chamada para o throttle e para a
Franquia de Assinaturas.
Numa sessão por transação, um lote é autorizado uma única vez para a chamada
inteira: por um campo de formulário pin quando transactionPin é "client", ou por uma única
solicitação no Tray, que mostra os nomes dos documentos, quando é "workstation". Um documento
recusado por si só, como um que não é PDF, é o ERROR do item dele, como sempre, e não é contado nem
mostrado na solicitação. Uma solicitação recusada ou sem resposta recusa a chamada inteira com
422 PIN_NOT_AVAILABLE, e não um ERROR por item, e nada é criado no serviço de assinatura.
acceptedAt é então o momento em que o titular autorizou o lote.
POST /v1/signatures/hashes: assinar hashes (Assinatura de hash)
(Extensão do TrustBridge, Servidor 1.14.0.) Assina hashes que o próprio cliente calculou, com o
certificado ao qual a sessão está vinculada, e devolve as assinaturas de forma síncrona, na mesma
ordem. Nenhum documento é enviado e nada é buscado depois. Serve para um integrador que monta a
própria estrutura assinada (CAdES, XAdES ou uma assinatura PAdES montada no cliente) em torno do
certificate e da chain que a criação da sessão e GET /v1/certificates/info respondem.
Só uma Aplicação Cliente à qual um administrador concedeu a Assinatura de hash pode chamar esta
operação. Um hash não traz nada que diga de que ele é o hash, então ela entrega à chave do titular o
que o cliente quiser. Ela fica desligada para toda Aplicação Cliente até que um administrador a ligue
para aquela aplicação na tela Aplicações Cliente do console administrativo. Quem chama sem essa
concessão é recusado com 403 HASH_SIGNING_NOT_GRANTED antes de a sessão ser consultada.
curl -s https://trustbridge.example.com/v1/signatures/hashes \
-H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
-d '{ "sessionId": "8f3c…", "digestAlgorithm": "SHA-256", "hashes": ["uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek="] }'
{ "signatures": ["Kx9f…"], "signedAt": "2026-10-18T14:10:00Z" }
| Campo | ||
|---|---|---|
sessionId | obrigatório | A sessão ativa cujo certificado assina. |
digestAlgorithm | obrigatório | SHA-256, SHA-384 ou SHA-512, um por chamada. |
hashes | obrigatório | Os hashes, cada um codificado em base64 e exatamente do tamanho da saída do algoritmo (32, 48 ou 64 bytes). No mínimo um e no máximo Signing:MaxBatchItems (50 por padrão). |
pin | só em sessões "client" | O PIN do titular para esta chamada, numa sessão por transação cujo transactionPin é "client". Recusado em qualquer outra sessão. |
- Cada assinatura é RSASSA-PKCS1-v1_5 pura sobre um DigestInfo que indica o
digestAlgorithm, e não uma estrutura CMS, feita com o certificado queGET /v1/certificates/infodescreve. - Um hash que não está em base64 ou tem o tamanho errado recusa a chamada inteira com
400 INVALID_REQUESTantes de qualquer assinatura, edetailsindica o índice dele (hashes[2]para o terceiro). - Tudo ou nada. Os hashes são assinados em ordem, e o primeiro que falhar encerra a chamada com a recusa correspondente. As assinaturas já feitas não são devolvidas (a mensagem informa quantas foram), então o cliente tem todas as assinaturas ou nenhuma, e reenvia a chamada inteira.
- Uma chamada conta uma vez na Franquia de Assinaturas, quantos hashes trouxer, e estende a janela
da sessão, as duas coisas só se a chave tiver sido usada. Ela é limitada como um todo por
Signing:RequestBudget(60 s por padrão). Cada hash é registrado na Trilha de Auditoria. - Numa sessão por transação
"workstation", o titular autoriza a chamada no Tray, que mostra a Aplicação Cliente e o número de hashes, antes de qualquer hash ser assinado.
Recusas além das de sempre: 403 HASH_SIGNING_NOT_GRANTED; 422 CERT_DATA_UNAVAILABLE quando a
plataforma não tem a codificação do certificado (certificate: null); 422 CERT_INCOMPLETE_IDENTITY
quando o certificado não indica o CPF do titular, que a assinatura de hashes exige mesmo onde um
documento não exigiria; 422 WORKSTATION_OFFLINE quando a máquina que guarda o certificado está
inacessível, ou saiu enquanto tinha um hash, ou foi removida da plataforma (details
workstation removed); 422 PIN_NOT_AVAILABLE quando a máquina não conseguiu
usar o PIN dela (nenhum guardado, recusado, ou o token bloqueado) ou o Servidor não guarda mais o
segredo com que a sessão foi criada; e 422 SIGN_TIMEOUT, depois do qual a chave pode ter assinado
alguns dos hashes, embora nenhum seja devolvido.
GET /v1/signatures/{signatureId}/document: buscar o documento assinado
curl -s https://trustbridge.example.com/v1/signatures/a1b2…/document \
-H "X-API-Key: $API_KEY" -o signed.pdf -w '%{http_code}\n'
| Status | Significado |
|---|---|
200 | O PDF assinado, como corpo da resposta. |
409 DOCUMENT_NOT_READY | Ainda está sendo assinado. Pergunte de novo. |
422 DOCUMENT_UNAVAILABLE | Terminou sem versão assinada: details diz Refused, Canceled ou Expired. Não há o que esperar. Para um documento assinado com o REST PKI Core, o REST PKI Core não o tem mais, sem details. |
404 DOCUMENT_NOT_FOUND | Nenhum documento tem esse id, inclusive um que o serviço de assinatura não guarda. |
502 SIGNER_ERROR | Não foi possível consultar o serviço de assinatura (inacessível, limitando requisições ou com falha) ou, para um documento assinado com o REST PKI Core, não foi possível pedi-lo ao REST PKI Core; o documento não foi afetado. Pergunte de novo em instantes. |
O Servidor não armazena nada, então o tempo durante o qual um documento pode ser buscado é a retenção
do Lacuna Signer, ou a do REST PKI Core para um documento assinado lá. Qualquer chave de API válida pode buscar qualquer documento cujo id conheça,
inclusive um criado por outra Aplicação Cliente. Isso é o contrato, e não um descuido: trate um
signatureId como um token de portador do documento dele.
O envelope de erro
Todo erro chega em um único envelope. code é uma string, não um conjunto fechado: trate um código
desconhecido como uma falha inesperada, e não como um erro de parsing. Decida com base em code,
nunca em message.
{ "error": { "code": "CERT_NOT_ACTIVE", "message": "…", "details": "…" } }
"O CPF", abaixo, significa o CPF e o CNPJ juntos numa sessão em nome de uma Empresa. O bloqueio por PIN é a única exceção, e continua por CPF.
| Código | HTTP | Significado |
|---|---|---|
INVALID_REQUEST | 400, 415 | Requisição malformada, ou um corpo em um media type que a operação não lê. details diz o que estava errado: por exemplo, um sessionId ausente, ou um CPF ou CNPJ com dígitos verificadores errados. Na criação de uma sessão sem pin, details começa por pin is required.: a aplicação não cria sessões sem o PIN, ou mais de um certificado poderia atender a sessão (PIN dispensado na criação). |
INVALID_DOCUMENT | 400 | Nenhum documento a assinar chegou, ou ele estava vazio. |
UNAUTHORIZED | 401 | A chave de API não foi aceita. |
SESSION_EXPIRED | 401 | Nenhuma Sessão de Assinatura ativa tem o sessionId informado: expirada, desativada, substituída, esgotada ou interrompida. Crie uma nova sessão. |
INVALID_PIN | 401 | Na criação com um pin: o PIN não abriu nenhum Certificado do Cofre do CPF e é diferente do PIN em uso em todas as estações de trabalho que têm um certificado dele: todo candidato foi tentado, e nenhum conferiu. Na assinatura numa sessão por transação "client": o pin da chamada não abriu o certificado. Nos dois casos, conta para o bloqueio do CPF. |
HASH_SIGNING_NOT_GRANTED | 403 | Esta Aplicação Cliente não recebeu a concessão da Assinatura de hash. Respondido antes de a sessão ser consultada, então não diz nada sobre o sessionId. |
CERT_NOT_FOUND | 404 | Na criação: nenhum certificado para o CPF em lugar nenhum, nem no cofre nem em uma estação de trabalho. Na leitura ou na assinatura: o certificado vinculado não está mais disponível. A leitura também responde isso depois que a estação de trabalho que o guardava foi removida; a assinatura responde esse caso com WORKSTATION_OFFLINE. |
DOCUMENT_NOT_FOUND | 404 | Nenhum documento tem esse id de assinatura. |
ENDPOINT_NOT_FOUND | 404 | Nenhuma operação responde a esse método e caminho. Verifique o prefixo /v1. |
DOCUMENT_NOT_READY | 409 | Ainda está sendo assinado. Vale perguntar de novo. |
DOCUMENT_TOO_LARGE | 413 | O documento ou o corpo da requisição excede o limite do Servidor; details informa o tamanho e o limite. |
CERT_NOT_ACTIVE | 422 | Todo certificado do CPF, ou o certificado vinculado, tem um veredito diferente de ativo (EXPIRED, REVOKED, UNTRUSTED, STALE). Na assinatura de um documento com o REST PKI Core: o REST PKI Core também não validou o certificado. |
DOCUMENT_UNAVAILABLE | 422 | O documento terminou sem versão assinada: recusado, cancelado ou expirado ou, assinado com o REST PKI Core, o REST PKI Core não o tem mais. Não há o que esperar. |
PIN_NOT_AVAILABLE | 422 | Na criação com um pin: não foi possível fazer nenhuma comparação: uma solicitação expirou, foi recusada ou não pôde ser exibida, um Certificado do Cofre não pôde ser aberto, ou tentativas recentes demais contra o certificado não conferiram. Na assinatura de hashes: não foi possível usar a chave. Na assinatura numa sessão por transação: o titular não autorizou a chamada, ou não foi possível conferir o pin dela (veja PIN por transação). details diz qual. |
WORKSTATION_OFFLINE | 422 | Todo certificado ativo do CPF está em uma estação de trabalho, e todas elas estão offline (depois da tolerância de reconexão, em uma assinatura) ou, para uma Aplicação Cliente com o PIN por transação ligado, são antigas demais para pedir o PIN ao titular a cada chamada. Com details workstation removed, a estação de trabalho da sessão foi removida da plataforma: nada mais é assinado nessa sessão, e ela expira sozinha. Uma criação cuja estação de trabalho escolhida foi removida no meio do caminho responde o mesmo, e então nenhuma sessão foi criada nem substituída. |
CERT_DATA_UNAVAILABLE | 422 | Os dados do próprio certificado, necessários para assinar, não estão guardados para ele. |
CERT_INCOMPLETE_IDENTITY | 422 | O certificado não identifica o titular o suficiente para assinar; details indica o campo que falta. Um endereço de e-mail é o único campo que o email da sessão pode suprir. A assinatura de hashes, e a de um documento com o REST PKI Core, só pedem o titular, e recusam um certificado que não identifique nenhum. |
UNSUPPORTED_DOCUMENT | 422 | Não é um PDF ou, para um documento assinado com o REST PKI Core, é um PDF que o REST PKI Core não consegue processar. |
SIGNER_REJECTED | 422 | O serviço de assinatura recusou a requisição, em geral o próprio documento; details traz a explicação dele e diz qual. Repetir sem mudança dá o mesmo resultado. |
SIGN_TIMEOUT | 422 | A assinatura não terminou dentro do tempo dado a uma requisição (60 s por padrão). Na assinatura de um documento, só chega enquanto não existe id de assinatura: nada foi assinado, exceto para um documento assinado com o REST PKI Core, em que a chave do titular pode já ter assinado e um documento assinado pode ter sido guardado. Na assinatura de hashes, nenhuma assinatura é devolvida, mas a chave pode ter assinado alguns deles. |
SERVER_BUSY | 429 | O Servidor está executando tantas chamadas do contrato quanto aceita e a fila dele está cheia. Nada na requisição foi recusado: tente de novo com um backoff próprio, com jitter. Nenhum Retry-After é enviado. |
TOO_MANY_PIN_ATTEMPTS | 429 | O bloqueio por PINs incorretos do CPF está em vigor, qualquer que seja o cnpj enviado com o CPF, ou nenhum: na criação da sessão e em toda chamada de assinatura numa sessão por transação "client", que compartilham a mesma contagem. Refere-se a um CPF, em todas as chaves de API; tentar de novo piora a situação. Some à medida que as falhas mais antigas saem da janela (15 min por padrão). |
INTERNAL_ERROR | 500 | Não foi possível concluir a requisição; é seguro tentar de novo, exceto na assinatura de um documento com o REST PKI Core, em que ele pode chegar depois de a chave do titular ter assinado, como a mensagem então diz, e reenviar o documento o assina de novo. |
SIGNER_ERROR | 502 | Não foi possível consultar o serviço de assinatura: inacessível, limitando requisições ou respondendo com uma falha própria. Em uma assinatura, só chega antes de o documento ter chegado ao serviço, então nada foi assinado; em uma busca, não diz nada sobre o documento. Para um documento assinado com o REST PKI Core, o serviço é o REST PKI Core, inclusive sem cota no plano contratado, e numa assinatura ele pode chegar depois de a chave do titular ter assinado. |
Dois 429 diferentes. SERVER_BUSY diz respeito ao Servidor naquele momento e se resolve sozinho;
TOO_MANY_PIN_ATTEMPTS diz respeito ao certificado de uma pessoa e não se resolve com novas
tentativas. Um cliente que decide só pelo status vai tratá-los da mesma forma, e é por isso que são
códigos separados.
A especificação
A descrição normativa do contrato é a especificação de referência do SignSession
(SignSession — Documentação de referência), escrita em português a partir da implementação deste
Servidor e adotada pelo autor da especificação do contrato no lugar do documento de integração
anterior. Peça uma cópia à Lacuna Software. O documento OpenAPI que um Servidor em execução serve em
/openapi/v1.json é gerado a partir do próprio Servidor e mantido idêntico ao que ele responde. Se os
dois discordarem, um deles está errado: informe a diferença em vez de supor que ela é intencional.
Os códigos que o documento OpenAPI marca como TrustBridge extension são os que a especificação
original não definia. A especificação atual os traz com a mesma marca; eles fazem parte do contrato
como qualquer outro. Três extensões não estão na especificação, então o documento OpenAPI é onde elas
estão descritas: a Assinatura de hash (POST /v1/signatures/hashes e
HASH_SIGNING_NOT_GRANTED), o PIN por transação (transactionPin e o pin nas chamadas de
assinatura), que não cria nenhum código, e o PIN dispensado na criação (um pin omitido em
POST /v1/sessions). Os documentos assinados com o REST PKI Core são mais uma: não mudam nenhum
formato do protocolo nem criam código, então as descrições das duas operações de assinatura de
documentos e da busca são onde eles estão descritos.
Vindo do documento de integração anterior
Um cliente escrito com base no documento de integração anterior, ou na outra implementação do contrato (o CertSession), encontra estas respostas. Todas elas agora são o contrato, e duas são silenciosas: produzem um comportamento diferente sem nenhum erro que o indique.
| O que um cliente encontra | O que fazer |
|---|---|
Toda rota tem o prefixo /v1. Uma rota sem o prefixo responde 404 ENDPOINT_NOT_FOUND. | Use o prefixo em todas as chamadas. Não há header nem media type de versão. |
Uma sessão é identificada pelo sessionId, e não por um CPF. Só a criação recebe um CPF; status, informações do certificado, assinatura e desativação recebem o id. | Guarde o sessionId. Nenhuma chamada o recupera a partir de um CPF. |
expiresIn nunca é nulo. Um sessionDuration omitido concede a janela padrão, e não uma ilimitada. | Silencioso. Leia expiresIn e expiresAt na resposta, em vez de supor a janela que você pediu. |
| Assinar estende a sessão em vez de encerrá-la, a menos que um administrador tenha definido uma Franquia de Assinaturas para a sua Aplicação Cliente (uma franquia de 1 dá sessões de uso único). | Silencioso. Um cliente que cria uma sessão por documento continua funcionando, ao custo de mais uma confirmação de PIN a cada vez. Para encerrar a sessão junto com o documento, chame POST /v1/sessions/deactivate. |
Um cpf precisa ser um CPF real: dígitos verificadores conferidos, valores de dígitos repetidos recusados. | 400 INVALID_REQUEST. Valide os dígitos verificadores também no seu formulário; o pattern do schema (^[0-9]{11}$) não consegue expressá-los. |
Uma chamada de assinatura sem sessionId é malformada, individual ou em lote. | 400 INVALID_REQUEST: corrija a requisição. Um 401 SESSION_EXPIRED significa criar uma sessão e tentar de novo. |
| Os certificados são validados quanto à cadeia e à revogação, não só quanto à expiração. | CERT_NOT_ACTIVE com UNTRUSTED, REVOKED ou STALE onde uma verificação só de expiração assinaria. |
| O Servidor descarta carga. | 429 SERVER_BUSY. Tente de novo após um backoff próprio; usar lotes reduz a frequência com que isso acontece. |
| O resultado é lido na busca. Não há webhook a registrar. | Consulte GET /v1/signatures/{signatureId}/document até receber 200 ou 422. |
Qualquer chave de API válida busca qualquer documento cujo signatureId conheça. | Trate o id como um token de portador: não o registre em log nem o entregue a quem não deve ter o documento. |
Como escrever um cliente robusto
- Guarde o
sessionId. Nenhuma chamada o recupera a partir de um CPF. - Esteja pronto para
SESSION_EXPIREDem qualquer chamada e recupere-se criando uma nova sessão. Você não tem como saber se a sua Aplicação Cliente tem uma Franquia de Assinaturas, nem deveria. - Leia o resultado na busca. Um
202diz que o documento foi aceito, não assinado. Consulte o endpoint do documento periodicamente enquanto receber409; pare em200ou422. - Defina um timeout generoso em
POST /v1/sessions. Uma criação que vai para uma solicitação no Tray App aguarda uma pessoa, até o timeout da solicitação mais alguns segundos. - Decida com base em
error.codee trate os dois 429 de forma diferente. Não reenvie sem mudança um documento recusado comSIGNER_REJECTED; reenvie um que recebeuSIGNER_ERROR, a menos que os seus documentos sejam assinados com o REST PKI Core, em que uma recusa depois de a chave do titular ter assinado significa que reenviar assina de novo. - Use lotes quando tiver muitos documentos. Uma chamada, uma vaga no throttle, um débito na franquia.
- Envie
emailse algum certificado com que você assina puder não trazer endereço. - Envie
cnpjsó para assinar em nome de uma Empresa. Uma sessão sem ele nunca se vincula a um e-CNPJ, e uma com ele nunca se vincula ao e-CPF da pessoa. - Leia
transactionPinem toda sessão que criar, e enviepinnuma chamada de assinatura só quando ele for"client". Com"workstation", dê à chamada de assinatura tempo para que uma pessoa responda no Tray. - Com o PIN dispensado na criação, trate um
400 INVALID_REQUESTna criação pedindo o PIN e enviando a mesma requisição com ele: mais de um certificado poderia atender aquele titular.
Testando o contrato manualmente
A Lacuna Software pode fornecer uma coleção do Postman com o contrato como este Servidor o implementa,
com um environment que traz um pequeno PDF codificado em base64 e um cnpj de exemplo. Importe os
dois, cole em apiKey uma chave de API emitida na tela Aplicações Cliente e execute as
requisições em ordem: crie uma sessão (acompanhe a solicitação no Tray App se não houver PIN em
cache), assine um documento e busque-o. Uma segunda requisição de criação faz o mesmo para uma
Empresa, enviando o cnpj do environment. Uma requisição assina hashes (Assinatura de hash), e uma
pasta Per-Transaction PIN cobre os dois tipos de sessão por transação; a primeira requisição dela
explica como omitir o pin com o PIN dispensado na criação. A coleção grava sessionId e signatureId no
environment, então não é preciso colar nada entre um passo e outro. Quando a coleção e o
/openapi/v1.json publicado pelo Servidor discordarem, vale o documento publicado.