Guias de Desenvolvimento

Signing Sessions

Guias dos SDKs

Webhooks

Webhooks permitem receber notificações em tempo real sobre eventos da API. A SignDocsBrasil envia requisições HTTP POST para a URL registrada quando eventos ocorrem.


Passo 1: Registrar webhook

const webhook = await client.webhooks.register({
  url: 'https://example.com/webhooks/signdocs',
  events: ['TRANSACTION.COMPLETED', 'TRANSACTION.FAILED', 'STEP.COMPLETED'],
});
console.log(webhook.webhookId);
console.log(webhook.secret); // Salve este valor — só é retornado uma vez!
from signdocs_brasil.models import RegisterWebhookRequest

webhook = client.webhooks.register(RegisterWebhookRequest(
    url='https://example.com/webhooks/signdocs',
    events=['TRANSACTION.COMPLETED', 'TRANSACTION.FAILED', 'STEP.COMPLETED'],
))
print(webhook.webhook_id)
print(webhook.secret)  # Salve — só é retornado uma vez!
webhook, _ := client.Webhooks.Register(ctx, &signdocs.RegisterWebhookRequest{
    URL:    "https://example.com/webhooks/signdocs",
    Events: []signdocs.WebhookEventType{
        signdocs.WebhookEventTransactionCompleted,
        signdocs.WebhookEventTransactionFailed,
        signdocs.WebhookEventStepCompleted,
    },
})
fmt.Println(webhook.WebhookID)
fmt.Println(webhook.Secret) // Salve — só é retornado uma vez!
RegisterWebhookResponse webhook = client.webhooks().register(
    new RegisterWebhookRequest(
        "https://example.com/webhooks/signdocs",
        List.of("TRANSACTION.COMPLETED", "TRANSACTION.FAILED", "STEP.COMPLETED")
    ));
System.out.println(webhook.webhookId);
System.out.println(webhook.secret); // Salve — só é retornado uma vez!
$webhook = $client->webhooks->register(new RegisterWebhookRequest(
    url: 'https://example.com/webhooks/signdocs',
    events: ['TRANSACTION.COMPLETED', 'TRANSACTION.FAILED', 'STEP.COMPLETED'],
));
echo $webhook->webhookId;
echo $webhook->secret; // Salve — só é retornado uma vez!

Eventos Disponíveis

Evento Descrição
TRANSACTION.CREATED Transação criada
TRANSACTION.COMPLETED Transação finalizada com sucesso
TRANSACTION.CANCELLED Transação cancelada
TRANSACTION.FAILED Transação falhou
TRANSACTION.EXPIRED Transação expirou
TRANSACTION.FALLBACK Fallback acionado
STEP.STARTED Etapa iniciada
STEP.COMPLETED Etapa concluída
STEP.FAILED Etapa falhou
QUOTA.WARNING Limite de cota próximo
API.DEPRECATION_NOTICE Aviso de descontinuação

Passo 2: Verificar assinatura HMAC-SHA256

Cada webhook enviado inclui dois headers:

Header Descrição
X-SignDocs-Signature Assinatura HMAC-SHA256 em hexadecimal
X-SignDocs-Timestamp Timestamp Unix (segundos) do envio

Formato de assinatura: HMAC-SHA256("{timestamp}.{body}", secret)

Tolerância padrão: 300 segundos (5 minutos).

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

app.post('/webhooks/signdocs', express.text({ type: '*/*' }), (req, res) => {
  const valid = verifyWebhookSignature(
    req.body,
    req.headers['x-signdocs-signature'] as string,
    req.headers['x-signdocs-timestamp'] as string,
    webhookSecret,
  );

  if (!valid) {
    return res.status(401).send('Assinatura inválida');
  }

  const payload = JSON.parse(req.body);
  console.log(payload.eventType, payload.transactionId);
  res.status(200).send('OK');
});
from flask import Flask, request
from signdocs_brasil import verify_webhook_signature

@app.route('/webhooks/signdocs', methods=['POST'])
def handle_webhook():
    body = request.get_data(as_text=True)
    signature = request.headers.get('X-SignDocs-Signature')
    timestamp = request.headers.get('X-SignDocs-Timestamp')

    if not verify_webhook_signature(body, signature, timestamp, webhook_secret):
        return 'Assinatura inválida', 401

    payload = request.get_json()
    print(payload['eventType'], payload.get('transactionId'))
    return 'OK', 200
