verifiabl
v0.15.0
Published
Official Verifiabl Node.js SDK for issuing payslip QR codes, formatting PII, AES-256-GCM encryption helpers, and typed issuer API calls.
Maintainers
Readme
verifiabl
Official Node.js SDK for issuing Verifiabl payslip QR codes.
Add a scannable QR code to each payslip you issue. You register the non-PII payslip data with Verifiabl and encrypt the employee's personal details on your own infrastructure, so they live only inside the QR code on the document and never reach Verifiabl.
Verifiabl is for accredited payroll providers. You receive sandbox credentials at onboarding. Full documentation is at docs.verifiabl.io.
Installation
npm install verifiablRequires Node.js 20+. The example below renders an SVG badge, which needs no extra dependencies. To render a PNG instead (slower, and it pulls in a native renderer), also install:
npm install @resvg/resvg-jsGetting started
This is the self-managed flow: register the payslip, encrypt the personal details locally, and generate the QR code yourself. You need four values from onboarding: your OAuth client ID and secret, your encryption key, and your key version.
import { VerifiablClient, formatPii, encryptPii, createBarcodeSvg } from "verifiabl";
const client = new VerifiablClient({
environment: "sandbox",
auth: {
clientId: process.env.VERIFIABL_CLIENT_ID!,
clientSecret: process.env.VERIFIABL_CLIENT_SECRET!,
},
});
// Your 32-byte key and key version, from onboarding. Load the key from a secrets manager.
const key = Buffer.from(process.env.VERIFIABL_ENCRYPTION_KEY_BASE64!, "base64");
const keyVersion = process.env.VERIFIABL_KEY_VERSION!; // e.g. "0f8fad5b-...e.1"
// 1. Format and encrypt the employee's details locally.
const pii = formatPii({
employeeName: "Jane A. Doe",
position: "Senior Developer",
department: "Engineering",
employerAbn: "12345678901",
bsb: "062-000",
accountNumber: "12345678",
accountName: "Jane A Doe",
});
const { encryptedPii, encryptionMetadata } = encryptPii(pii, key, keyVersion);
// 2. Register the non-PII data. Verifiabl returns a Verifiabl reference.
const { verifiablReference } = await client.registerNonPii({
schema: "au.payslip.v1",
issuedAt: new Date().toISOString(),
// Canonical au.payslip.v1: money is integer cents, and net must reconcile as
// net = gross - salarySacrifice - paygw - deductions + reimbursements.
payslipNonPii: {
periodStart: "2026-05-01",
periodEnd: "2026-05-31",
paymentDate: "2026-06-04",
currency: "AUD",
grossCents: 900_000,
paygwCents: 225_000,
netCents: 675_000,
ytdGrossCents: 5_400_000,
ytdPaygwCents: 1_350_000,
},
encryptionMetadata,
});
// 3. Render the QR code and embed the SVG in your payslip PDF.
const { svg } = createBarcodeSvg(
{ verifiablReference, encryptedPii },
{ environment: "sandbox" },
);Prefer createBarcodeSvg when you can: SVG scales to any size without losing quality. Use createBarcodePng when you need a raster PNG (it needs the @resvg/resvg-js renderer). Verifiabl can also build the QR code for you instead of generating it locally. See the docs for both.
Rendering many codes
Generate codes in a loop. Each call is independent, so a single payslip and a large pay run are both fast:
for (const { verifiablReference, encryptedPii } of records) {
const { png } = await createBarcodePng({ verifiablReference, encryptedPii }, {}, 720);
// embed png in this record's PDF
}PNGs default to truecolour. Pass { palette: true } for smaller files when you embed many codes in a PDF.
Batch registration
For pay runs, register up to 1000 records in one request with registerNonPiiBatch. The provider generates each Verifiabl reference up-front with generateVerifiablReference and includes it on each record, so the whole batch can go in one round trip. Results come back in the same order as the input records (results[i] is the outcome of records[i]); one bad record never fails the whole batch.
import { encryptPii, formatPii, generateVerifiablReference } from "verifiabl";
const issuedAt = new Date().toISOString();
const prepared = payslips.map((payslip) => {
const verifiablReference = generateVerifiablReference();
const { encryptedPii, encryptionMetadata } = encryptPii(
formatPii(payslip.pii),
key,
keyVersion,
);
// Keep `encryptedPii` alongside the reference locally: you need both to render the barcode.
return { verifiablReference, encryptedPii, encryptionMetadata, payslip };
});
const { results } = await client.registerNonPiiBatch({
records: prepared.map(({ verifiablReference, encryptionMetadata, payslip }) => ({
verifiablReference,
schema: "au.payslip.v1",
issuedAt,
payslipNonPii: payslip.nonPii,
encryptionMetadata,
})),
});
for (const result of results) {
if (result.status === "error") {
console.error(result.verifiablReference, result.code, result.detail);
}
}Environments
Set environment to production (default) or sandbox. Pass the same value to the client and the barcode renderer, so the scan URL printed on the document matches where the record was registered.
Errors
Failed requests throw VerifiablApiError with a stable code and a requestId to quote to support. Auth failures throw VerifiablAuthError.
import { VerifiablApiError } from "verifiabl";
try {
await client.registerNonPii(request);
} catch (err) {
if (err instanceof VerifiablApiError && err.code === "VALIDATION_FAILED") {
console.log(err.requestId);
}
}Security
Employee PII is encrypted on your infrastructure and never reaches Verifiabl. Keep your encryption key and OAuth secret in a secrets manager. See the security model for the full detail.
Documentation
Full API reference, the alternative API flow, barcode placement rules, and the security model are at docs.verifiabl.io.
