Guias de Desenvolvimento

Signing Sessions

Guias dos SDKs

Envelopes (Multi-Signatário)

Agrupe sessões de assinatura de múltiplos signatários sobre um único documento, com modos PARALLEL e SEQUENTIAL.


1. Visão Geral

Um Envelope agrupa múltiplas sessões de assinatura sobre um único documento. Em vez de criar transações individuais para cada signatário, você faz um único upload do PDF e adiciona cada signatário como uma sessão dentro do envelope.

Dois modos de assinatura são suportados:

Signing Sessions vs Envelopes

Característica Signing Session Envelope
Signatários 1 por transação N signatários (até 20)
Upload do documento 1 upload por transação 1 upload para todos
Modo de assinatura N/A PARALLEL ou SEQUENTIAL
PDF combinado Apenas individual Carimbo combinado com todas as assinaturas
Acompanhamento Por transação Status global + por sessão

2. Criar Envelope

Cria o envelope com o documento em base64 e define o modo de assinatura. O status resultante será CREATED.

import { SignDocsBrasilClient } from '@signdocs-brasil/api';
import { readFileSync } from 'fs';

const client = new SignDocsBrasilClient({ /* ... */ });
const pdfBase64 = readFileSync('contrato.pdf').toString('base64');

const envelope = await client.envelopes.create({
  signingMode: 'PARALLEL',
  totalSigners: 2,
  document: {
    content: pdfBase64,
    filename: 'contrato.pdf',
  },
});
console.log(envelope.envelopeId, envelope.status); // CREATED
from signdocs_brasil import SignDocsBrasilClient, ClientConfig
from signdocs_brasil.models.envelope import CreateEnvelopeRequest

client = SignDocsBrasilClient(ClientConfig(# ...
))
pdf_base64 = open('contrato.pdf', 'rb').read().hex()  # ou base64.b64encode(...)

envelope = client.envelopes.create(CreateEnvelopeRequest(
    signing_mode='PARALLEL',
    total_signers=2,
    document_content=pdf_base64,
    document_filename='contrato.pdf',
))
print(envelope.envelope_id, envelope.status)  # CREATED
envelope, err := client.Envelopes.Create(ctx, &signdocs.CreateEnvelopeRequest{
    SigningMode:  "PARALLEL",
    TotalSigners: 2,
    Document: signdocs.EnvelopeDocument{
        Content:  pdfBase64,
        Filename: "contrato.pdf",
    },
})
if err != nil {
    log.Fatal(err)
}
fmt.Println(envelope.EnvelopeID, envelope.Status) // CREATED
CreateEnvelopeRequest request = new CreateEnvelopeRequest();
request.setSigningMode("PARALLEL");
request.setTotalSigners(2);
request.setDocument(pdfBase64, "contrato.pdf");

Envelope envelope = client.envelopes().create(request);
System.out.println(envelope.getEnvelopeId() + " " + envelope.getStatus()); // CREATED
use SignDocsBrasil\Api\Models\CreateEnvelopeRequest;

$envelope = $client->envelopes->create(new CreateEnvelopeRequest(
    signingMode: 'PARALLEL',
    totalSigners: 2,
    documentContent: $pdfBase64,
    documentFilename: 'contrato.pdf',
));
echo $envelope->envelopeId . ' ' . $envelope->status; // CREATED
var envelope = await client.Envelopes.CreateAsync(new CreateEnvelopeRequest
{
    SigningMode = "PARALLEL",
    TotalSigners = 2,
    DocumentContent = pdfBase64,
    DocumentFilename = "contrato.pdf",
});
Console.WriteLine($"{envelope.EnvelopeId} {envelope.Status}"); // CREATED

3. Adicionar Signatários

Adicione uma sessão de assinatura para cada signatário. Cada chamada retorna um EnvelopeSession com a URL de assinatura e clientSecret para integração via widget ou redirect.

