@voltade/wess-sdk
v1.4.2
Published
A comprehensive TypeScript SDK for the WESS Open API with built-in error handling and resource-based architecture
Readme
@voltade/wess-sdk
A comprehensive, type-safe TypeScript SDK for the WESS Open API with built-in error handling and resource-based architecture.
Features
- Type-safe: Full TypeScript support with exported types and Zod schemas
- Resource-based architecture: Organized by API domain (user, branches, customers)
- Error handling: Specific error classes for different failure scenarios
- Zero dependencies: Uses native
fetchAPI (Node.js 18+) - Configurable: Environment variables or programmatic configuration
Installation
Note: This package is published to the public npm registry (registry.npmjs.org) as a public scoped package.
npm install @voltade/wess-sdk
# or
yarn add @voltade/wess-sdk
# or
pnpm add @voltade/wess-sdkQuick Start
import { WessClient } from "@voltade/wess-sdk";
const client = new WessClient({
baseUrl: "https://your-wess-api.com/api",
bearerToken: "your-bearer-token",
});
// Get current user
const user = await client.user.get();
// List all branches
const branches = await client.branches.list();
// Create an appointment
const appointment = await client.branches.createOnlineAppointment(branchId, {
date: "2024-12-15 10:00:00",
items: [
{ product_id: 1718, unit: 1 },
{ product_id: 5523, unit: 1 },
],
customer_id: 123,
});Configuration
Environment Variables
WESS_BASE_URL=https://your-wess-api.com/api
WESS_BEARER_TOKEN=your-bearer-tokenClient Options
interface WessClientConfig {
baseUrl: string; // Base URL for the WESS API
bearerToken: string; // Bearer token for authentication
timeout?: number; // Request timeout in milliseconds (default: 30000)
headers?: Record<string, string>; // Custom headers for all requests
}Method Reference
Quick lookup table for all SDK methods.
UserResource
| Method | HTTP | Endpoint | Returns |
| ------- | ---- | ---------- | -------------- |
| get() | GET | /v1/user | UserResponse |
BranchesResource
| Method | HTTP | Endpoint | Returns |
| --------------------------------------------------------------- | ----- | ----------------------------------------------------------------------------- | ------------------------------- |
| list() | GET | /v1/branches | Branch[] |
| get(branchId) | GET | /v1/branches/:branch | Branch |
| getCustomer(branchId, customerId) | GET | /v1/branches/:branch/customers/:customer | Customer |
| getOnlineAppointmentServices(branchId) | GET | /v1/online/branches/:branch/appointments/services | OnlineService[] |
| getOnlineAppointmentEmployees(branchId, params) | GET | /v1/online/branches/:branch/appointments/employees | OnlineEmployee[] |
| getOnlineAppointmentTimeSlots(branchId, params) | GET | /v1/online/branches/:branch/appointments/time-slots | OnlineTimeSlot[] |
| createOnlineAppointment(branchId, params, options?) | POST | /v1/online/branches/:branch/appointments | OnlineSaleTicket |
| cancelOnlineAppointment(branchId, saleTicketId, params) | PATCH | /v1/online/branches/:branch/appointments/:saleTicketId/cancel | OnlineSaleTicket |
| createCustomer(branchId, params) | POST | /v1/online/branches/:branch/customers | OnlineCustomer |
| getCustomerUpcomingAppointments(branchId, customerId, params) | GET | /v1/online/branches/:branch/customers/:customer/appointments/upcoming-appointments | OnlineSaleTicket[] |
CustomersResource
| Method | HTTP | Endpoint | Returns |
| ---------------------------------- | ---- | ----------------------------------------- | ------------ |
| searchByPhoneNumber(phoneNumber) | GET | /v1/online/customers/lookup/phone-number/:phoneNumber | OnlineCustomer[] |
OnlineResource
| Method | HTTP | Endpoint | Returns |
| ----------------------------------------------------------------- | ----- | ------------------------------------------------------------------------------------ | ----------------------------- |
| listBranches(params?) | GET | /v1/online/branches | { data: OnlineBranch[] } |
| getBranch(branchId) | GET | /v1/online/branches/:branch | OnlineBranch |
| getServices(branchId) | GET | /v1/online/branches/:branch/appointments/services | OnlineService[] |
| getEmployees(branchId, params) | GET | /v1/online/branches/:branch/appointments/employees | OnlineEmployee[] |
| getTimeSlots(branchId, params) | GET | /v1/online/branches/:branch/appointments/time-slots | OnlineTimeSlot[] |
| listAppointments(branchId, params?) | GET | /v1/online/branches/:branch/appointments | { data: ... } |
| createAppointment(branchId, params, options?) | POST | /v1/online/branches/:branch/appointments | OnlineSaleTicket |
| cancelAppointment(branchId, saleTicketId, params) | PATCH | /v1/online/branches/:branch/appointments/:saleTicketId/cancel | OnlineSaleTicket |
| lookupCustomerByPhone(phone) | GET | /v1/online/customers/lookup/phone-number/:phone | OnlineCustomer[] |
| listCustomers(branchId, params?) | GET | /v1/online/branches/:branch/customers | { data: OnlineCustomer[] } |
| createCustomer(branchId, params) | POST | /v1/online/branches/:branch/customers | OnlineCustomer |
| getCustomer(branchId, customerId) | GET | /v1/online/branches/:branch/customers/:customer | OnlineCustomer |
| getCustomerUpcomingAppointments(branchId, customerId, params) | GET | /v1/online/branches/:branch/customers/:customer/appointments/upcoming-appointments | OnlineSaleTicket[] |
API Reference
User Resource
client.user.get()
Get the current authenticated user.
const response = await client.user.get();
// Returns: { code: number, data: User }Returns: UserResponse
Branches Resource
client.branches.list()
List all branches.
const branches = await client.branches.list();Returns: Branch[]
client.branches.get(branchId)
Get a single branch by ID.
const branch = await client.branches.get(4630);Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| branchId | number | Yes | The branch ID |
Returns: Branch
client.branches.getCustomer(branchId, customerId)
Get a customer from a specific branch.
const customer = await client.branches.getCustomer(4630, 123);
// or by customer code
const customer = await client.branches.getCustomer(4630, "CUST001");Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| branchId | number | Yes | The branch ID |
| customerId | number \| string | Yes | The customer ID or customer code |
Returns: Customer
client.branches.getOnlineAppointmentServices(branchId)
Get services available for online appointments.
const services = await client.branches.getOnlineAppointmentServices(4630);Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| branchId | number | Yes | The branch ID |
Returns: OnlineService[]
client.branches.getOnlineAppointmentEmployees(branchId, params)
Get employees available for online appointments, filtered by product IDs.
const employees = await client.branches.getOnlineAppointmentEmployees(4630, {
product_ids: [1718, 5523],
});Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| branchId | number | Yes | The branch ID |
| params.product_ids | number[] | Yes | Array of product IDs to filter employees |
Returns: OnlineEmployee[]
client.branches.getOnlineAppointmentTimeSlots(branchId, params)
Get available time slots for online appointments.
const timeSlots = await client.branches.getOnlineAppointmentTimeSlots(4630, {
product_ids: [1718],
date: "2024-12-15",
});Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| branchId | number | Yes | The branch ID |
| params.product_ids | number[] | Yes | Array of product IDs |
| params.date | string | Yes | Date in YYYY-MM-DD format |
Returns: OnlineTimeSlot[]
client.branches.createOnlineAppointment(branchId, params, options?)
Create an online appointment.
const appointment = await client.branches.createOnlineAppointment(
4630,
{
date: "2024-12-15 10:00:00",
items: [
{ product_id: 1718, unit: 1 },
{ product_id: 5523, unit: 2 },
],
customer_id: 123,
employee_id: 456, // optional
remark: "Notes here", // optional
},
{
idempotencyKey: "unique-request-key", // optional, prevents duplicate submissions
}
);Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| branchId | number | Yes | The branch ID |
| params.date | string | Yes | Appointment date (ISO8601 or Y-m-d H:i:s format) |
| params.items | { product_id: number, unit: number }[] | Yes | Array of items with product ID and quantity |
| params.customer_id | number | Yes | The customer ID |
| params.employee_id | number \| null | No | The employee ID (optional) |
| params.remark | string | No | Appointment notes |
| options.idempotencyKey | string | No | Idempotency key to prevent duplicates |
Returns: OnlineSaleTicket
client.branches.cancelOnlineAppointment(branchId, saleTicketId, params)
Cancel an online appointment.
const cancelled = await client.branches.cancelOnlineAppointment(4630, 789, {
customer_id: 123,
});Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| branchId | number | Yes | The branch ID |
| saleTicketId | number | Yes | The sale ticket ID to cancel |
| params.customer_id | number | Yes | The customer ID |
Returns: OnlineSaleTicket
client.branches.getCustomerUpcomingAppointments(branchId, customerId, params)
Get upcoming appointments for a customer within a date range.
const appointments = await client.branches.getCustomerUpcomingAppointments(
4630,
123,
{
from: "2024-12-01 00:00:00",
to: "2024-12-31 23:59:59",
}
);Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| branchId | number | Yes | The branch ID |
| customerId | number | Yes | The customer ID |
| params.from | string | Yes | Start date (ISO8601 or Y-m-d H:i:s format) |
| params.to | string | Yes | End date (ISO8601 or Y-m-d H:i:s format) |
Returns: OnlineSaleTicket[]
Note: Only returns appointments with statuses: OPEN, CONFIRMED, REQUEST, BOOKING_CONFIRMED.
Customers Resource
client.customers.searchByPhoneNumber(phoneNumber)
Search customers by phone number.
const customers = await client.customers.searchByPhoneNumber("+60123456789");Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| phoneNumber | string | Yes | The phone number to search for |
Returns: OnlineCustomer[]
Online Resource
Endpoints for the /v1/online/... namespace (public online booking surface). Online branches are implicitly online-enabled — the online branch shape has no published field (unlike the management Branch type).
client.online.listBranches(params?)
List online branches.
const { data } = await client.online.listBranches();Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| params.page | number | No | Page number |
| params.per_page | number | No | Results per page |
Returns: { data: OnlineBranch[] }
client.online.getBranch(branchId)
Get a single online branch by ID.
const branch = await client.online.getBranch(4630);Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| branchId | number | Yes | The branch ID |
Returns: OnlineBranch
client.online.getServices(branchId)
Get services available for online appointments.
const services = await client.online.getServices(4630);Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| branchId | number | Yes | The branch ID |
Returns: OnlineService[]
client.online.getEmployees(branchId, params)
Get employees available for online appointments, filtered by product IDs.
const employees = await client.online.getEmployees(4630, {
product_ids: [1718, 5523],
});Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| branchId | number | Yes | The branch ID |
| params.product_ids | number[] | Yes | Array of product IDs to filter employees |
Returns: OnlineEmployee[]
client.online.getTimeSlots(branchId, params)
Get available time slots (per employee) for online appointments.
const timeSlots = await client.online.getTimeSlots(4630, {
product_ids: [1718],
date: "2024-12-15",
});Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| branchId | number | Yes | The branch ID |
| params.product_ids | number[] | Yes | Array of product IDs |
| params.date | string | Yes | Date in YYYY-MM-DD format |
Returns: OnlineTimeSlot[]
Note: Slot times are returned as UTC ISO8601 strings — convert to branch-local time before passing to createAppointment.
client.online.listAppointments(branchId, params?)
List online appointments for a branch.
const { data } = await client.online.listAppointments(4630);Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| branchId | number | Yes | The branch ID |
| params.page | number | No | Page number |
| params.per_page | number | No | Results per page |
Returns: { data: ... } (lenient shape)
client.online.createAppointment(branchId, params, options?)
Create an online appointment.
const appointment = await client.online.createAppointment(
4630,
{
date: "2024-12-15 10:00:00",
items: [
{ product_id: 1718, unit: 1 },
{ product_id: 5523, unit: 2 },
],
customer_id: 123,
employee_id: 456, // optional
remark: "Notes here", // optional
},
{
idempotencyKey: "unique-request-key", // optional, prevents duplicate submissions
}
);Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| branchId | number | Yes | The branch ID |
| params.date | string | Yes | Branch-local appointment date in YYYY-MM-DD HH:mm:ss format |
| params.items | { product_id: number, unit: number }[] | Yes | Array of items with product ID and quantity |
| params.customer_id | number | Yes | The customer ID |
| params.employee_id | number \| null | No | The employee ID (optional) |
| params.remark | string | No | Appointment notes |
| options.idempotencyKey | string | No | Idempotency key to prevent duplicates |
Returns: OnlineSaleTicket
Note: date must be branch-local YYYY-MM-DD HH:mm:ss. Since getTimeSlots returns slot times as UTC ISO8601, convert them to branch-local time before booking.
client.online.cancelAppointment(branchId, saleTicketId, params)
Cancel an online appointment.
const cancelled = await client.online.cancelAppointment(4630, 789, {
customer_id: 123,
});Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| branchId | number | Yes | The branch ID |
| saleTicketId | number | Yes | The sale ticket ID to cancel |
| params.customer_id | number | Yes | The customer ID |
Returns: OnlineSaleTicket
client.online.lookupCustomerByPhone(phone)
Look up customers by phone number.
const customers = await client.online.lookupCustomerByPhone("+6590029446");Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| phone | string | Yes | Full E.164 phone number including + and country code |
Returns: OnlineCustomer[] (without relation_branch)
Note: Requires the full E.164 number including the + and country code (e.g. +6590029446). A national-only number returns an empty array.
client.online.listCustomers(branchId, params?)
List customers for an online branch.
const { data } = await client.online.listCustomers(4630);Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| branchId | number | Yes | The branch ID |
| params.page | number | No | Page number |
| params.per_page | number | No | Results per page |
Returns: { data: OnlineCustomer[] }
client.online.createCustomer(branchId, params)
Create a customer for an online branch.
const customer = await client.online.createCustomer(4630, {
first_name: "Jane",
phone_mobile_country_code: "65",
phone_mobile: "90029446",
});Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| branchId | number | Yes | The branch ID |
| params.first_name | string | Yes | Customer first name |
| params.phone_mobile_country_code | string | Yes | Mobile country code |
| params.phone_mobile | string | Yes | Mobile number |
| params.ic_no | string | No | IC / identification number |
| params.last_name | string \| null | No | Customer last name |
| params.salutation | string | No | Salutation |
| params.detail | object | No | Additional customer details |
Returns: OnlineCustomer
client.online.getCustomer(branchId, customerId)
Get a single customer from an online branch.
const customer = await client.online.getCustomer(4630, 123);Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| branchId | number | Yes | The branch ID |
| customerId | number | Yes | The customer ID |
Returns: OnlineCustomer (with relation_branch)
client.online.getCustomerUpcomingAppointments(branchId, customerId, params)
Get upcoming appointments for a customer within a date range.
const appointments = await client.online.getCustomerUpcomingAppointments(
4630,
123,
{
from: "2024-12-01 00:00:00",
to: "2024-12-31 23:59:59",
}
);Parameters:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| branchId | number | Yes | The branch ID |
| customerId | number | Yes | The customer ID |
| params.from | string | Yes | Start of range (see note) |
| params.to | string | Yes | End of range (see note) |
Returns: OnlineSaleTicket[]
Note: from/to must be branch-local YYYY-MM-DD HH:mm:ss or ISO8601-with-timezone, and from must be ≥ now.
Error Handling
The SDK provides specific error classes for different failure scenarios:
import {
WessError,
WessAuthenticationError,
WessNotFoundError,
WessValidationError,
WessRateLimitError,
WessNetworkError,
WessTimeoutError,
} from "@voltade/wess-sdk";
try {
const branch = await client.branches.get(99999);
} catch (error) {
if (error instanceof WessAuthenticationError) {
// 401 - Invalid or expired token
} else if (error instanceof WessNotFoundError) {
// 404 - Resource not found
} else if (error instanceof WessValidationError) {
// 422 - Validation failed
console.error("Errors:", error.errors);
} else if (error instanceof WessRateLimitError) {
// 429 - Too many requests
} else if (error instanceof WessNetworkError) {
// Network connectivity issues
} else if (error instanceof WessTimeoutError) {
// Request timeout
} else if (error instanceof WessError) {
// Generic API error
console.error(`API error (${error.statusCode}):`, error.message);
}
}Error Classes
| Error Class | Status Code | Description |
| ------------------------- | ----------- | ------------------------------ |
| WessError | Any | Base error class |
| WessAuthenticationError | 401 | Invalid or missing credentials |
| WessNotFoundError | 404 | Resource not found |
| WessValidationError | 422 | Request validation failed |
| WessRateLimitError | 429 | Too many requests |
| WessNetworkError | - | Network connectivity issues |
| WessTimeoutError | - | Request timeout |
TypeScript Types
All types are exported with Zod schemas for runtime validation.
Import Types
import type {
// Config
WessClientConfig,
RequestOptions,
// User
User,
UserResponse,
// Branch
Branch,
// Customer
Customer,
// Online Appointments - Services
OnlineAppointmentService,
ServiceDetails,
// Online Appointments - Employees
OnlineAppointmentEmployee,
EmployeeProduct,
// Online Appointments - Time Slots
OnlineAppointmentTimeSlot,
// Online Appointments - Sale Ticket (Create/Cancel/Upcoming)
OnlineAppointmentSaleTicket,
SaleTicketCustomer,
SaleTicketItem,
// Online Resource (/v1/online/...)
OnlineBranch,
OnlineService,
OnlineEmployee,
OnlineTimeSlot,
OnlineCustomer,
OnlineSaleTicket,
// Request Parameters
GetEmployeesParams,
GetTimeSlotsParams,
CreateAppointmentParams,
CreateAppointmentOptions,
CancelAppointmentParams,
GetUpcomingAppointmentsParams,
// Error Responses
ApiErrorResponse,
ValidationErrorResponse,
} from "@voltade/wess-sdk";Import Zod Schemas
import {
// User
UserSchema,
UserResponseSchema,
// Branch
BranchSchema,
// Customer
CustomerSchema,
// Online Appointments
OnlineAppointmentServiceSchema,
ServiceDetailsSchema,
OnlineAppointmentEmployeeSchema,
EmployeeProductSchema,
OnlineAppointmentTimeSlotSchema,
OnlineAppointmentSaleTicketSchema,
SaleTicketCustomerSchema,
SaleTicketItemSchema,
// Request Parameters
GetEmployeesParamsSchema,
GetTimeSlotsParamsSchema,
CreateAppointmentParamsSchema,
CancelAppointmentParamsSchema,
GetUpcomingAppointmentsParamsSchema,
// Error Responses
ApiErrorResponseSchema,
ValidationErrorResponseSchema,
} from "@voltade/wess-sdk";Advanced Usage
Direct Resource Access
import { WessClient, BranchesResource } from "@voltade/wess-sdk";
const client = new WessClient({ baseUrl: "...", bearerToken: "..." });
const branches: BranchesResource = client.branches;
const allBranches = await branches.list();Custom HTTP Client
const httpClient = client.getHttpClient();
// Make custom requests
const response = await httpClient.get("/custom-endpoint", {
params: { key: "value" },
});
// Available methods: get, post, put, patch, deleteRequirements
- Node.js >= 18.0.0
- TypeScript >= 5.0.0
Architecture
@voltade/wess-sdk/
├── index.ts # Main entry point & WessClient class
├── client.ts # HTTP client wrapper
├── types.ts # Re-exports from types/
├── errors.ts # Error classes
├── resources/
│ ├── index.ts # Resource exports
│ ├── user.ts # User resource (1 method)
│ ├── branches.ts # Branches resource (9 methods)
│ └── customers.ts # Customers resource (1 method)
└── types/
├── index.ts # Type exports
├── config.ts # Config types
├── user.ts # User types
├── branch.ts # Branch types
├── customer.ts # Customer types
├── appointment.ts # Appointment types
└── error.ts # Error response typesPublishing (Maintainers Only)
This package is published to the public npm registry (registry.npmjs.org) as a public scoped package (@voltade/wess-sdk).
1. Authenticate
npm login(must be a user with publish access to the @voltade org/scope on npmjs.org)
2. Version + publish
The version in package.json is already set for the release. Then:
npm publish --access publicprepublishOnlyrunsnpm run clean && npm run buildautomatically, sodist/is rebuilt from source before publishing.- For future releases, bump with
npm version patch|minor|majorfirst.
3. Verify
npm view @voltade/wess-sdk version # should show the new versionLicense
MIT (c) Voltade
Support
For issues and questions, please file an issue on GitHub.
