Guias de Desenvolvimento

Política e Conformidade

Signing Sessions

Guias dos SDKs

Assinatura Expressa (Signing Sessions)

Procurando scripts prontos para copiar e colar? Veja as Receitas de Integração.

Signing Sessions oferecem uma abstracao de alto nivel sobre a Transaction API. Uma unica chamada cria a transacao, configura as etapas e gera a URL de checkout — 1 chamada para criar, 1 polling para acompanhar.

Formatos suportados: Além de PDF, a assinatura expressa aceita documentos em diversos formatos (DOCX, XLSX, PPTX, ODT, imagens, TXT, CSV, XML, HTML, entre outros). Para documentos não-PDF, o campo filename é obrigatório e determina o formato. Documentos PDF utilizam assinatura PAdES (embutida no PDF); demais formatos utilizam assinatura CAdES (arquivo .p7s separado).


Comparacao: Transaction API vs Signing Sessions

Aspecto Transaction API Signing Sessions
Chamadas para assinar 8+ (criar, etapas, etc) 1 (create) + poll/webhook
Controle granular Total Abstraido
URL de checkout Gerar manualmente Retornada automaticamente
Webhook TRANSACTION.* SIGNING_SESSION.*

Passo 1: Criar sessao

Cria a sessao com perfil CLICK_ONLY e documento inline. Retorna sessionId, url e clientSecret.

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

const session = await client.signingSessions.create({
  purpose: 'DOCUMENT_SIGNATURE',
  policy: { profile: 'CLICK_ONLY' },
  signer: { name: 'Joao Silva', email: 'joao@example.com', userExternalId: 'user-001' },
  document: { content: documentBase64, filename: 'contrato.pdf' },
  returnUrl: 'https://app.example.com/done',
  locale: 'pt-BR',
  expiresInMinutes: 60,
});
console.log(session.sessionId, session.url);
from signdocs_brasil import SignDocsBrasilClient, ClientConfig
from signdocs_brasil.models import (
    CreateSigningSessionRequest, Policy, Signer, InlineDocument,
)

session = client.signing_sessions.create(CreateSigningSessionRequest(
    purpose='DOCUMENT_SIGNATURE',
    policy=Policy(profile='CLICK_ONLY'),
    signer=Signer(name='Joao Silva', email='joao@example.com', user_external_id='user-001'),
    document=InlineDocument(content=document_base64, filename='contrato.pdf'),
    return_url='https://app.example.com/done',
    locale='pt-BR',
    expires_in_minutes=60,
))
print(session.session_id, session.url)
session, err := client.SigningSessions.Create(ctx, &signdocs.CreateSigningSessionRequest{
    Purpose:          signdocs.PurposeDocumentSignature,
    Policy:           signdocs.Policy{Profile: signdocs.PolicyProfileClickOnly},
    Signer:           signdocs.Signer{Name: "Joao Silva", Email: "joao@example.com", UserExternalID: "user-001"},
    Document:         &signdocs.DocumentInline{Content: documentBase64, Filename: "contrato.pdf"},
    ReturnURL:        "https://app.example.com/done",
    Locale:           "pt-BR",
    ExpiresInMinutes: 60,
})
if err != nil { log.Fatal(err) }
fmt.Println(session.SessionID, session.URL)
CreateSigningSessionRequest request = new CreateSigningSessionRequest();
request.purpose = "DOCUMENT_SIGNATURE";
request.policy = new Policy("CLICK_ONLY");
request.signer = new Signer("Joao Silva", "joao@example.com", "user-001");
request.document = new CreateSigningSessionRequest.InlineDocument(documentBase64, "contrato.pdf");
request.returnUrl = "https://app.example.com/done";
request.locale = "pt-BR";
request.expiresInMinutes = 60;

SigningSession session = client.signingSessions().create(request);
System.out.println(session.sessionId + " " + session.url);
use SignDocsBrasil\Api\Models\CreateSigningSessionRequest;
use SignDocsBrasil\Api\Models\Policy;
use SignDocsBrasil\Api\Models\Signer;

