npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

convertaja

v0.1.0

Published

SDK oficial da API do Convertajá: PDF, DANFE, etiquetas ZPL, assinatura eletrônica, CNPJ, CEP, boleto e Pix.

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, FormData e Blob nativos 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 convertaja

Requer 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/depois

Consultar 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é maxRetries vezes (padrão 2) em falha de rede, tempo esgotado, 408, 429 (exceto test_limit e reminder_too_soon), 5xx e 409 idempotency_in_progress.
  • Em 429 espera o Retry-After da 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á 409 idempotency_key_reused.
  • Depois de cada chamada: cj.creditsRemaining (X-Credits-Remaining), cj.lastRateLimit (X-RateLimit-*), cj.lastRequestId e cj.lastResponse ({ status, attempts, idempotencyKey, replayed }).

Testes

npm test   # node:test contra um servidor HTTP local; nenhuma chamada sai da máquina

Licença

MIT