import (
    "io"
    "net/http"
    signdocs "github.com/signdocsbrasil/signdocsbrasil-go"
)

func webhookHandler(w http.ResponseWriter, r *http.Request) {
    body, _ := io.ReadAll(r.Body)

    valid := signdocs.VerifyWebhookSignature(
        string(body),
        r.Header.Get("X-SignDocs-Signature"),
        r.Header.Get("X-SignDocs-Timestamp"),
        webhookSecret,
    )

    if !valid {
        http.Error(w, "Assinatura inválida", http.StatusUnauthorized)
        return
    }

    // Processar evento...
    w.WriteHeader(http.StatusOK)
}
import com.signdocsbrasil.api.WebhookVerifier;
import java.util.stream.Collectors;

// Em um servlet ou controller Spring
String body = request.getReader().lines().collect(Collectors.joining());
String signature = request.getHeader("X-SignDocs-Signature");
String timestamp = request.getHeader("X-SignDocs-Timestamp");

boolean valid = WebhookVerifier.verifySignature(body, signature, timestamp, webhookSecret);

if (!valid) {
    response.setStatus(401);
    return;
}

// Processar evento...
use SignDocsBrasil\Api\WebhookVerifier;

$body = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_SIGNDOCS_SIGNATURE'] ?? '';
$timestamp = $_SERVER['HTTP_X_SIGNDOCS_TIMESTAMP'] ?? '';

$valid = WebhookVerifier::verify($body, $signature, $timestamp, $webhookSecret);

if (!$valid) {
    http_response_code(401);
    echo 'Assinatura inválida';
    exit;
}

$payload = json_decode($body, true);
// Processar evento...

Tolerância personalizada

verifyWebhookSignature(body, sig, ts, secret, { toleranceSeconds: 600 });
verify_webhook_signature(body, sig, ts, secret, tolerance_seconds=600)
signdocs.VerifyWebhookSignature(body, sig, ts, secret,
    signdocs.WithToleranceSeconds(600))
WebhookVerifier.verifySignature(body, sig, ts, secret, 600);
WebhookVerifier::verify($body, $sig, $ts, $secret, toleranceSeconds: 600);

Passo 3: Testar webhook

Envia uma entrega de teste para o endpoint registrado.

const test = await client.webhooks.test(webhook.webhookId);
console.log(test.deliveryId);
console.log(test.status);     // "delivered" ou "failed"
console.log(test.statusCode); // HTTP status retornado pelo seu endpoint
test = client.webhooks.test(webhook.webhook_id)
print(test.delivery_id, test.status)
test, _ := client.Webhooks.Test(ctx, webhook.WebhookID)
fmt.Println(test.DeliveryID, test.Status)
WebhookTestResponse test = client.webhooks().test(webhook.webhookId);
System.out.println(test.deliveryId + " " + test.status);
$test = $client->webhooks->test($webhook->webhookId);
echo $test->deliveryId . ' ' . $test->status;

Gerenciar webhooks

Listar

const webhooks = await client.webhooks.list();
for (const wh of webhooks) {
  console.log(wh.webhookId, wh.url, wh.events, wh.status);
}
webhooks = client.webhooks.list()
for wh in webhooks:
    print(wh.webhook_id, wh.url, wh.events, wh.status)
webhooks, _ := client.Webhooks.List(ctx)
for _, wh := range webhooks {
    fmt.Println(wh.WebhookID, wh.URL, wh.Events, wh.Status)
}
List<Webhook> webhooks = client.webhooks().list();
for (Webhook wh : webhooks) {
    System.out.println(wh.webhookId + " " + wh.url);
}
$webhooks = $client->webhooks->list();
foreach ($webhooks as $wh) {
    echo $wh->webhookId . ' ' . $wh->url . PHP_EOL;
}

Deletar

await client.webhooks.delete(webhookId);
client.webhooks.delete(webhook_id)
client.Webhooks.Delete(ctx, webhookID)
client.webhooks().delete(webhookId);
$client->webhooks->delete($webhookId);