// Signatário 1 — Diretor Financeiro
const session1 = await client.envelopes.addSession(envelope.envelopeId, {
  signer: {
    name: 'Maria Oliveira',
    email: 'maria@empresa.com',
    userExternalId: 'user-001',
  },
  policy: { profile: 'CLICK_ONLY' },
  signerIndex: 1,
});
console.log(session1.url); // URL de assinatura

// Signatário 2 — Diretor Jurídico
const session2 = await client.envelopes.addSession(envelope.envelopeId, {
  signer: {
    name: 'Carlos Santos',
    email: 'carlos@empresa.com',
    userExternalId: 'user-002',
  },
  policy: { profile: 'CLICK_ONLY' },
  signerIndex: 2,
});
console.log(session2.url); // URL de assinatura
from signdocs_brasil.models.envelope import AddEnvelopeSessionRequest

# Signatário 1 — Diretor Financeiro
session1 = client.envelopes.add_session(
    envelope.envelope_id,
    AddEnvelopeSessionRequest(
        signer_name='Maria Oliveira',
        signer_email='maria@empresa.com',
        signer_user_external_id='user-001',
        policy_profile='CLICK_ONLY',
        signer_index=1,
    ),
)
print(session1.url)  # URL de assinatura

# Signatário 2 — Diretor Jurídico
session2 = client.envelopes.add_session(
    envelope.envelope_id,
    AddEnvelopeSessionRequest(
        signer_name='Carlos Santos',
        signer_email='carlos@empresa.com',
        signer_user_external_id='user-002',
        policy_profile='CLICK_ONLY',
        signer_index=2,
    ),
)
print(session2.url)  # URL de assinatura
// Signatário 1 — Diretor Financeiro
session1, err := client.Envelopes.AddSession(ctx, envelope.EnvelopeID, &signdocs.AddEnvelopeSessionRequest{
    Signer: signdocs.EnvelopeSessionSigner{
        Name:           "Maria Oliveira",
        Email:          "maria@empresa.com",
        UserExternalID: "user-001",
    },
    Policy:     signdocs.EnvelopeSessionPolicy{Profile: "CLICK_ONLY"},
    SignerIndex: 1,
})
if err != nil {
    log.Fatal(err)
}
fmt.Println(session1.URL) // URL de assinatura

// Signatário 2 — Diretor Jurídico
session2, err := client.Envelopes.AddSession(ctx, envelope.EnvelopeID, &signdocs.AddEnvelopeSessionRequest{
    Signer: signdocs.EnvelopeSessionSigner{
        Name:           "Carlos Santos",
        Email:          "carlos@empresa.com",
        UserExternalID: "user-002",
    },
    Policy:     signdocs.EnvelopeSessionPolicy{Profile: "CLICK_ONLY"},
    SignerIndex: 2,
})
if err != nil {
    log.Fatal(err)
}
fmt.Println(session2.URL) // URL de assinatura
// Signatário 1 — Diretor Financeiro
AddEnvelopeSessionRequest req1 = new AddEnvelopeSessionRequest();
req1.setSignerName("Maria Oliveira");
req1.setSignerEmail("maria@empresa.com");
req1.setSignerUserExternalId("user-001");
req1.setPolicyProfile("CLICK_ONLY");
req1.setSignerIndex(1);

EnvelopeSession session1 = client.envelopes().addSession(envelope.getEnvelopeId(), req1);
System.out.println(session1.getUrl()); // URL de assinatura

// Signatário 2 — Diretor Jurídico
AddEnvelopeSessionRequest req2 = new AddEnvelopeSessionRequest();
req2.setSignerName("Carlos Santos");
req2.setSignerEmail("carlos@empresa.com");
req2.setSignerUserExternalId("user-002");
req2.setPolicyProfile("CLICK_ONLY");
req2.setSignerIndex(2);

EnvelopeSession session2 = client.envelopes().addSession(envelope.getEnvelopeId(), req2);
System.out.println(session2.getUrl()); // URL de assinatura
use SignDocsBrasil\Api\Models\AddEnvelopeSessionRequest;