$session = $client->signingSessions->create(new CreateSigningSessionRequest(
    purpose: 'DOCUMENT_SIGNATURE',
    policy: new Policy(profile: 'CLICK_ONLY'),
    signer: new Signer(name: 'Joao Silva', email: 'joao@example.com', userExternalId: 'user-001'),
    document: ['content' => $documentBase64, 'filename' => 'contrato.pdf'],
    returnUrl: 'https://app.example.com/done',
    locale: 'pt-BR',
    expiresInMinutes: 60,
));
echo $session->sessionId . ' ' . $session->url;
using SignDocsBrasil.Api;
using SignDocsBrasil.Api.Models;

var session = await client.SigningSessions.CreateAsync(new CreateSigningSessionRequest
{
    Purpose = "DOCUMENT_SIGNATURE",
    Policy = new Policy { Profile = "CLICK_ONLY" },
    Signer = new Signer { Name = "Joao Silva", Email = "joao@example.com", UserExternalId = "user-001" },
    Document = new InlineDocument { Content = documentBase64, Filename = "contrato.pdf" },
    ReturnUrl = "https://app.example.com/done",
    Locale = "pt-BR",
    ExpiresInMinutes = 60,
});
Console.WriteLine($"{session.SessionId} {session.Url}");

Passo 2: Redirecionar ou Checkout embutido

<!-- Redirecionamento -->
<a href="{{ session.url }}">Assinar documento</a>

<!-- Checkout embutido -->
<div id="signdocs-checkout"></div>
<script src="https://cdn.signdocs.com.br/v1/signdocs-brasil.js"></script>
<script>
  const sd = SignDocsBrasil.init({ locale: 'pt-BR' });
  sd.checkout({
    clientSecret: '{{ session.clientSecret }}',
    onComplete: function (event) { window.location.href = '/done'; },
    onError: function (event) { console.error(event.code); },
    onClose: function () { console.log('Cancelado'); },
  });
</script>

Passo 3: Polling com waitForCompletion

Retorna quando o status for COMPLETED, CANCELLED ou EXPIRED.

const result = await client.signingSessions.waitForCompletion(session.sessionId, {
  timeoutMs: 300_000, intervalMs: 2_000,
});
console.log(result.status, result.evidenceId); // COMPLETED
result = client.signing_sessions.wait_for_completion(
    session.session_id, timeout_ms=300_000, interval_ms=2_000)
print(result.status, result.evidence_id)  # COMPLETED
result, err := client.SigningSessions.WaitForCompletion(ctx, session.SessionID, &signdocs.WaitOptions{
    TimeoutMs: 300_000, IntervalMs: 2_000,
})
if err != nil { log.Fatal(err) }
fmt.Println(result.Status, result.EvidenceID)
SigningSessionStatusResponse result = client.signingSessions().waitForCompletion(
    session.sessionId, new WaitOptions(300_000, 2_000));
System.out.println(result.status + " " + result.evidenceId);
$result = $client->signingSessions->waitForCompletion($session->sessionId, [
    'timeoutMs' => 300_000, 'intervalMs' => 2_000,
]);
echo $result->status . ' ' . $result->evidenceId;
var result = await client.SigningSessions.WaitForCompletionAsync(session.SessionId,
    new WaitOptions { TimeoutMs = 300_000, IntervalMs = 2_000 });
Console.WriteLine($"Status: {result.Status}");
Console.WriteLine($"Evidence ID: {result.EvidenceId}");

Passo 4: Webhook

Registre um webhook para SIGNING_SESSION.COMPLETED em vez de polling.

{
  "event": "SIGNING_SESSION.COMPLETED",
  "payload": {
    "sessionId": "ss_abc123", "transactionId": "tx_def456",
    "status": "COMPLETED", "evidenceId": "ev_ghi789",
    "completedAt": "2026-03-10T14:30:00Z"
  }
}

Outros eventos: SIGNING_SESSION.EXPIRED, SIGNING_SESSION.CANCELLED.


Passo 5: Cancelar sessao

const cancelled = await client.signingSessions.cancel(session.sessionId);
console.log(cancelled.status, cancelled.cancelledAt); // CANCELLED
cancelled = client.signing_sessions.cancel(session.session_id)
print(cancelled.status, cancelled.cancelled_at)  # CANCELLED
cancelled, err := client.SigningSessions.Cancel(ctx, session.SessionID)
if err != nil { log.Fatal(err) }
fmt.Println(cancelled.Status, cancelled.CancelledAt)
CancelSigningSessionResponse cancelled = client.signingSessions().cancel(session.sessionId);
System.out.println(cancelled.status + " " + cancelled.cancelledAt);
$cancelled = $client->signingSessions->cancel($session->sessionId);
echo $cancelled->status . ' ' . $cancelled->cancelledAt;
var cancelled = await client.SigningSessions.CancelAsync(session.SessionId);
Console.WriteLine($"{cancelled.Status} {cancelled.CancelledAt}");

