Integração com Node.js / TypeScript em poucos minutos

Exemplos prontos para copiar com fetch nativo (Node 18+): criar assinatura, consultar status, baixar o PDF e validar webhooks.

Sem bibliotecas extras: só HTTP. Execute sempre no servidor — a chave de API nunca vai para o navegador.

  1. 1.Carimbo e nome do anexo

    Na qualificada, escolha first, last ou all; page prevalece sobre pageIndex (0-based). delivery.filename na criação simples ou qualificada define o nome do anexo sem ativar envios. No reenvio, substitui só aquela chamada. Acentos preservados, extensão PDF. Cobrança por pessoa/canal, com estorno em falha; indisponível para contas Prescri.

    entrega.ts
    const sig = await client.createQualifiedSignature({
      provider: "birdid", documentName: "Relatório", signerCpf, pdfBase64,
      stampOptions: {page: "all", position: "bottom-right"},
      delivery: {filename: "Relatório Médico.pdf"},
    });
    // Depois de consultar status signed:
    await client.deliverSignature(sig.signature_id, ["email"], {filename: "Relatório Final.pdf"});
  2. 2.Envelopes: contrato e anexos no mesmo link

    Até 10 PDFs, mesmos signatários e confirmação única. Cada documento concluído consome uma unidade. Baixe o ZIP ou um PDF individual após a conclusão.

    envelope.ts
    const envelope = await client.createSimpleSignature({
      documentName: "Contrato e anexos",
      documents: [{document_name: "Contrato", pdf_base64: contratoBase64}, {document_name: "Anexo", pdf_base64: anexoBase64}],
      signers: [{full_name: "Maria Exemplo", email: "maria@example.com"}],
      sendEmailInvitation: true,
    });
    const zip = await client.downloadPdf(envelope.signature_id, {format: "zip"});
  3. 3.SDK leve (opcional, sem dependências)

    Prefere um cliente pronto? Copie a pasta sdk/node do repositório: um único arquivo com cliente, tipos TypeScript e validação de webhook.

    uso.ts
    import { AssinaturaClient, verifyWebhook } from "./assinatura-sdk/index.js";
    
    const client = new AssinaturaClient({ apiKey: process.env.ASSINATURA_API_KEY, apiSecret: process.env.ASSINATURA_API_SECRET });
    
    const sig = await client.createSimpleSignature({
      documentName: "Contrato",
      pdfBase64,
      signers: [{ full_name: "Maria Exemplo", email: "maria@example.com" }],
      sendEmailInvitation: true,
    });
    const status = await client.getSignature(sig.signature_id);
    const pdf = await client.downloadPdf(sig.signature_id); // Buffer
  4. 4.Configure as credenciais

    Guarde a chave e o segredo de webhook em variáveis de ambiente do servidor.

    .env
    ASSINATURA_API_KEY=sk_sua_chave
    ASSINATURA_API_SECRET=seu_api_secret # também valida o webhook
    ASSINATURA_BASE_URL=https://assinatura.online/api/public/v1
  5. 5.Crie uma assinatura simples

    O signatário recebe o convite por e-mail e confirma com um código. A resposta traz o sign_url.

    criar-assinatura.ts
    const BASE = process.env.ASSINATURA_BASE_URL!;
    const headers = {
      "X-API-Key": process.env.ASSINATURA_API_KEY!,
      "X-API-Secret": process.env.ASSINATURA_API_SECRET!,
      "Content-Type": "application/json",
    };
    
    export async function criarAssinatura(pdfBase64: string) {
      const res = await fetch(`${BASE}/signatures/create`, {
        method: "POST",
        headers,
        body: JSON.stringify({
          mode: "simple",
          document_name: "Contrato de prestação de serviços",
          pdf_base64: pdfBase64,
          signer: { full_name: "Maria Exemplo", email: "maria@example.com" },
          send_email_invitation: true,
          external_id: "pedido-123",
        }),
      });
      const data = await res.json();
      if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
      return data as { signature_id: string; status: string; sign_url: string };
    }
  6. 6.Assinatura qualificada com Bird ID ou VIDaaS

    Troque o modo pelo provedor e informe o CPF do titular do certificado.

    qualificada.ts
    await fetch(`${BASE}/signatures/create`, {
      method: "POST",
      headers,
      body: JSON.stringify({
        provider: "birdid", // ou "vidaas" / "a1"
        document_name: "Laudo técnico",
        pdf_base64: pdfBase64,
        signer: { cpf: "52998224725", full_name: "Ana Exemplo" },
      }),
    });
  7. 7.A1 direto com retentativa segura

    O certificado é conferido (senha, CPF, validade) antes de qualquer gravação e usado só em memória; a resposta já vem com status signed e o PDF. Repita com a mesma Idempotency-Key após um timeout: a resposta é a mesma, sem nova solicitação. Para não reenviar o arquivo a cada chamada, veja a sessão A1 opcional (até 7 dias) em assinatura.online/documentacao#a1-direto.

    a1-direto.ts
    const res = await fetch(`${BASE}/signatures/create`, {
      method: "POST",
      headers: { ...headers, "Idempotency-Key": "contrato-2431-v1" },
      body: JSON.stringify({
        provider: "a1",
        document_name: "Contrato de prestação de serviço",
        pdf_base64: pdfBase64,
        signer: { cpf: "52998224725" },
        a1_pfx_base64: pfxBase64,          // ou certificate: { pfx_base64, password }
        a1_password: process.env.A1_PASSWORD,
      }),
    });
    const data = await res.json(); // erro após criar traz data.signature_id
  8. 8.Consulte o status e baixe o PDF

    Use para conferir o resultado no servidor. O PDF fica disponível após a conclusão.

    status.ts
    const r = await fetch(`${BASE}/signatures/get?signature_id=${id}`, { headers });
    const assinatura = await r.json(); // assinatura.status
    
    const pdf = await fetch(`${BASE}/signatures/pdf?signature_id=${id}`, { headers });
    const bytes = Buffer.from(await pdf.arrayBuffer());
  9. 9.Valide o webhook (HMAC-SHA256)

    Leia o corpo bruto, confira timestamp (tolerância de 300 s, passado ou futuro), formato v1 com 64 hexadecimais e HMAC; responda 401 se algo falhar. Depois, para signature.completed, confirme o status no servidor (503 se não confirmar) e grave pedido + deduplicação numa única transação; só responda 2xx após o commit. Mesma regra do verifyWebhook do SDK.

    webhook.ts
    import crypto from "node:crypto";
    
    const TOLERANCIA_S = 300; // mesma tolerância do verifyWebhook do SDK
    
    /** Valida o webhook antes de qualquer efeito. O segredo fica só no servidor. */
    export function webhookValido(timestamp: string, header: string, rawBody: string): boolean {
      if (!/^\d{1,12}$/.test(timestamp)) return false;                    // Unix em segundos
      const agora = Math.floor(Date.now() / 1000);
      if (Math.abs(agora - Number(timestamp)) > TOLERANCIA_S) return false; // passado ou futuro
      const recebido = /(?:^|,)\s*v1=([0-9a-f]{64})(?:,|$)/i.exec(header)?.[1]?.toLowerCase();
      if (!recebido) return false;                                         // 64 hex antes de comparar
      const esperado = crypto
        .createHmac("sha256", process.env.ASSINATURA_API_SECRET!)
        .update(`${timestamp}.${rawBody}`)                              // corpo BRUTO, sem reserializar
        .digest("hex");
      return crypto.timingSafeEqual(Buffer.from(recebido, "hex"), Buffer.from(esperado, "hex"));
    }
    
    export async function handleWebhook(request: Request) {
      const rawBody = await request.text();
      const timestamp = request.headers.get("X-Assinatura-Timestamp") ?? "";
      const header = request.headers.get("X-Assinatura-Signature") ?? "";
      if (!webhookValido(timestamp, header, rawBody)) return new Response("assinatura inválida", { status: 401 });
    
      const evento = JSON.parse(rawBody);
      // Este exemplo trata apenas signature.completed; outros eventos são só reconhecidos.
      if (evento.event !== "signature.completed") return new Response("ok");
    
      // 1) Confirme no servidor ANTES de qualquer efeito. Sem confirmação → 503 (a plataforma tenta de novo).
      const status = await consultarStatus(evento.signature_id).catch(() => null); // GET /signatures/get
      if (status !== "signed") return new Response("status não confirmado", { status: 503 });
    
      // 2) Atualize o pedido E registre a deduplicação na MESMA transação (integração sua).
      //    Para completed, signature_id + event identifica o evento. Não serve para todos:
      //    participant_signed ocorre várias vezes — inclua signer e signed_at na chave.
      try {
        await marcarAssinadoEDeduplicar({
          chave: `${evento.signature_id}:${evento.event}`,
          externalId: evento.external_id,
        }); // ex.: BEGIN; INSERT dedup (chave única) ON CONFLICT DO NOTHING; UPDATE pedido; COMMIT
      } catch {
        return new Response("falha temporária", { status: 503 }); // nada foi confirmado; a retentativa reprocessa
      }
      return new Response("ok"); // 2xx só depois do COMMIT (ou se a chave já existia)
    }

Prompt para Cursor, Claude ou Lovable

Cole na sua ferramenta de IA para gerar a integração no seu projeto.

prompt.txt
Integre a API da assinatura.online neste projeto Node.js/TypeScript, apenas no servidor.
Base: https://assinatura.online/api/public/v1, headers X-API-Key: <ASSINATURA_API_KEY> e X-API-Secret: <ASSINATURA_API_SECRET>. Na assinatura qualificada (birdid, vidaas ou a1), use o header Idempotency-Key em retentativas de POST /signatures/create; ele não se aplica ao mode "simple".
1) Função que faz POST /signatures/create com mode "simple", document_name, pdf_base64, signer { full_name, email }, send_email_invitation: true e external_id; retorne signature_id e sign_url.
2) GET /signatures/get?signature_id= para status e GET /signatures/pdf?signature_id= para o PDF.
3) Rota de webhook que valida HMAC-SHA256 de "<X-Assinatura-Timestamp>.<corpo bruto>" com ASSINATURA_API_SECRET contra o valor v1= do header X-Assinatura-Signature, usando comparação em tempo constante.
Nunca exponha a chave no navegador. Consulte https://assinatura.online/documentacao.