// Signatário 1 — Diretor Financeiro
$session1 = $client->envelopes->addSession($envelope->envelopeId, new AddEnvelopeSessionRequest(
    signerName: 'Maria Oliveira',
    signerEmail: 'maria@empresa.com',
    signerUserExternalId: 'user-001',
    policyProfile: 'CLICK_ONLY',
    signerIndex: 1,
));
echo $session1->url; // URL de assinatura

// Signatário 2 — Diretor Jurídico
$session2 = $client->envelopes->addSession($envelope->envelopeId, new AddEnvelopeSessionRequest(
    signerName: 'Carlos Santos',
    signerEmail: 'carlos@empresa.com',
    signerUserExternalId: 'user-002',
    policyProfile: 'CLICK_ONLY',
    signerIndex: 2,
));
echo $session2->url; // URL de assinatura
// Signatário 1 — Diretor Financeiro
var session1 = await client.Envelopes.AddSessionAsync(envelope.EnvelopeId, new AddEnvelopeSessionRequest
{
    SignerName = "Maria Oliveira",
    SignerEmail = "maria@empresa.com",
    SignerUserExternalId = "user-001",
    PolicyProfile = "CLICK_ONLY",
    SignerIndex = 1,
});
Console.WriteLine(session1.Url); // URL de assinatura

// Signatário 2 — Diretor Jurídico
var session2 = await client.Envelopes.AddSessionAsync(envelope.EnvelopeId, new AddEnvelopeSessionRequest
{
    SignerName = "Carlos Santos",
    SignerEmail = "carlos@empresa.com",
    SignerUserExternalId = "user-002",
    PolicyProfile = "CLICK_ONLY",
    SignerIndex = 2,
});
Console.WriteLine(session2.Url); // URL de assinatura

4. Acompanhar Conclusão

Consulte o envelope para verificar o progresso. Quando todos os signatários concluírem, o status muda para COMPLETED.

const detail = await client.envelopes.get(envelope.envelopeId);

console.log(detail.status);             // ACTIVE ou COMPLETED
console.log(detail.completedSessions);  // 1 de 2
console.log(detail.totalSigners);       // 2

for (const session of detail.sessions) {
  console.log(`${session.signerName}: ${session.status}`);
}
detail = client.envelopes.get(envelope.envelope_id)

print(detail.status)              # ACTIVE ou COMPLETED
print(detail.completed_sessions)  # 1 de 2
print(detail.total_signers)       # 2

for session in detail.sessions:
    print(f'{session.signer_name}: {session.status}')
detail, err := client.Envelopes.Get(ctx, envelope.EnvelopeID)
if err != nil {
    log.Fatal(err)
}

fmt.Println(detail.Status)             // ACTIVE ou COMPLETED
fmt.Println(detail.CompletedSessions)  // 1 de 2
fmt.Println(detail.TotalSigners)       // 2

for _, s := range detail.Sessions {
    fmt.Printf("%s: %s\n", s.SignerName, s.Status)
}
EnvelopeDetail detail = client.envelopes().get(envelope.getEnvelopeId());

System.out.println(detail.getStatus());             // ACTIVE ou COMPLETED
System.out.println(detail.getCompletedSessions());  // 1 de 2
System.out.println(detail.getTotalSigners());       // 2

for (EnvelopeSessionSummary s : detail.getSessions()) {
    System.out.println(s.getSignerName() + ": " + s.getStatus());
}
$detail = $client->envelopes->get($envelope->envelopeId);

echo $detail->status;             // ACTIVE ou COMPLETED
echo $detail->completedSessions;  // 1 de 2
echo $detail->totalSigners;       // 2

foreach ($detail->sessions as $session) {
    echo "{$session->signerName}: {$session->status}\n";
}
var detail = await client.Envelopes.GetAsync(envelope.EnvelopeId);

Console.WriteLine(detail.Status);             // ACTIVE ou COMPLETED
Console.WriteLine(detail.CompletedSessions);  // 1 de 2
Console.WriteLine(detail.TotalSigners);       // 2