Passo 6: Listar sessoes

const list = await client.signingSessions.list({ status: 'COMPLETED', limit: 20 });
for (const s of list.sessions) console.log(s.sessionId, s.status, s.createdAt);
if (list.nextCursor) await client.signingSessions.list({ status: 'COMPLETED', cursor: list.nextCursor });
result = client.signing_sessions.list(ListSigningSessionsRequest(status='COMPLETED', limit=20))
for s in result.sessions:
    print(s.session_id, s.status, s.created_at)
result, err := client.SigningSessions.List(ctx, &signdocs.ListSigningSessionsRequest{
    Status: "COMPLETED", Limit: 20,
})
if err != nil { log.Fatal(err) }
for _, s := range result.Sessions { fmt.Println(s.SessionID, s.Status) }
ListSigningSessionsRequest listReq = new ListSigningSessionsRequest();
listReq.status = "COMPLETED";
listReq.limit = 20;
SigningSessionListResponse result = client.signingSessions().list(listReq);
for (SigningSessionSummary s : result.sessions) System.out.println(s.sessionId);
$result = $client->signingSessions->list(['status' => 'COMPLETED', 'limit' => 20]);
foreach ($result->sessions as $s) echo $s->sessionId . ' ' . $s->status . "\n";
var list = await client.SigningSessions.ListAsync(new ListSigningSessionsRequest
{
    Status = "COMPLETED",
    Limit = 20,
});
foreach (var s in list.Sessions)
    Console.WriteLine($"{s.SessionId} {s.Status} {s.CreatedAt}");

OTP (CLICK_PLUS_OTP)

Adiciona verificacao por codigo OTP. No sandbox, sandbox.otpCode retorna o codigo para testes.

const otp = await client.signingSessions.create({
  purpose: 'DOCUMENT_SIGNATURE',
  policy: { profile: 'CLICK_PLUS_OTP' },
  signer: { name: 'Maria Souza', email: 'maria@example.com', userExternalId: 'user-002' },
  document: { content: documentBase64, filename: 'contrato.pdf' },
});
console.log(otp.url); // sandbox: otp.sandbox.otpCode
otp = client.signing_sessions.create(CreateSigningSessionRequest(
    purpose='DOCUMENT_SIGNATURE', policy=Policy(profile='CLICK_PLUS_OTP'),
    signer=Signer(name='Maria Souza', email='maria@example.com', user_external_id='user-002'),
    document=InlineDocument(content=document_base64, filename='contrato.pdf'),
))  # sandbox: otp.sandbox.otp_code
otp, err := client.SigningSessions.Create(ctx, &signdocs.CreateSigningSessionRequest{
    Purpose:  signdocs.PurposeDocumentSignature,
    Policy:   signdocs.Policy{Profile: signdocs.PolicyProfileClickPlusOTP},
    Signer:   signdocs.Signer{Name: "Maria Souza", Email: "maria@example.com", UserExternalID: "user-002"},
    Document: &signdocs.DocumentInline{Content: documentBase64, Filename: "contrato.pdf"},
}) // sandbox: otp.Sandbox.OTPCode
CreateSigningSessionRequest otpReq = new CreateSigningSessionRequest();
otpReq.purpose = "DOCUMENT_SIGNATURE";
otpReq.policy = new Policy("CLICK_PLUS_OTP");
otpReq.signer = new Signer("Maria Souza", "maria@example.com", "user-002");
otpReq.document = new CreateSigningSessionRequest.InlineDocument(documentBase64, "contrato.pdf");
SigningSession otp = client.signingSessions().create(otpReq); // sandbox: otp.sandbox.otpCode
$otp = $client->signingSessions->create(new CreateSigningSessionRequest(
    purpose: 'DOCUMENT_SIGNATURE', policy: new Policy(profile: 'CLICK_PLUS_OTP'),
    signer: new Signer(name: 'Maria Souza', email: 'maria@example.com', userExternalId: 'user-002'),
    document: ['content' => $documentBase64, 'filename' => 'contrato.pdf'],
)); // sandbox: $otp->sandbox->otpCode
var otp = await client.SigningSessions.CreateAsync(new CreateSigningSessionRequest
{
    Purpose = "DOCUMENT_SIGNATURE",
    Policy = new Policy { Profile = "CLICK_PLUS_OTP" },
    Signer = new Signer { Name = "Maria Souza", Email = "maria@example.com", UserExternalId = "user-002" },
    Document = new InlineDocument { Content = documentBase64, Filename = "contrato.pdf" },
});
Console.WriteLine($"URL: {otp.Url}"); // sandbox: otp.Sandbox.OtpCode

