convertaja
v0.1.0
Published
SDK oficial da API do Convertajá: PDF, DANFE, etiquetas ZPL, assinatura eletrônica, CNPJ, CEP, boleto e Pix.
Maintainers
Readme
convertaja (Node.js)
SDK oficial da API do Convertajá para Node.js: comprimir e converter PDF, gerar DANFE a partir do XML da NF-e, converter etiquetas ZPL, assinatura eletrônica, Central de Expedição e consultas de CNPJ, CEP, boleto e Pix.
- Sem dependências: usa
fetch,FormDataeBlobnativos do Node 18+. - CommonJS e ESM, com tipos TypeScript (
index.d.ts). - Repete sozinho em falha de rede, 429 (respeitando
Retry-After) e 5xx, sem cobrar duas vezes (Idempotency-Key automática).
Ainda não publicado no npm. Até a publicação, instale pelo caminho local ou pelo repositório git (veja abaixo).
Instalação
# caminho local (monorepo ou cópia da pasta)
npm install ../caminho/para/integrations/sdk-node
# ou direto do git (quando o repositório estiver disponível)
npm install "git+https://github.com/convertaja/sdk-node.git"
# depois da publicação
npm install convertajaRequer Node.js 18 ou mais novo.
Primeiros passos
Crie a chave em Minha conta → API (/conta/api). Use cj_test_… para testar (não consome créditos, até 20 chamadas por dia) e cj_live_… em produção.
const { Convertaja } = require('convertaja');
// ou: import Convertaja from 'convertaja';
const cj = new Convertaja({ apiKey: process.env.CONVERTAJA_API_KEY });
const me = await cj.me();
console.log(me.plan.label, me.credits.remaining);
console.log('Créditos depois da última chamada:', cj.creditsRemaining);Opções do construtor: apiKey (padrão: process.env.CONVERTAJA_API_KEY), baseUrl (padrão https://convertaja.com.br), maxRetries (padrão 2), timeoutMs (padrão 120000), retryDelayMs (padrão 500).
Comprimir um PDF
const { writeFile } = require('node:fs/promises');
// wait: true segura a resposta até 60 s; se ainda não terminou, use jobs.wait
let job = await cj.tools.run('comprimir-pdf', {
files: ['contrato.pdf'], // caminho, Buffer, Blob, { data, name }, { url } ou { base64, name }
options: { level: 'high' }, // low | medium | high
wait: true,
});
if (job.status !== 'done' && job.status !== 'error') job = await cj.jobs.wait(job.id);
if (job.status === 'error') throw new Error(job.error.message);
const file = await cj.jobs.download(job.id); // um arquivo, ou ZIP se houver vários
await writeFile(file.filename || 'comprimido.pdf', file.data);
console.log(job.info); // ex.: tamanho antes/depoisConsultar CNPJ e CEP (grátis)
const empresa = await cj.lookups.cnpj('11.222.333/0001-81'); // aceita o CNPJ alfanumérico
console.log(empresa.razaoSocial, empresa.situacao, empresa.endereco);
const endereco = await cj.lookups.cep('01310-100');
console.log(endereco.logradouro, endereco.cidade, endereco.uf);DANFE a partir do XML da NF-e
const job = await cj.tools.run('xml-nfe-para-pdf', {
files: ['nota-35241012345678000199550010000012341000012345.xml'], // vários XMLs ou um ZIP
options: { layout: 'a4' }, // a4 | simplificado (10×15, etiqueta)
wait: true,
});
const danfe = await cj.jobs.download(job.id);
await writeFile('danfe.pdf', danfe.data);ZPL para PDF
// código ZPL direto nas opções (sem arquivo)...
let job = await cj.tools.run('zpl-para-pdf', {
options: { zpl: '^XA^FO50,50^A0N,40,40^FDOlá^FS^XZ', width_mm: 100, height_mm: 150, dpmm: '8' },
wait: true,
});
// ...ou arquivos .zpl/.txt
job = await cj.tools.run('zpl-para-pdf', { files: ['etiquetas.zpl'], wait: true });
await writeFile('etiquetas.pdf', (await cj.jobs.download(job.id)).data);Veja todas as ferramentas, extensões aceitas e opções (JSON Schema) com await cj.tools.list() ou await cj.tools.get('comprimir-pdf').
Assinatura eletrônica (envelope)
const envelope = await cj.esign.envelopes.create({
file: 'contrato.pdf', // ou { url: 'https://…/contrato.pdf' }
name: 'Contrato de prestação de serviços',
message: 'Olá! Segue o contrato para assinatura.',
signers: [
{ name: 'Ana Souza', email: '[email protected]', role: 'sign' },
{ name: 'Bruno Lima', email: '[email protected]', role: 'witness' },
],
fields: [
{ signer: 0, type: 'signature', page: 0, x: 0.1, y: 0.75, w: 0.3, h: 0.07 },
{ signer: 1, type: 'signature', page: 0, x: 0.55, y: 0.75, w: 0.3, h: 0.07 },
],
webhookUrl: 'https://seusite.com.br/webhooks/convertaja', // esign.envelope.completed | refused | expired | canceled
});
console.log(envelope.id, envelope.status); // K7QD-9WZ2-M4HT-R8PA pending
// depois que todos assinarem:
const assinado = await cj.esign.envelopes.downloadFinal(envelope.id);
await writeFile('contrato-assinado.pdf', assinado.data);Também há list, listAll, get(id, { events: true, links: true }), send (para rascunhos criados com send: false), remind, cancel, delete e downloadAudit.
Outros recursos
await cj.validate({ type: 'cnpj', value: '12.ABC.345/01DE-35' });
await cj.validate({ values: ['123.456.789-09', '11.222.333/0001-81'] }); // lote: 1 crédito a cada 50
await cj.boleto.parse('23793.38128 86000.782713 36950.000632 1 10000000012345');
await cj.pix.brcode({ key: '[email protected]', name: 'Empresa', city: 'SAO PAULO', amount: 10.5, format: 'png' });
await cj.qrcode({ text: 'https://convertaja.com.br', format: 'svg' });
await cj.barcode({ type: 'ean13', value: '789123456789', format: 'png' });
// Tarefas
const page = await cj.jobs.list({ status: 'done', limit: 20 }); // page.next_cursor → startingAfter
for await (const job of cj.jobs.listAll({ tool: 'comprimir-pdf' })) console.log(job.id);
await cj.jobs.downloadFile(jobId, 0);
await cj.jobs.delete(jobId); // apaga os arquivos antes de 1 hora (LGPD)
// Central de Expedição: etiquetas + XMLs das notas
const lote = await cj.expedicao.lotes.create({ labels: ['etiquetas.pdf'], xmls: ['notas.zip'], intercalar: true, wait: true });
const pronto = await cj.expedicao.lotes.wait(lote.id);
await writeFile('expedicao.zip', (await cj.expedicao.lotes.download(pronto.id)).data);
// Webhook de teste (envia um evento assinado para a sua URL)
await cj.webhooks.test('https://seusite.com.br/webhooks/convertaja');Webhooks: verificação da assinatura
Cada aviso chega com X-Convertaja-Signature: t=<unix>,v1=<hex>, onde v1 = HMAC-SHA256(segredo, "<t>.<corpo bruto>"). O segredo (whsec_…) fica em Minha conta → API. Verifique sempre com o corpo bruto (antes de qualquer JSON.parse) e recuse assinaturas com mais de 300 s.
const express = require('express');
const { Convertaja, ConvertajaSignatureError } = require('convertaja');
const app = express();
app.post('/webhooks/convertaja', express.raw({ type: 'application/json' }), (req, res) => {
let event;
try {
event = Convertaja.webhooks.constructEvent(req.body, req.get('X-Convertaja-Signature'), process.env.CONVERTAJA_WEBHOOK_SECRET);
} catch (err) {
if (err instanceof ConvertajaSignatureError) return res.status(400).send('assinatura inválida');
throw err;
}
switch (event.type) {
case 'job.completed': /* event.data é a tarefa (id, outputs, download_url…) */ break;
case 'job.failed': break;
case 'esign.envelope.completed': break;
case 'webhook.test': break;
}
res.sendStatus(200); // responda 2xx rápido; o Convertajá tenta até 3 vezes
});Convertaja.webhooks.verify(rawBody, header, secret, toleranceSec = 300) devolve true/false se você preferir tratar sozinho. Use o cabeçalho X-Convertaja-Delivery para descartar entregas repetidas.
Erros
Toda falha vira ConvertajaError:
const { ConvertajaError } = require('convertaja');
try {
await cj.tools.run('comprimir-pdf', { files: ['grande.pdf'] });
} catch (err) {
if (err instanceof ConvertajaError) {
console.log(err.status); // 402
console.log(err.code); // 'insufficient_credits' (estável, para tratar no código)
console.log(err.message); // mensagem em português
console.log(err.requestId); // informe ao suporte
console.log(err.details); // corpo de erro completo (ex.: credits_required)
}
}Códigos do SDK, além dos da API: network_error, timeout, wait_timeout (em jobs.wait), missing_api_key, invalid_signature. A lista completa dos códigos da API está em /api/docs.
Novas tentativas e idempotência
- Repete até
maxRetriesvezes (padrão 2) em falha de rede, tempo esgotado, 408, 429 (excetotest_limitereminder_too_soon), 5xx e 409idempotency_in_progress. - Em 429 espera o
Retry-Afterda API (até 60 s); nos outros casos usa recuo exponencial (0,5 s, 1 s, 2 s… até 8 s). - Todo POST leva uma
Idempotency-Key(UUID) gerada uma vez por chamada e repetida em todas as tentativas. Se a primeira tentativa chegou a ser processada, a repetição devolve a mesma resposta (Idempotent-Replayed: true), sem novo débito. - Para repetir com segurança entre processos (ex.: fila de jobs), passe a sua chave:
cj.tools.run('comprimir-pdf', { files, idempotencyKey: 'pedido-' + pedido.id }). A mesma chave com outro conteúdo em 24 h dá 409idempotency_key_reused. - Depois de cada chamada:
cj.creditsRemaining(X-Credits-Remaining),cj.lastRateLimit(X-RateLimit-*),cj.lastRequestIdecj.lastResponse({ status, attempts, idempotencyKey, replayed }).
Testes
npm test # node:test contra um servidor HTTP local; nenhuma chamada sai da máquinaLicença
MIT
