JavaScript / TypeScript SDK
The official JS/TS SDK ships both ESM and CommonJS with full
TypeScript declarations. Universal: works in Node 18+, Deno, Bun,
Cloudflare Workers, Vercel Edge, and browsers — built on globalThis.fetch,
no HTTP-client dependency.
Install
Section titled “Install”npm install @zutax/sdkpnpm add @zutax/sdkyarn add @zutax/sdkbun add @zutax/sdkGet an API key
Section titled “Get an API key”- Sign in to your ZUTAX workspace.
- Settings → API keys → Create key.
- Copy the key — you’ll only see it once.
Your first transmit
Section titled “Your first transmit”import { ZutaxClient, type InvoiceTransmitInput } from '@zutax/sdk';
const client = new ZutaxClient({ apiKey: 'zk_test_...', baseUrl: 'https://staging-api.getzutax.com',});
const payload: InvoiceTransmitInput = { external_id: 'INV-2026-0042', issue_date: '2026-05-03', due_date: '2026-06-03', customer: { external_id: 'C-100', legal_name: 'Acme Industries Limited', tin: '12345678-0001', address: { city_name: 'Lagos', country_code: 'NG' }, }, lines: [ { name: 'Consulting services — May 2026', quantity: '1', unit_price_naira: '250000.00', vat_rate: '7.5', }, ],};
const result = await client.invoices.transmit(payload, { idempotencyKey: 'INV-2026-0042',});console.log(result.invoice_id, result.irn);Errors
Section titled “Errors”import { ValidationError, RateLimitError, ZutaxError,} from '@zutax/sdk';
try { await client.invoices.transmit(payload);} catch (err) { if (err instanceof ValidationError) { for (const e of err.errors) console.log(e.loc, e.msg); } else if (err instanceof RateLimitError) { console.log(`Slow down, retry in ${err.retryAfter}s`); } else if (err instanceof ZutaxError) { console.log(`Failed: ${err.statusCode} ${err.message}`); } else { throw err; }}| Exception | When it fires |
|---|---|
| AuthError | 401 / 403 |
| NotFoundError | 404 |
| ValidationError | 400 / 422 (carries errors from the server) |
| ConflictError | 409 |
| RateLimitError | 429 (carries retryAfter in seconds) |
| ServerError | 5xx |
| ZutaxError | base class |
Idempotency
Section titled “Idempotency”Pass per-call:
await client.invoices.transmit(payload, { idempotencyKey: 'INV-2026-0042' });…or scope to an async block (uses AsyncLocalStorage in Node so it flows
through await chains):
import { withIdempotencyKey } from '@zutax/sdk';
await withIdempotencyKey('INV-2026-0042', async () => { await client.invoices.transmit(payload);});Resources
Section titled “Resources”client.invoices // transmit, get, getByExternalId, list, cancelclient.parties // create, get, getByExternalId, listclient.products // create, get, getByExternalId, listclient.creditNotes // create, get, getByExternalId, listEdge runtimes & browsers
Section titled “Edge runtimes & browsers”For Cloudflare Workers / Vercel Edge: bundle as ESM and configure your
bundler to mark node:async_hooks as external (or stub it). Without ALS,
withIdempotencyKey falls back to a module-level variable — fine for
single-task usage, but pass idempotencyKey: explicitly for safety with
concurrent requests.
Reference connector
Section titled “Reference connector”A complete end-to-end example — pulls pending invoices from a generic REST
API and transmits each one — lives in
zutax-sdks/reference-connectors/typescript-rest-source/.
Fork it as a starting point for your own custom-ERP integration.