Back to docs

SDK

JavaScript / TypeScript

The official @qrmax/node SDK — typed, tree-shakeable, works in Node 18+, Bun, Deno, and edge runtimes. Zero dependencies.

100% TypeScriptZero dependenciesESM + CJSEdge runtime compatible

Install

npm install @qrmax/node

Requires Node.js 18+ (native fetch support). For older Node, install node-fetch and polyfill globally.

Initialize the client

Import and instantiate the client with your API key.

TypeScript
import QRMax from '@qrmax/node';

const qrmax = new QRMax({
  apiKey: process.env.QRMAX_API_KEY,
});

Create a dynamic QR code

Returns the short URL and the image URL for immediate use.

TypeScript
const qr = await qrmax.qrCodes.create({
  type: 'url',
  target: 'https://example.com/spring-sale',
  dynamic: true,
  design: {
    foregroundColor: '#4f46e5',
    logoUrl: 'https://example.com/logo.png',
    cornerStyle: 'rounded',
  },
});

console.log(qr.shortUrl);  // https://qrx.io/a3b9c2
console.log(qr.imageUrl);  // https://cdn.qrmax.io/qr/qr_01HNP....png

Re-target a dynamic QR

The printed pattern never changes — only the redirect does.

TypeScript
await qrmax.qrCodes.update(qr.id, {
  target: 'https://example.com/summer-sale',
});

Fetch scan analytics

Pass a date range. Returns counts, device breakdown, and geo.

TypeScript
const analytics = await qrmax.qrCodes.analytics(qr.id, {
  from: '2026-04-01',
  to:   '2026-04-19',
});

console.log(analytics.totalScans);     // 1247
console.log(analytics.uniqueScanners); // 892
console.log(analytics.topCountries);   // [{ code: 'US', scans: 420 }, ...]

List and paginate

Use async iteration for automatic pagination across all pages.

TypeScript
for await (const qr of qrmax.qrCodes.list({ type: 'url' })) {
  console.log(qr.id, qr.shortUrl);
}

Verify a webhook

Validate the HMAC signature before trusting an inbound webhook payload.

TypeScript
import { verifyWebhook } from '@qrmax/node';

app.post('/webhooks/qrmax', (req, res) => {
  const signature = req.header('X-QRMax-Signature');
  const body = req.rawBody;  // raw bytes, not parsed JSON

  const valid = verifyWebhook({
    body,
    signature,
    secret: process.env.QRMAX_WEBHOOK_SECRET,
  });

  if (!valid) return res.status(400).send('invalid signature');

  const event = JSON.parse(body.toString());
  console.log(event.event, event.data);
  res.status(200).send('ok');
});

Error handling

Typed errors let you distinguish between network, auth, and validation failures.

TypeScript
import { QRMaxError, RateLimitError } from '@qrmax/node';

try {
  await qrmax.qrCodes.create({ type: 'url', target: '' });
} catch (err) {
  if (err instanceof RateLimitError) {
    console.log(`Retry after ${err.retryAfterMs}ms`);
  } else if (err instanceof QRMaxError) {
    console.log(err.status, err.code, err.message);
  } else {
    throw err;
  }
}