foreach (var session in detail.Sessions)
{
    Console.WriteLine($"{session.SignerName}: {session.Status}");
}
Webhook: Você também pode receber notificações em tempo real configurando um webhook para o evento envelope.completed. Consulte o guia de Webhooks para detalhes.

5. Carimbo Combinado

Após a conclusão de todas as sessões (status COMPLETED), gere o PDF combinado com todas as assinaturas embutidas.

const combined = await client.envelopes.combinedStamp(envelope.envelopeId);

console.log(combined.downloadUrl);  // URL pré-assinada para download
console.log(combined.signerCount);  // 2
console.log(combined.expiresIn);    // segundos até expirar
combined = client.envelopes.combined_stamp(envelope.envelope_id)

print(combined.download_url)  # URL pré-assinada para download
print(combined.signer_count)  # 2
print(combined.expires_in)    # segundos até expirar
combined, err := client.Envelopes.CombinedStamp(ctx, envelope.EnvelopeID)
if err != nil {
    log.Fatal(err)
}

fmt.Println(combined.DownloadURL)  // URL pré-assinada para download
fmt.Println(combined.SignerCount)  // 2
fmt.Println(combined.ExpiresIn)    // segundos até expirar
EnvelopeCombinedStampResponse combined = client.envelopes().combinedStamp(envelope.getEnvelopeId());

System.out.println(combined.getDownloadUrl());  // URL pré-assinada para download
System.out.println(combined.getSignerCount());  // 2
System.out.println(combined.getExpiresIn());    // segundos até expirar
$combined = $client->envelopes->combinedStamp($envelope->envelopeId);

echo $combined->downloadUrl;  // URL pré-assinada para download
echo $combined->signerCount;  // 2
echo $combined->expiresIn;    // segundos até expirar
var combined = await client.Envelopes.CombinedStampAsync(envelope.EnvelopeId);

Console.WriteLine(combined.DownloadUrl);  // URL pré-assinada para download
Console.WriteLine(combined.SignerCount);  // 2
Console.WriteLine(combined.ExpiresIn);    // segundos até expirar

6. Exemplo Completo

Fluxo ponta a ponta: criar envelope, adicionar 2 signatários, aguardar conclusão e obter o PDF combinado.

import { SignDocsBrasilClient } from '@signdocs-brasil/api';
import { readFileSync } from 'fs';

const client = new SignDocsBrasilClient({
  clientId: process.env.SIGNDOCS_CLIENT_ID!,
  clientSecret: process.env.SIGNDOCS_CLIENT_SECRET!,
});

// 1. Criar envelope
const pdfBase64 = readFileSync('contrato.pdf').toString('base64');
const envelope = await client.envelopes.create({
  signingMode: 'PARALLEL',
  totalSigners: 2,
  document: { content: pdfBase64, filename: 'contrato.pdf' },
});
console.log('Envelope criado:', envelope.envelopeId);

// 2. Adicionar signatários
const session1 = await client.envelopes.addSession(envelope.envelopeId, {
  signer: { name: 'Maria Oliveira', email: 'maria@empresa.com', userExternalId: 'user-001' },
  policy: { profile: 'CLICK_ONLY' },
  signerIndex: 1,
});

const session2 = await client.envelopes.addSession(envelope.envelopeId, {
  signer: { name: 'Carlos Santos', email: 'carlos@empresa.com', userExternalId: 'user-002' },
  policy: { profile: 'CLICK_ONLY' },
  signerIndex: 2,
});

console.log('Signatário 1 URL:', session1.url);
console.log('Signatário 2 URL:', session2.url);

// 3. Aguardar conclusão (polling)
let detail = await client.envelopes.get(envelope.envelopeId);
while (detail.status !== 'COMPLETED') {
  await new Promise(r => setTimeout(r, 5000));
  detail = await client.envelopes.get(envelope.envelopeId);
  console.log(`Progresso: ${detail.completedSessions}/${detail.totalSigners}`);
}

