Colete assinaturas de múltiplos signatários no mesmo documento com um único upload. Modos paralelo e sequencial.
Um Envelope é um container que agrupa múltiplas sessões de assinatura para o mesmo documento. Em vez de criar transações separadas e fazer upload do PDF várias vezes, você:
Envelopes são ideais para contratos com múltiplas partes, atas societárias, procurações e qualquer documento que exija mais de uma assinatura.
Pré-requisito: Este guia assume que você já configurou as credenciais e obteve um token de acesso. Veja o Guia de Início Rápido e a Visão Geral da Assinatura Expressa para os fundamentos.
SEU BACKEND SIGNDOCS API
──────────── ────────────
1. Criar envelope ──> POST /v1/envelopes
(documento base64, modo, total) Upload + criar container
<── { envelopeId, status: CREATED }
2. Adicionar signatário 1 ──> POST /v1/envelopes/{id}/sessions
(nome, cpf, perfil, signerIndex: 1) Cria transação + sessão
<── { sessionId, url, clientSecret }
status: CREATED → ACTIVE
3. Adicionar signatário 2 ──> POST /v1/envelopes/{id}/sessions
(nome, cpf, perfil, signerIndex: 2) Cria transação + sessão
<── { sessionId, url, clientSecret }
... (repetir para cada signatário)
4. Consultar progresso ──> GET /v1/envelopes/{id}
<── { status, addedSessions, completedSessions, sessions[] }
5. Todos assinaram → COMPLETED ──> Webhook: ENVELOPE.ALL_SIGNED
6. Gerar PDF combinado ──> POST /v1/envelopes/{id}/combined-stamp
<── { downloadUrl, signerCount }
| Etapa | Endpoint | Método | HTTP |
|---|---|---|---|
| Criar envelope | /v1/envelopes |
POST | 201 |
| Adicionar signatário | /v1/envelopes/{id}/sessions |
POST | 201 |
| Consultar envelope | /v1/envelopes/{id} |
GET | 200 |
| PDF combinado | /v1/envelopes/{id}/combined-stamp |
POST | 200 |
O campo signingMode define como os signatários interagem com o envelope:
| PARALLEL | SEQUENTIAL | |
|---|---|---|
| Ordem | Todos assinam simultaneamente | Ordem rigorosa por signerIndex |
| Velocidade | Mais rápido | Depende de cada signatário |
| Caso de uso | Co-signatários, testemunhas, sócios | Aprovações hierárquicas, fluxos regulatórios |
| Notificação | Todos recebem ao mesmo tempo | Próximo notificado após anterior concluir |
O primeiro passo é criar o envelope com o documento em base64. O documento é armazenado uma única vez e reutilizado por todas as sessões de assinatura.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
signingMode |
string |
Sim | "PARALLEL" ou "SEQUENTIAL" |
totalSigners |
number |
Sim | Número total de signatários esperados |
document.content |
string |
Sim | Conteúdo base64 do PDF (máx. 10 MB) |
document.filename |
string |
Não | Nome do arquivo |
appearance.brandColor |
string |
Não | Cor da marca em hex (ex: #1a5276) |
appearance.logoUrl |
string |
Não | URL HTTPS do logo para a página de assinatura |
locale |
string |
Não | "pt-BR" (padrão), "en", "es" |
returnUrl |
string |
Não | URL de redirecionamento após assinatura |
cancelUrl |
string |
Não | URL de redirecionamento após cancelamento |
expiresInMinutes |
number |
Não | 5 a 10.080 min (padrão: 1.440 = 24h) |
metadata |
object |
Não | Dados customizados (chave ≤ 256, valor ≤ 1024 chars) |
import { readFileSync } from 'fs';
const pdfBase64 = readFileSync('contrato.pdf').toString('base64');
const envelope = await client.envelopes.create({
signingMode: 'SEQUENTIAL',
totalSigners: 2,
document: {
content: pdfBase64,
filename: 'contrato-prestacao-servicos.pdf',
},
returnUrl: 'https://app.empresa.com.br/assinatura/sucesso',
cancelUrl: 'https://app.empresa.com.br/assinatura/cancelado',
expiresInMinutes: 4320, // 3 dias
metadata: { contractId: 'CTR-2026-001' },
});
console.log(envelope.envelopeId); // "env_01J..."
console.log(envelope.status); // "CREATED"
import base64
from pathlib import Path
from signdocs_brasil.models.envelope import CreateEnvelopeRequest
pdf_base64 = base64.b64encode(Path('contrato.pdf').read_bytes()).decode()
envelope = client.envelopes.create(CreateEnvelopeRequest(
signing_mode='SEQUENTIAL',
total_signers=2,
document_content=pdf_base64,
document_filename='contrato-prestacao-servicos.pdf',
return_url='https://app.empresa.com.br/assinatura/sucesso',
cancel_url='https://app.empresa.com.br/assinatura/cancelado',
expires_in_minutes=4320, # 3 dias
metadata={'contractId': 'CTR-2026-001'},
))
print(envelope.envelope_id) # "env_01J..."
print(envelope.status) # "CREATED"
pdfBytes, _ := os.ReadFile("contrato.pdf")
pdfBase64 := base64.StdEncoding.EncodeToString(pdfBytes)
envelope, err := client.Envelopes.Create(ctx, &signdocs.CreateEnvelopeRequest{
SigningMode: "SEQUENTIAL",
TotalSigners: 2,
Document: signdocs.EnvelopeDocument{
Content: pdfBase64,
Filename: "contrato-prestacao-servicos.pdf",
},
ReturnURL: "https://app.empresa.com.br/assinatura/sucesso",
CancelURL: "https://app.empresa.com.br/assinatura/cancelado",
ExpiresInMinutes: 4320,
Metadata: map[string]string{"contractId": "CTR-2026-001"},
})
if err != nil { log.Fatal(err) }
fmt.Println(envelope.EnvelopeID) // "env_01J..."
fmt.Println(envelope.Status) // "CREATED"
byte[] pdfBytes = Files.readAllBytes(Path.of("contrato.pdf"));
String pdfBase64 = Base64.getEncoder().encodeToString(pdfBytes);
Envelope envelope = client.envelopes().create(CreateEnvelopeRequest.builder()
.signingMode("SEQUENTIAL")
.totalSigners(2)
.documentContent(pdfBase64)
.documentFilename("contrato-prestacao-servicos.pdf")
.returnUrl("https://app.empresa.com.br/assinatura/sucesso")
.cancelUrl("https://app.empresa.com.br/assinatura/cancelado")
.expiresInMinutes(4320)
.metadata(Map.of("contractId", "CTR-2026-001"))
.build());
System.out.println(envelope.getEnvelopeId()); // "env_01J..."
System.out.println(envelope.getStatus()); // "CREATED"
$pdfBase64 = base64_encode(file_get_contents('contrato.pdf'));
$envelope = $client->envelopes()->create([
'signingMode' => 'SEQUENTIAL',
'totalSigners' => 2,
'document' => [
'content' => $pdfBase64,
'filename' => 'contrato-prestacao-servicos.pdf',
],
'returnUrl' => 'https://app.empresa.com.br/assinatura/sucesso',
'cancelUrl' => 'https://app.empresa.com.br/assinatura/cancelado',
'expiresInMinutes' => 4320,
'metadata' => ['contractId' => 'CTR-2026-001'],
]);
echo $envelope->envelopeId; // "env_01J..."
echo $envelope->status; // "CREATED"
var pdfBase64 = Convert.ToBase64String(File.ReadAllBytes("contrato.pdf"));
var envelope = await client.Envelopes.CreateAsync(new CreateEnvelopeRequest
{
SigningMode = "SEQUENTIAL",
TotalSigners = 2,
Document = new EnvelopeDocument
{
Content = pdfBase64,
Filename = "contrato-prestacao-servicos.pdf",
},
ReturnUrl = "https://app.empresa.com.br/assinatura/sucesso",
CancelUrl = "https://app.empresa.com.br/assinatura/cancelado",
ExpiresInMinutes = 4320,
Metadata = new Dictionary<string, string> { ["contractId"] = "CTR-2026-001" },
});
Console.WriteLine(envelope.EnvelopeId); // "env_01J..."
Console.WriteLine(envelope.Status); // "CREATED"
{
"envelopeId": "env_01J5X7K9M2N8P4Q6R3S1T0",
"status": "CREATED",
"signingMode": "SEQUENTIAL",
"totalSigners": 2,
"documentHash": "sha256:a1b2c3d4e5f6...",
"createdAt": "2026-03-20T10:00:00.000Z",
"expiresAt": "2026-03-23T10:00:00.000Z"
}
Cada signatário é adicionado individualmente. A API cria atomicamente uma transação, as etapas de verificação e um token de embed — tudo em uma única chamada.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
signer.userExternalId |
string |
Sim | ID externo do usuário no seu sistema |
signer.name |
string |
Sim | Nome completo do signatário |
signer.cpf |
string |
Depende | CPF (obrigatório para biometria) |
signer.email |
string |
Depende | E-mail (obrigatório para OTP via e-mail) |
signer.phone |
string |
Depende | Telefone (obrigatório para OTP via SMS) |
signer.otpChannel |
string |
Não | "SMS" ou "EMAIL" |
signer.birthDate |
string |
Depende | Data de nascimento (SERPRO) |
policy.profile |
string |
Sim | Perfil de assinatura (ver tabela abaixo) |
signerIndex |
number |
Sim | Ordem do signatário (1-based) |
purpose |
string |
Não | Finalidade (padrão: "DOCUMENT_SIGNATURE") |
returnUrl |
string |
Não | Sobrescreve o returnUrl do envelope |
cancelUrl |
string |
Não | Sobrescreve o cancelUrl do envelope |
metadata |
object |
Não | Metadados por sessão |
| Perfil | Descrição | Campos obrigatórios |
|---|---|---|
CLICK_ONLY |
Aceite simples por clique | name, cpf ou cnpj |
CLICK_PLUS_OTP |
Aceite + código OTP | email (ou phone) |
BIOMETRIC |
Verificação facial | cpf |
BIOMETRIC_PLUS_OTP |
Biometria + OTP | cpf + email |
DIGITAL_CERTIFICATE |
Certificado ICP-Brasil A1 | cpf |
BIOMETRIC_SERPRO |
Biometria contra base SERPRO | cpf + birthDate |
BIOMETRIC_SERPRO_AUTO_FALLBACK |
NT65 consignado | cpf + birthDate |
CUSTOM |
Etapas customizadas | varia |
Restrição: O perfil
DIGITAL_CERTIFICATEnão pode ser combinado com outros perfis no mesmo envelope. Se um signatário usar certificado digital, todos devem usar.
// Signatário 1 — Diretor Financeiro (biometria + OTP)
const session1 = await client.envelopes.addSession(envelope.envelopeId, {
signer: {
userExternalId: 'usr_maria_001',
name: 'Maria Souza',
cpf: '123.456.789-00',
email: 'maria.souza@empresa.com.br',
phone: '+5511999990001',
otpChannel: 'SMS',
},
policy: { profile: 'BIOMETRIC_PLUS_OTP' },
signerIndex: 1,
metadata: { role: 'CFO' },
});
console.log(session1.url); // URL da página de assinatura
console.log(session1.clientSecret); // Para integração via SDK frontend
// Signatário 2 — Diretor Jurídico (aceite simples)
const session2 = await client.envelopes.addSession(envelope.envelopeId, {
signer: {
userExternalId: 'usr_carlos_002',
name: 'Carlos Lima',
cpf: '987.654.321-00',
},
policy: { profile: 'CLICK_ONLY' },
signerIndex: 2,
});
from signdocs_brasil.models.envelope import AddEnvelopeSessionRequest
# Signatário 1 — Diretor Financeiro (biometria + OTP)
session1 = client.envelopes.add_session(envelope.envelope_id, AddEnvelopeSessionRequest(
signer_name='Maria Souza',
signer_cpf='123.456.789-00',
signer_email='maria.souza@empresa.com.br',
signer_phone='+5511999990001',
signer_otp_channel='SMS',
signer_user_external_id='usr_maria_001',
policy_profile='BIOMETRIC_PLUS_OTP',
signer_index=1,
metadata={'role': 'CFO'},
))
print(session1.url) # URL da página de assinatura
print(session1.client_secret) # Para integração via SDK frontend
# Signatário 2 — Diretor Jurídico (aceite simples)
session2 = client.envelopes.add_session(envelope.envelope_id, AddEnvelopeSessionRequest(
signer_name='Carlos Lima',
signer_cpf='987.654.321-00',
signer_user_external_id='usr_carlos_002',
policy_profile='CLICK_ONLY',
signer_index=2,
))
// Signatário 1 — Diretor Financeiro (biometria + OTP)
session1, err := client.Envelopes.AddSession(ctx, envelope.EnvelopeID, &signdocs.AddEnvelopeSessionRequest{
Signer: signdocs.Signer{
UserExternalID: "usr_maria_001",
Name: "Maria Souza",
CPF: "123.456.789-00",
Email: "maria.souza@empresa.com.br",
Phone: "+5511999990001",
OTPChannel: "SMS",
},
Policy: signdocs.Policy{Profile: "BIOMETRIC_PLUS_OTP"},
SignerIndex: 1,
Metadata: map[string]string{"role": "CFO"},
})
if err != nil { log.Fatal(err) }
fmt.Println(session1.URL) // URL da página de assinatura
fmt.Println(session1.ClientSecret) // Para integração via SDK frontend
// Signatário 2 — Diretor Jurídico (aceite simples)
session2, err := client.Envelopes.AddSession(ctx, envelope.EnvelopeID, &signdocs.AddEnvelopeSessionRequest{
Signer: signdocs.Signer{
UserExternalID: "usr_carlos_002",
Name: "Carlos Lima",
CPF: "987.654.321-00",
},
Policy: signdocs.Policy{Profile: "CLICK_ONLY"},
SignerIndex: 2,
})
if err != nil { log.Fatal(err) }
// Signatário 1 — Diretor Financeiro (biometria + OTP)
EnvelopeSession session1 = client.envelopes().addSession(
envelope.getEnvelopeId(),
AddEnvelopeSessionRequest.builder()
.signerName("Maria Souza")
.signerCpf("123.456.789-00")
.signerEmail("maria.souza@empresa.com.br")
.signerPhone("+5511999990001")
.signerOtpChannel("SMS")
.signerUserExternalId("usr_maria_001")
.policyProfile("BIOMETRIC_PLUS_OTP")
.signerIndex(1)
.metadata(Map.of("role", "CFO"))
.build());
System.out.println(session1.getUrl()); // URL da página de assinatura
System.out.println(session1.getClientSecret()); // Para integração via SDK frontend
// Signatário 2 — Diretor Jurídico (aceite simples)
EnvelopeSession session2 = client.envelopes().addSession(
envelope.getEnvelopeId(),
AddEnvelopeSessionRequest.builder()
.signerName("Carlos Lima")
.signerCpf("987.654.321-00")
.signerUserExternalId("usr_carlos_002")
.policyProfile("CLICK_ONLY")
.signerIndex(2)
.build());
// Signatário 1 — Diretor Financeiro (biometria + OTP)
$session1 = $client->envelopes()->addSession($envelope->envelopeId, [
'signer' => [
'userExternalId' => 'usr_maria_001',
'name' => 'Maria Souza',
'cpf' => '123.456.789-00',
'email' => 'maria.souza@empresa.com.br',
'phone' => '+5511999990001',
'otpChannel' => 'SMS',
],
'policy' => ['profile' => 'BIOMETRIC_PLUS_OTP'],
'signerIndex' => 1,
'metadata' => ['role' => 'CFO'],
]);
echo $session1->url; // URL da página de assinatura
echo $session1->clientSecret; // Para integração via SDK frontend
// Signatário 2 — Diretor Jurídico (aceite simples)
$session2 = $client->envelopes()->addSession($envelope->envelopeId, [
'signer' => [
'userExternalId' => 'usr_carlos_002',
'name' => 'Carlos Lima',
'cpf' => '987.654.321-00',
],
'policy' => ['profile' => 'CLICK_ONLY'],
'signerIndex' => 2,
]);
// Signatário 1 — Diretor Financeiro (biometria + OTP)
var session1 = await client.Envelopes.AddSessionAsync(envelope.EnvelopeId, new AddEnvelopeSessionRequest
{
Signer = new Signer
{
UserExternalId = "usr_maria_001",
Name = "Maria Souza",
Cpf = "123.456.789-00",
Email = "maria.souza@empresa.com.br",
Phone = "+5511999990001",
OtpChannel = "SMS",
},
Policy = new Policy { Profile = "BIOMETRIC_PLUS_OTP" },
SignerIndex = 1,
Metadata = new Dictionary<string, string> { ["role"] = "CFO" },
});
Console.WriteLine(session1.Url); // URL da página de assinatura
Console.WriteLine(session1.ClientSecret); // Para integração via SDK frontend
// Signatário 2 — Diretor Jurídico (aceite simples)
var session2 = await client.Envelopes.AddSessionAsync(envelope.EnvelopeId, new AddEnvelopeSessionRequest
{
Signer = new Signer
{
UserExternalId = "usr_carlos_002",
Name = "Carlos Lima",
Cpf = "987.654.321-00",
},
Policy = new Policy { Profile = "CLICK_ONLY" },
SignerIndex = 2,
});
{
"sessionId": "ses_01J5X8A2B3C4D5E6F7G8H9",
"transactionId": "txn_01J5X8A2B3C4D5E6F7G8",
"signerIndex": 1,
"status": "ACTIVE",
"url": "https://sign.signdocs.com.br/ses_01J5X8A2B3C4D5E6F7G8H9?token=eyJ...",
"clientSecret": "cs_live_a1b2c3d4e5f6...",
"expiresAt": "2026-03-23T10:00:00.000Z"
}
Use a url para redirecionar o signatário ou embutir em iframe. Use o clientSecret com o SDK JavaScript para integração inline:
// No frontend — embutir sessão de assinatura
sd.checkout({ clientSecret: session1.clientSecret });
Consulte o envelope a qualquer momento para verificar o progresso global e o status individual de cada signatário.
const detail = await client.envelopes.get(envelope.envelopeId);
console.log(detail.status); // "ACTIVE"
console.log(detail.completedSessions); // 1
console.log(detail.totalSigners); // 2
for (const s of detail.sessions) {
console.log(`${s.signerName} (${s.signerIndex}): ${s.status}`);
}
detail = client.envelopes.get(envelope.envelope_id)
print(detail.status) # "ACTIVE"
print(detail.completed_sessions) # 1
print(detail.total_signers) # 2
for s in detail.sessions:
print(f"{s.signer_name} ({s.signer_index}): {s.status}")
detail, err := client.Envelopes.Get(ctx, envelope.EnvelopeID)
if err != nil { log.Fatal(err) }
fmt.Println(detail.Status) // "ACTIVE"
fmt.Println(detail.CompletedSessions) // 1
fmt.Println(detail.TotalSigners) // 2
for _, s := range detail.Sessions {
fmt.Printf("%s (%d): %s\n", s.SignerName, s.SignerIndex, s.Status)
}
EnvelopeDetail detail = client.envelopes().get(envelope.getEnvelopeId());
System.out.println(detail.getStatus()); // "ACTIVE"
System.out.println(detail.getCompletedSessions()); // 1
System.out.println(detail.getTotalSigners()); // 2
for (EnvelopeSessionSummary s : detail.getSessions()) {
System.out.printf("%s (%d): %s%n", s.getSignerName(), s.getSignerIndex(), s.getStatus());
}
$detail = $client->envelopes()->get($envelope->envelopeId);
echo $detail->status; // "ACTIVE"
echo $detail->completedSessions; // 1
echo $detail->totalSigners; // 2
foreach ($detail->sessions as $s) {
echo "{$s->signerName} ({$s->signerIndex}): {$s->status}\n";
}
var detail = await client.Envelopes.GetAsync(envelope.EnvelopeId);
Console.WriteLine(detail.Status); // "ACTIVE"
Console.WriteLine(detail.CompletedSessions); // 1
Console.WriteLine(detail.TotalSigners); // 2
foreach (var s in detail.Sessions)
{
Console.WriteLine($"{s.SignerName} ({s.SignerIndex}): {s.Status}");
}
{
"envelopeId": "env_01J5X7K9M2N8P4Q6R3S1T0",
"status": "ACTIVE",
"signingMode": "SEQUENTIAL",
"totalSigners": 2,
"addedSessions": 2,
"completedSessions": 1,
"documentHash": "sha256:a1b2c3d4e5f6...",
"sessions": [
{
"sessionId": "ses_01J5X8A2B3C4D5E6F7G8H9",
"transactionId": "txn_01J5X8A2B3C4D5E6F7G8",
"signerIndex": 1,
"signerName": "Maria Souza",
"status": "COMPLETED",
"completedAt": "2026-03-20T14:30:00.000Z",
"evidenceId": "evd_01J5X9B3C4D5E6F7G8H9"
},
{
"sessionId": "ses_01J5X8C4D5E6F7G8H9I0J1",
"transactionId": "txn_01J5X8C4D5E6F7G8H9I0",
"signerIndex": 2,
"signerName": "Carlos Lima",
"status": "ACTIVE"
}
],
"createdAt": "2026-03-20T10:00:00.000Z",
"updatedAt": "2026-03-20T14:30:00.000Z",
"expiresAt": "2026-03-23T10:00:00.000Z"
}
Quando todos os signatários concluírem (status COMPLETED), gere o PDF final com todas as assinaturas combinadas:
const combined = await client.envelopes.combinedStamp(envelope.envelopeId);
console.log(combined.downloadUrl); // URL temporária para download
console.log(combined.signerCount); // 2
// Baixar o PDF combinado
const res = await fetch(combined.downloadUrl);
const pdf = Buffer.from(await res.arrayBuffer());
writeFileSync('contrato-assinado-completo.pdf', pdf);
import httpx
combined = client.envelopes.combined_stamp(envelope.envelope_id)
print(combined.download_url) # URL temporária para download
print(combined.signer_count) # 2
# Baixar o PDF combinado
pdf = httpx.get(combined.download_url).content
Path('contrato-assinado-completo.pdf').write_bytes(pdf)
combined, err := client.Envelopes.CombinedStamp(ctx, envelope.EnvelopeID)
if err != nil { log.Fatal(err) }
fmt.Println(combined.DownloadURL) // URL temporária para download
fmt.Println(combined.SignerCount) // 2
// Baixar o PDF combinado
resp, _ := http.Get(combined.DownloadURL)
defer resp.Body.Close()
pdf, _ := io.ReadAll(resp.Body)
os.WriteFile("contrato-assinado-completo.pdf", pdf, 0644)
CombinedStampResponse combined = client.envelopes().combinedStamp(envelope.getEnvelopeId());
System.out.println(combined.getDownloadUrl()); // URL temporária para download
System.out.println(combined.getSignerCount()); // 2
// Baixar o PDF combinado
byte[] pdf = new URL(combined.getDownloadUrl()).openStream().readAllBytes();
Files.write(Path.of("contrato-assinado-completo.pdf"), pdf);
$combined = $client->envelopes()->combinedStamp($envelope->envelopeId);
echo $combined->downloadUrl; // URL temporária para download
echo $combined->signerCount; // 2
// Baixar o PDF combinado
$pdf = file_get_contents($combined->downloadUrl);
file_put_contents('contrato-assinado-completo.pdf', $pdf);
var combined = await client.Envelopes.CombinedStampAsync(envelope.EnvelopeId);
Console.WriteLine(combined.DownloadUrl); // URL temporária para download
Console.WriteLine(combined.SignerCount); // 2
// Baixar o PDF combinado
var pdf = await new HttpClient().GetByteArrayAsync(combined.DownloadUrl);
File.WriteAllBytes("contrato-assinado-completo.pdf", pdf);
{
"envelopeId": "env_01J5X7K9M2N8P4Q6R3S1T0",
"downloadUrl": "https://storage.signdocs.com.br/signed/env_01J5X7K9M2N8P4Q6R3S1T0.pdf?token=...",
"expiresIn": 3600,
"signerCount": 2
}
Importante: A URL de download expira (padrão: 1 hora). Armazene o PDF no seu próprio storage para acesso permanente.
O envelope transiciona automaticamente entre estados:
CREATED ──(primeira sessão adicionada)──> ACTIVE ──(todos assinaram)──> COMPLETED
ACTIVE ──(cancelado via API)──> CANCELLED
ACTIVE ──(prazo expirou)──> EXPIRED
| Status | Descrição | Ações permitidas |
|---|---|---|
CREATED |
Envelope criado, sem sessões | Adicionar sessões |
ACTIVE |
Pelo menos uma sessão adicionada | Adicionar sessões, consultar, cancelar |
COMPLETED |
Todos os signatários assinaram | Consultar, gerar PDF combinado |
CANCELLED |
Cancelado | Consultar |
EXPIRED |
Prazo expirou | Consultar |
O SignDocs emite eventos específicos durante o ciclo de vida do envelope. Configure webhooks para reagir em tempo real. Veja o Guia de Webhooks para configuração completa.
| Evento | Quando |
|---|---|
ENVELOPE.CREATED |
Envelope criado com sucesso |
ENVELOPE.ALL_SIGNED |
Último signatário concluiu — envelope COMPLETED |
TRANSACTION.CREATED |
Sessão adicionada ao envelope (por signatário) |
SIGNING_SESSION.CREATED |
Sessão de assinatura criada |
SIGNING_SESSION.COMPLETED |
Signatário concluiu sua assinatura |
SIGNING_SESSION.EXPIRED |
Sessão do signatário expirou |
// Webhook handler — gerar PDF combinado automaticamente
app.post('/webhooks/signdocs', async (req, res) => {
const event = req.body;
if (event.type === 'ENVELOPE.ALL_SIGNED') {
const envelopeId = event.data.envelopeId;
// Gerar PDF combinado
const combined = await client.envelopes.combinedStamp(envelopeId);
// Baixar e armazenar
const pdf = await fetch(combined.downloadUrl).then(r => r.arrayBuffer());
await saveToStorage(`envelopes/${envelopeId}/signed.pdf`, Buffer.from(pdf));
console.log(`Envelope ${envelopeId} completo — ${combined.signerCount} assinaturas`);
}
res.status(200).json({ received: true });
});
| Limite | Valor | Observação |
|---|---|---|
| Tamanho máximo do documento | 10 MB | Base64 aumenta ~33% — arquivo original ≤ 7,5 MB |
| Expiração mínima | 5 minutos | |
| Expiração máxima | 10.080 minutos (7 dias) | |
| Expiração padrão | 1.440 minutos (24 horas) | Se expiresInMinutes não for informado |
| Escopo OAuth2 | transactions:write |
Para criar envelopes e sessões |
| Chaves de metadata | ≤ 256 caracteres | |
| Valores de metadata | ≤ 1.024 caracteres | |
DIGITAL_CERTIFICATE |
Exclusivo | Não pode ser combinado com outros perfis |