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.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.tsconst 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.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.tsconst 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.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.tsimport { 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); // Buffer4.Configure as credenciais
Guarde a chave e o segredo de webhook em variáveis de ambiente do servidor.
.envASSINATURA_API_KEY=sk_sua_chave ASSINATURA_API_SECRET=seu_api_secret # também valida o webhook ASSINATURA_BASE_URL=https://assinatura.online/api/public/v15.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.tsconst 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.Assinatura qualificada com Bird ID ou VIDaaS
Troque o modo pelo provedor e informe o CPF do titular do certificado.
qualificada.tsawait 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.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.tsconst 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_id8.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.tsconst 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.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.tsimport 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.
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.