// 4. Obter PDF combinado
const combined = await client.envelopes.combinedStamp(envelope.envelopeId);
console.log('Download:', combined.downloadUrl);
import time
from signdocs_brasil import SignDocsBrasilClient, ClientConfig
from signdocs_brasil.models.envelope import (
    CreateEnvelopeRequest,
    AddEnvelopeSessionRequest,
)

client = SignDocsBrasilClient(ClientConfig(
    client_id='YOUR_CLIENT_ID',
    client_secret='YOUR_CLIENT_SECRET',
))

# 1. Criar envelope
import base64
pdf_base64 = base64.b64encode(open('contrato.pdf', 'rb').read()).decode()
envelope = client.envelopes.create(CreateEnvelopeRequest(
    signing_mode='PARALLEL',
    total_signers=2,
    document_content=pdf_base64,
    document_filename='contrato.pdf',
))
print('Envelope criado:', envelope.envelope_id)

# 2. Adicionar signatários
session1 = client.envelopes.add_session(
    envelope.envelope_id,
    AddEnvelopeSessionRequest(
        signer_name='Maria Oliveira',
        signer_email='maria@empresa.com',
        signer_user_external_id='user-001',
        policy_profile='CLICK_ONLY',
        signer_index=1,
    ),
)

session2 = client.envelopes.add_session(
    envelope.envelope_id,
    AddEnvelopeSessionRequest(
        signer_name='Carlos Santos',
        signer_email='carlos@empresa.com',
        signer_user_external_id='user-002',
        policy_profile='CLICK_ONLY',
        signer_index=2,
    ),
)

print('Signatário 1 URL:', session1.url)
print('Signatário 2 URL:', session2.url)

# 3. Aguardar conclusão (polling)
detail = client.envelopes.get(envelope.envelope_id)
while detail.status != 'COMPLETED':
    time.sleep(5)
    detail = client.envelopes.get(envelope.envelope_id)
    print(f'Progresso: {detail.completed_sessions}/{detail.total_signers}')

# 4. Obter PDF combinado
combined = client.envelopes.combined_stamp(envelope.envelope_id)
print('Download:', combined.download_url)
package main

import (
    "context"
    "encoding/base64"
    "fmt"
    "log"
    "os"
    "time"

    signdocs "github.com/signdocs-brasil/signdocs-go"
)

func main() {
    ctx := context.Background()
    client, _ := signdocs.NewClient(
        os.Getenv("SIGNDOCS_CLIENT_ID"),
        os.Getenv("SIGNDOCS_CLIENT_SECRET"),
    )

    // 1. Criar envelope
    pdfBytes, _ := os.ReadFile("contrato.pdf")
    pdfBase64 := base64.StdEncoding.EncodeToString(pdfBytes)

    envelope, err := client.Envelopes.Create(ctx, &signdocs.CreateEnvelopeRequest{
        SigningMode:  "PARALLEL",
        TotalSigners: 2,
        Document: signdocs.EnvelopeDocument{
            Content:  pdfBase64,
            Filename: "contrato.pdf",
        },
    })
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Envelope criado:", envelope.EnvelopeID)

    // 2. Adicionar signatários
    session1, err := client.Envelopes.AddSession(ctx, envelope.EnvelopeID, &signdocs.AddEnvelopeSessionRequest{
        Signer:     signdocs.EnvelopeSessionSigner{Name: "Maria Oliveira", Email: "maria@empresa.com", UserExternalID: "user-001"},
        Policy:     signdocs.EnvelopeSessionPolicy{Profile: "CLICK_ONLY"},
        SignerIndex: 1,
    })
    if err != nil {
        log.Fatal(err)
    }

    session2, err := client.Envelopes.AddSession(ctx, envelope.EnvelopeID, &signdocs.AddEnvelopeSessionRequest{
        Signer:     signdocs.EnvelopeSessionSigner{Name: "Carlos Santos", Email: "carlos@empresa.com", UserExternalID: "user-002"},
        Policy:     signdocs.EnvelopeSessionPolicy{Profile: "CLICK_ONLY"},
        SignerIndex: 2,
    })
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("Signatário 1 URL:", session1.URL)
    fmt.Println("Signatário 2 URL:", session2.URL)

    // 3. Aguardar conclusão (polling)
    for {
        detail, err := client.Envelopes.Get(ctx, envelope.EnvelopeID)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Printf("Progresso: %d/%d\n", detail.CompletedSessions, detail.TotalSigners)
        if detail.Status == "COMPLETED" {
            break
        }
        time.Sleep(5 * time.Second)
    }

    // 4. Obter PDF combinado
    combined, err := client.Envelopes.CombinedStamp(ctx, envelope.EnvelopeID)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Download:", combined.DownloadURL)
}
import com.signdocsbrasil.api.SignDocsBrasilClient;
import com.signdocsbrasil.api.models.*;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;