Biometria (BIOMETRIC)

Verificacao biometrica facial via liveness no checkout hospedado.

const bio = await client.signingSessions.create({
  purpose: 'DOCUMENT_SIGNATURE',
  policy: { profile: 'BIOMETRIC' },
  signer: { name: 'Carlos Lima', cpf: '123.456.789-00', userExternalId: 'user-003' },
  document: { content: documentBase64, filename: 'contrato.pdf' },
});
bio = client.signing_sessions.create(CreateSigningSessionRequest(
    purpose='DOCUMENT_SIGNATURE', policy=Policy(profile='BIOMETRIC'),
    signer=Signer(name='Carlos Lima', cpf='123.456.789-00', user_external_id='user-003'),
    document=InlineDocument(content=document_base64, filename='contrato.pdf'),
))
bio, _ := client.SigningSessions.Create(ctx, &signdocs.CreateSigningSessionRequest{
    Purpose:  signdocs.PurposeDocumentSignature,
    Policy:   signdocs.Policy{Profile: signdocs.PolicyProfileBiometric},
    Signer:   signdocs.Signer{Name: "Carlos Lima", CPF: "123.456.789-00", UserExternalID: "user-003"},
    Document: &signdocs.DocumentInline{Content: documentBase64, Filename: "contrato.pdf"},
})
CreateSigningSessionRequest bioReq = new CreateSigningSessionRequest();
bioReq.purpose = "DOCUMENT_SIGNATURE";
bioReq.policy = new Policy("BIOMETRIC");
bioReq.signer = new Signer("Carlos Lima", "user-003");
bioReq.signer.cpf = "123.456.789-00";
bioReq.document = new CreateSigningSessionRequest.InlineDocument(documentBase64, "contrato.pdf");
SigningSession bio = client.signingSessions().create(bioReq);
$bio = $client->signingSessions->create(new CreateSigningSessionRequest(
    purpose: 'DOCUMENT_SIGNATURE', policy: new Policy(profile: 'BIOMETRIC'),
    signer: new Signer(name: 'Carlos Lima', cpf: '123.456.789-00', userExternalId: 'user-003'),
    document: ['content' => $documentBase64, 'filename' => 'contrato.pdf'],
));
var bio = await client.SigningSessions.CreateAsync(new CreateSigningSessionRequest
{
    Purpose = "DOCUMENT_SIGNATURE",
    Policy = new Policy { Profile = "BIOMETRIC" },
    Signer = new Signer { Name = "Carlos Lima", Cpf = "123.456.789-00", UserExternalId = "user-003" },
    Document = new InlineDocument { Content = documentBase64, Filename = "contrato.pdf" },
});

Certificado Digital (DIGITAL_CERTIFICATE)

Assinatura via certificado digital ICP-Brasil (A1/A3).

