@zenglobal/api-client
v5.1.12
Published
TypeScript client for the Zen API v1, generated from the OpenAPI specification
Keywords
Readme
@zenglobal/api-client
TypeScript client for the Zen API v1, generated from the OpenAPI specification using @hey-api/openapi-ts.
Installation
npm install @zenglobal/api-client kyky is a peer dependency — you must install it alongside the client.
Quick Start
import {
configureZenClient,
loyaltyGetCard,
pingPing,
} from "@zenglobal/api-client";
// Configure once at startup
configureZenClient({
baseUrl: "https://api.zenglobal.au",
username: process.env.ZEN_API_USER!,
password: process.env.ZEN_API_PASS!,
partner: {
partnerId: "q74vzg9o",
name: "my-pos-app",
version: "1.0.0",
},
});
// Call any endpoint with full type safety
const { data: ping } = await pingPing();
const { data: card } = await loyaltyGetCard({
path: { cardNumber: "12345" },
});Error Handling
The client provides two error classes for different failure modes:
ZenApiError — Business-Logic Errors (HTTP 200)
Zen sometimes returns HTTP 200 with a non-zero result field indicating a business-logic error. The client automatically throws these as ZenApiError:
import { ZenApiError, voucherEnquireVoucher } from "@zenglobal/api-client";
try {
const { data } = await voucherEnquireVoucher({
path: { voucherType: "Gift" },
body: { voucher: { serialnumber: "999" } },
});
} catch (error) {
if (error instanceof ZenApiError) {
console.error(`Zen error ${error.code}: ${error.errorMessage}`);
// error.issue, error.reason — optional detail fields
// error.response — the original Response
}
}ZenHttpError — HTTP Errors (4xx/5xx)
When throwOnError: true is set and the server returns an HTTP error, the client throws a ZenHttpError with structured details:
import { ZenHttpError, loyaltyGetCard } from "@zenglobal/api-client";
try {
const { data } = await loyaltyGetCard({
path: { cardNumber: "12345" },
throwOnError: true,
});
} catch (error) {
if (error instanceof ZenHttpError) {
console.error(error.message);
// "HTTP 400 GET https://api.zenglobal.au/v1/loyalty/card/12345: Bad Request"
// error.status — HTTP status code
// error.statusText — HTTP status text
// error.body — parsed response body (JSON or text)
// error.url — request URL
// error.method — HTTP method
// error.response — the original Response
}
}Both error classes extend Error, so instanceof Error works for catch-all handling.
Configuration Options
| Option | Type | Default | Description |
| ----------- | ------------ | -------------- | --------------------------------------------------------------------- |
| baseUrl | string | — | Zen API host, no path (operation paths already carry /v1) |
| username | string | — | HTTP Basic Auth username (or use auth) |
| password | string | — | HTTP Basic Auth password (or use auth) |
| auth | ZenAuth | — | OAuth config — clientCredentials or organisation (see below) |
| partner | ZenPartner | — | Partner identification (see below) |
| companyId | string | — | Target company (X-COMPANY-ID); required with mode: "organisation" |
| timeout | number | 30000 | Request timeout in ms |
| retry | object | { limit: 3 } | Retry configuration |
OAuth client_credentials
Instead of HTTP Basic, the client can authenticate with the OAuth 2.0
client_credentials grant. The existing Partner API key is
the OAuth client (clientId = key username, clientSecret = key password).
The client mints a zen-partner-api Bearer token from the auth host, caches it
in memory, refreshes it before expiry, and (for idempotent requests) refreshes
and retries once on a 401.
configureZenClient({
baseUrl: "https://api.zenglobal.au",
partner: { partnerId: "q74vzg9o", name: "my-pos-app", version: "1.0.0" },
auth: {
mode: "clientCredentials",
clientId: process.env.ZEN_CLIENT_ID!,
clientSecret: process.env.ZEN_CLIENT_SECRET!,
// scope: "read write", // optional; space-delimited
// tokenUrl: "https://auth-dev.zenglobal.au/oauth/token", // override for development
},
});The token endpoint defaults to production and is a separate host from
baseUrl. Override it via auth.tokenUrl per stage:
| Stage | Token endpoint |
| -------------------------------- | ------------------------------------------- |
| local | https://auth.zendev.au/oauth/token |
| development (auth-dev / staging) | https://auth-dev.zenglobal.au/oauth/token |
| production (default) | https://auth.zenglobal.au/oauth/token |
auth.zendev.au is the local dev-proxy host, not development. Pointing a
staging consumer at production has caused a prod issuer leak before — see
Token endpoints
for the authoritative table.
Token-endpoint failures throw ZenAuthError.
Organisation credentials
An organisation credential is a single OAuth client that can act as any
company in its organisation. The token names the organisation; each request
names the company via X-COMPANY-ID. Bind the company at construction and
build one client per company:
const auth = {
mode: "organisation",
clientId: process.env.ZEN_OAUTH_CLIENT_ID!,
clientSecret: process.env.ZEN_OAUTH_CLIENT_SECRET!,
} as const;
const bottlemart = createZenClient({
baseUrl: "https://api.zenglobal.au",
partner: { partnerId: "q74vzg9o", name: "my-app", version: "1.0.0" },
auth,
companyId: "2",
});companyId is required in this mode — omitting it throws at construction
rather than failing on the first request. Clients sharing a credential share
one token, so the example above mints once no matter how many companies you
build clients for.
The company must belong to the credential's organisation and be within its allow-list, both of which the server re-reads on every request. Narrowing the allow-list or deactivating the client therefore takes effect on the next call, not at token expiry.
companyId is also accepted in the other auth modes, where the server honours
it only when it names the credential's own chain.
Partner Identification
Every client must specify a partner identity via the partner property. This sets the zen-client-user-agent header on all requests, allowing Zen to attribute API traffic to the correct integration partner.
| Field | Type | Description |
| ----------- | -------- | -------------------------------------- |
| partnerId | string | 8-character partner ID assigned by Zen |
| name | string | Name of the integrating application |
| version | string | Version of the integrating application |
Contact Zen Global to obtain your partner ID.
Advanced Usage
Direct Client Access
For advanced scenarios, access the underlying client directly:
import { client } from "@zenglobal/api-client";
// Add custom interceptors
client.interceptors.request.use((request, options) => {
request.headers.set("X-Request-ID", crypto.randomUUID());
return request;
});Regenerating
When the OpenAPI spec changes, run the unified pipeline:
cd packages/zen-api-client
npm run pipelineThis runs all steps in order:
- Breaking change detection —
oasdiffcompares current snapshot against the compiled spec - Changelog generation — auto-generates CHANGELOG.md entries with SemVer suggestion
- SDK regeneration —
@hey-api/openapi-tsregeneratessrc/client/ - Snapshot update — copies the new spec to
openapi.snapshot.yaml - Typecheck —
tsc --noEmit - Tests —
vitest run
If breaking changes are detected, the pipeline blocks. Use --force to allow breaking changes (bumps major version):
npm run pipeline -- --forceIndividual Scripts
npm run generate # Regenerate SDK only
npm run check:breaking # Check for breaking API changes
npm run test # Run tests
npm run typecheck # TypeScript type checkoasdiff is auto-installed to .bin/ on first run if not found on PATH (macOS and Linux).
Available SDK Functions
100 typed functions covering all Zen v1 API endpoints. Functions follow camelCase naming derived from the OpenAPI operationIds:
loyaltyGetCard,loyaltyCreateCard,loyaltySearchCards, ...voucherEnquireVoucher,voucherRedeemVoucher, ...membershipGetMember,membershipCreateMember, ...transactionCreateTransaction,transactionGetTransactioninventoryGetItem,inventorySearchInventory, ...orderCreateOrder,orderGetOrder,orderSearchOrders, ...offerList,offerCreate,offerUpdate,offerDeletesmsSendSms,smsGetSmsBalance, ...pingPing,versionGetVersion,sitesGetSites- And many more — see
src/client/sdk.gen.tsfor the full list.