public class EnvelopeExample {
    public static void main(String[] args) throws Exception {
        SignDocsBrasilClient client = new SignDocsBrasilClient(
            System.getenv("SIGNDOCS_CLIENT_ID"),
            System.getenv("SIGNDOCS_CLIENT_SECRET")
        );

        // 1. Criar envelope
        byte[] pdfBytes = Files.readAllBytes(Path.of("contrato.pdf"));
        String pdfBase64 = Base64.getEncoder().encodeToString(pdfBytes);

        CreateEnvelopeRequest envReq = new CreateEnvelopeRequest();
        envReq.setSigningMode("PARALLEL");
        envReq.setTotalSigners(2);
        envReq.setDocument(pdfBase64, "contrato.pdf");

        Envelope envelope = client.envelopes().create(envReq);
        System.out.println("Envelope criado: " + envelope.getEnvelopeId());

        // 2. Adicionar signatários
        AddEnvelopeSessionRequest req1 = new AddEnvelopeSessionRequest();
        req1.setSignerName("Maria Oliveira");
        req1.setSignerEmail("maria@empresa.com");
        req1.setSignerUserExternalId("user-001");
        req1.setPolicyProfile("CLICK_ONLY");
        req1.setSignerIndex(1);
        EnvelopeSession session1 = client.envelopes().addSession(envelope.getEnvelopeId(), req1);

        AddEnvelopeSessionRequest req2 = new AddEnvelopeSessionRequest();
        req2.setSignerName("Carlos Santos");
        req2.setSignerEmail("carlos@empresa.com");
        req2.setSignerUserExternalId("user-002");
        req2.setPolicyProfile("CLICK_ONLY");
        req2.setSignerIndex(2);
        EnvelopeSession session2 = client.envelopes().addSession(envelope.getEnvelopeId(), req2);

        System.out.println("Signatário 1 URL: " + session1.getUrl());
        System.out.println("Signatário 2 URL: " + session2.getUrl());

        // 3. Aguardar conclusão (polling)
        EnvelopeDetail detail;
        do {
            Thread.sleep(5000);
            detail = client.envelopes().get(envelope.getEnvelopeId());
            System.out.printf("Progresso: %d/%d%n", detail.getCompletedSessions(), detail.getTotalSigners());
        } while (!"COMPLETED".equals(detail.getStatus()));

        // 4. Obter PDF combinado
        EnvelopeCombinedStampResponse combined = client.envelopes().combinedStamp(envelope.getEnvelopeId());
        System.out.println("Download: " + combined.getDownloadUrl());
    }
}
<?php
use SignDocsBrasil\Api\SignDocsBrasilClient;
use SignDocsBrasil\Api\Models\CreateEnvelopeRequest;
use SignDocsBrasil\Api\Models\AddEnvelopeSessionRequest;

$client = new SignDocsBrasilClient(
    clientId: getenv('SIGNDOCS_CLIENT_ID'),
    clientSecret: getenv('SIGNDOCS_CLIENT_SECRET'),
);

// 1. Criar envelope
$pdfBase64 = base64_encode(file_get_contents('contrato.pdf'));
$envelope = $client->envelopes->create(new CreateEnvelopeRequest(
    signingMode: 'PARALLEL',
    totalSigners: 2,
    documentContent: $pdfBase64,
    documentFilename: 'contrato.pdf',
));
echo "Envelope criado: {$envelope->envelopeId}\n";