const cert = await client.signingSessions.create({
  purpose: 'DOCUMENT_SIGNATURE',
  policy: { profile: 'DIGITAL_CERTIFICATE' },
  signer: { name: 'Ana Pereira', cpf: '987.654.321-00', userExternalId: 'user-004' },
  document: { content: documentBase64, filename: 'contrato.pdf' },
});
cert = client.signing_sessions.create(CreateSigningSessionRequest(
    purpose='DOCUMENT_SIGNATURE', policy=Policy(profile='DIGITAL_CERTIFICATE'),
    signer=Signer(name='Ana Pereira', cpf='987.654.321-00', user_external_id='user-004'),
    document=InlineDocument(content=document_base64, filename='contrato.pdf'),
))
cert, _ := client.SigningSessions.Create(ctx, &signdocs.CreateSigningSessionRequest{
    Purpose:  signdocs.PurposeDocumentSignature,
    Policy:   signdocs.Policy{Profile: signdocs.PolicyProfileDigitalCertificate},
    Signer:   signdocs.Signer{Name: "Ana Pereira", CPF: "987.654.321-00", UserExternalID: "user-004"},
    Document: &signdocs.DocumentInline{Content: documentBase64, Filename: "contrato.pdf"},
})
CreateSigningSessionRequest certReq = new CreateSigningSessionRequest();
certReq.purpose = "DOCUMENT_SIGNATURE";
certReq.policy = new Policy("DIGITAL_CERTIFICATE");
certReq.signer = new Signer("Ana Pereira", "user-004");
certReq.signer.cpf = "987.654.321-00";
certReq.document = new CreateSigningSessionRequest.InlineDocument(documentBase64, "contrato.pdf");
SigningSession cert = client.signingSessions().create(certReq);
$cert = $client->signingSessions->create(new CreateSigningSessionRequest(
    purpose: 'DOCUMENT_SIGNATURE', policy: new Policy(profile: 'DIGITAL_CERTIFICATE'),
    signer: new Signer(name: 'Ana Pereira', cpf: '987.654.321-00', userExternalId: 'user-004'),
    document: ['content' => $documentBase64, 'filename' => 'contrato.pdf'],
));
var cert = await client.SigningSessions.CreateAsync(new CreateSigningSessionRequest
{
    Purpose = "DOCUMENT_SIGNATURE",
    Policy = new Policy { Profile = "DIGITAL_CERTIFICATE" },
    Signer = new Signer { Name = "Ana Pereira", Cpf = "987.654.321-00", UserExternalId = "user-004" },
    Document = new InlineDocument { Content = documentBase64, Filename = "contrato.pdf" },
});

Personalizacao (Appearance)

Configure cores, logo e textos do checkout via appearance.

await client.signingSessions.create({
  // ...signer, document, policy (ver Passo 1)
  appearance: {
    brandColor: '#1A73E8', logoUrl: 'https://example.com/logo.png',
    title: 'Assine seu contrato', submitLabel: 'Concordo e assino',
  },
});
from signdocs_brasil.models import Appearance

client.signing_sessions.create(CreateSigningSessionRequest(
    # ...signer, document, policy (ver Passo 1)
    appearance=Appearance(
        brand_color='#1A73E8', logo_url='https://example.com/logo.png',
        title='Assine seu contrato', submit_label='Concordo e assino'),
))
client.SigningSessions.Create(ctx, &signdocs.CreateSigningSessionRequest{
    // ...Signer, Document, Policy (ver Passo 1)
    Appearance: &signdocs.Appearance{
        BrandColor: "#1A73E8", LogoURL: "https://example.com/logo.png",
        Title: "Assine seu contrato", SubmitLabel: "Concordo e assino",
    },
})
request.appearance = new Appearance("#1A73E8", "https://example.com/logo.png",
    "Assine seu contrato", "Concordo e assino");
// adicionar ao CreateSigningSessionRequest (ver Passo 1)
'appearance' => [
    'brandColor' => '#1A73E8', 'logoUrl' => 'https://example.com/logo.png',
    'title' => 'Assine seu contrato', 'submitLabel' => 'Concordo e assino',
],
// adicionar ao CreateSigningSessionRequest (ver Passo 1)
await client.SigningSessions.CreateAsync(new CreateSigningSessionRequest
{
    // ...Signer, Document, Policy (ver Passo 1)
    Appearance = new Appearance
    {
        BrandColor = "#1A73E8",
        LogoUrl = "https://example.com/logo.png",
        Title = "Assine seu contrato",
        SubmitLabel = "Concordo e assino",
    },
});

Resumo do fluxo

signingSessions.create ──> sessionId + url + clientSecret
        |
        v
  Redirecionar OU JS SDK checkout
        |
        v
  Signatario completa fluxo
        |
   ┌────┴────┐
   v         v
Webhook   Polling (waitForCompletion)
   |         |
   └────┬────┘
        v
  COMPLETED ──> evidenceId
        |
        v
  Evidencias / Download Documento