// 2. Adicionar signatários
$session1 = $client->envelopes->addSession($envelope->envelopeId, new AddEnvelopeSessionRequest(
    signerName: 'Maria Oliveira',
    signerEmail: 'maria@empresa.com',
    signerUserExternalId: 'user-001',
    policyProfile: 'CLICK_ONLY',
    signerIndex: 1,
));

$session2 = $client->envelopes->addSession($envelope->envelopeId, new AddEnvelopeSessionRequest(
    signerName: 'Carlos Santos',
    signerEmail: 'carlos@empresa.com',
    signerUserExternalId: 'user-002',
    policyProfile: 'CLICK_ONLY',
    signerIndex: 2,
));

echo "Signatário 1 URL: {$session1->url}\n";
echo "Signatário 2 URL: {$session2->url}\n";

// 3. Aguardar conclusão (polling)
do {
    sleep(5);
    $detail = $client->envelopes->get($envelope->envelopeId);
    echo "Progresso: {$detail->completedSessions}/{$detail->totalSigners}\n";
} while ($detail->status !== 'COMPLETED');

// 4. Obter PDF combinado
$combined = $client->envelopes->combinedStamp($envelope->envelopeId);
echo "Download: {$combined->downloadUrl}\n";
using SignDocsBrasil.Api;
using SignDocsBrasil.Api.Models;

var client = new SignDocsBrasilClient(new ClientConfig
{
    ClientId = Environment.GetEnvironmentVariable("SIGNDOCS_CLIENT_ID")!,
    ClientSecret = Environment.GetEnvironmentVariable("SIGNDOCS_CLIENT_SECRET")!,
});

// 1. Criar envelope
var pdfBase64 = Convert.ToBase64String(File.ReadAllBytes("contrato.pdf"));
var envelope = await client.Envelopes.CreateAsync(new CreateEnvelopeRequest
{
    SigningMode = "PARALLEL",
    TotalSigners = 2,
    DocumentContent = pdfBase64,
    DocumentFilename = "contrato.pdf",
});
Console.WriteLine($"Envelope criado: {envelope.EnvelopeId}");

// 2. Adicionar signatários
var session1 = await client.Envelopes.AddSessionAsync(envelope.EnvelopeId, new AddEnvelopeSessionRequest
{
    SignerName = "Maria Oliveira",
    SignerEmail = "maria@empresa.com",
    SignerUserExternalId = "user-001",
    PolicyProfile = "CLICK_ONLY",
    SignerIndex = 1,
});

var session2 = await client.Envelopes.AddSessionAsync(envelope.EnvelopeId, new AddEnvelopeSessionRequest
{
    SignerName = "Carlos Santos",
    SignerEmail = "carlos@empresa.com",
    SignerUserExternalId = "user-002",
    PolicyProfile = "CLICK_ONLY",
    SignerIndex = 2,
});

Console.WriteLine($"Signatário 1 URL: {session1.Url}");
Console.WriteLine($"Signatário 2 URL: {session2.Url}");

// 3. Aguardar conclusão (polling)
EnvelopeDetail detail;
do
{
    await Task.Delay(5000);
    detail = await client.Envelopes.GetAsync(envelope.EnvelopeId);
    Console.WriteLine($"Progresso: {detail.CompletedSessions}/{detail.TotalSigners}");
} while (detail.Status != "COMPLETED");

// 4. Obter PDF combinado
var combined = await client.Envelopes.CombinedStampAsync(envelope.EnvelopeId);
Console.WriteLine($"Download: {combined.DownloadUrl}");

Resumo do fluxo

Criar Envelope (documento + modo + totalSigners)
    │
    ▼
Adicionar Sessão 1 (signatário + política)
    │
    ▼
Adicionar Sessão 2 (signatário + política)
    │
    ▼
Aguardar conclusão (polling ou webhook)
    │
    ▼
Obter Carimbo Combinado (PDF com todas as assinaturas)