The official ElevatedPOS client for Node.js and TypeScript. Strongly typed, zero production dependencies — uses the platform fetch API.
Package name: @elevatedpos/api-client
npm install @elevatedpos/api-client
pnpm add @elevatedpos/api-client
yarn add @elevatedpos/api-client
The SDK supports API Key authentication for server-to-server use cases and OAuth 2.0 for partner integrations that act on behalf of a merchant.
import { createClient } from '@elevatedpos/api-client';
const epos = createClient({
apiKey: process.env.ELEVATEDPOS_API_KEY!, // never expose this in the browser
// baseUrl: 'https://sandbox.elevatedpos.com.au', // uncomment to use sandbox
});After completing the OAuth flow and receiving tokens, pass the access token via the standard Authorization header by constructing a thin wrapper. Refer to the OAuth docs for the full flow.
// OAuth: use a proxy that injects the bearer token server-side // and call your own backend endpoints to avoid exposing the token. // The ElevatedPOS API also accepts: Authorization: Bearer <access_token> // Set this at the gateway level when forwarding merchant requests.
import { createClient } from '@elevatedpos/api-client';
const epos = createClient({ apiKey: process.env.ELEVATEDPOS_API_KEY! });
// List products with pagination
const { data: products, meta } = await epos.catalog.products.list({
isActive: true,
limit: 20,
});
console.log(`Showing ${products.length} of ${meta.totalCount} products`);
// Search customers
const { data: customers } = await epos.customers.list({ search: 'Jane' });
// Create an order
const { data: order } = await epos.orders.create({
locationId: 'loc_01HXXXXXXXXXX',
customerId: customers[0].id,
lines: [
{ productId: products[0].id, quantity: 2, unitPrice: 4.50 },
],
});
console.log('Order created:', order.orderNumber, order.status);
// Adjust inventory after a manual count
await epos.inventory.stock.adjust(products[0].id, {
locationId: 'loc_01HXXXXXXXXXX',
quantity: -3,
reason: 'manual_count',
});
// Accrue loyalty points
await epos.loyalty.accounts.accruePoints(customers[0].id, {
orderId: order.id,
points: 50,
description: 'Purchase reward',
});All methods are async and return typed promises. Errors throw ElevatedPOSApiError.
client.catalog.products.list(params?) → Promise<PaginatedResponse<Product>>List products. Filter by categoryId, search, isActive, limit, cursor.
client.catalog.products.get(id) → Promise<ApiResponse<Product>>Retrieve a single product by ID.
client.catalog.products.create(data) → Promise<ApiResponse<Product>>Create a new product.
client.catalog.products.update(id, data) → Promise<ApiResponse<Product>>Partial update a product.
client.catalog.products.delete(id) → Promise<void>Delete a product.
client.catalog.categories.list() → Promise<PaginatedResponse<Category>>List all categories for the org.
client.catalog.categories.get(id) → Promise<ApiResponse<Category>>Retrieve a single category by ID.
client.orders.list(params?) → Promise<PaginatedResponse<Order>>List orders. Filter by status, customerId, locationId, limit, cursor.
client.orders.get(id) → Promise<ApiResponse<Order>>Retrieve a full order with line items.
client.orders.create(data) → Promise<ApiResponse<Order>>Create a new order with line items.
client.orders.updateStatus(id, status) → Promise<ApiResponse<Order>>Transition an order to a new status.
client.customers.list(params?) → Promise<PaginatedResponse<Customer>>List customers. Filter by search, limit, cursor.
client.customers.get(id) → Promise<ApiResponse<Customer>>Retrieve a single customer by ID.
client.customers.create(data) → Promise<ApiResponse<Customer>>Create a new customer record.
client.customers.update(id, data) → Promise<ApiResponse<Customer>>Partial update a customer.
client.inventory.stock.list(params?) → Promise<PaginatedResponse<StockLevel>>List stock levels. Filter by locationId, lowStock, productId.
client.inventory.stock.get(productId, locationId) → Promise<ApiResponse<StockLevel>>Get stock level for a specific product at a location.
client.inventory.stock.adjust(productId, data) → Promise<ApiResponse<StockAdjustment>>Adjust inventory quantity with a reason.
client.loyalty.accounts.get(customerId) → Promise<ApiResponse<LoyaltyAccount>>Get loyalty account for a customer.
client.loyalty.accounts.transactions(accountId, params?) → Promise<PaginatedResponse<LoyaltyTransaction>>List points transactions for an account.
client.loyalty.accounts.accruePoints(accountId, data) → Promise<ApiResponse<LoyaltyTransaction>>Add loyalty points to an account.
client.loyalty.accounts.redeemPoints(accountId, data) → Promise<ApiResponse<LoyaltyTransaction>>Redeem loyalty points from an account.
client.webhooks.list() → Promise<PaginatedResponse<Webhook>>List all registered webhooks for the org.
client.webhooks.create(data) → Promise<ApiResponse<Webhook>>Register a new webhook endpoint. Secret returned only once.
client.webhooks.update(id, data) → Promise<ApiResponse<Webhook>>Update URL, events, label, or enabled state.
client.webhooks.delete(id) → Promise<void>Remove a webhook endpoint.
client.webhooks.test(id) → Promise<ApiResponse<...>>Send a test ping to the webhook URL.
client.webhooks.deliveries(id, params?) → Promise<PaginatedResponse<WebhookDelivery>>List recent delivery attempts with status and duration.
ElevatedPOS signs every delivery with X-ElevatedPOS-Signature: sha256=HMAC(secret, body). Use verifyWebhookSignature to validate incoming requests.
import { verifyWebhookSignature } from '@elevatedpos/api-client';
// Next.js App Router example
export async function POST(request: Request) {
const rawBody = await request.text();
const signature = request.headers.get('x-elevatedpos-signature') ?? '';
const isValid = await verifyWebhookSignature(
rawBody,
signature,
process.env.ELEVATEDPOS_WEBHOOK_SECRET!,
);
if (!isValid) {
return new Response('Invalid signature', { status: 401 });
}
const event = JSON.parse(rawBody);
console.log('Event type:', event.type); // e.g. 'order.completed'
console.log('Payload:', event.data);
return new Response('OK', { status: 200 });
}const { data: webhook } = await epos.webhooks.create({
url: 'https://yourapp.com/api/elevatedpos-webhook',
events: ['order.completed', 'payment.captured', 'inventory.low_stock'],
label: 'Production handler',
});
// IMPORTANT: save webhook.secret securely now — it is never shown again
// Store it in an environment variable or secrets manager, never log it
// Later: list deliveries for debugging
const { data: deliveries } = await epos.webhooks.deliveries(webhook.id);
deliveries.forEach((d) => {
console.log(d.event, d.success, d.statusCode, `${d.durationMs}ms`);
});All failed requests throw a typed ElevatedPOSApiError. Network timeouts throw with status === 0.
import { createClient, ElevatedPOSApiError } from '@elevatedpos/api-client';
const epos = createClient({ apiKey: process.env.ELEVATEDPOS_API_KEY! });
try {
const { data: product } = await epos.catalog.products.get('nonexistent-id');
} catch (err) {
if (err instanceof ElevatedPOSApiError) {
// Strongly typed properties:
console.error(err.status); // 404
console.error(err.message); // "Not Found"
console.error(err.detail); // "Product not found"
console.error(err.type); // "https://elevatedpos.com.au/errors/not-found"
// Convenience getters:
if (err.isNotFound) { /* handle 404 */ }
if (err.isValidationError) { /* handle 422 — check err.detail */ }
if (err.isUnauthorized) { /* re-authenticate */ }
if (err.isForbidden) { /* insufficient permissions */ }
if (err.isServerError) { /* 5xx — retry with backoff */ }
}
}All types are exported from @elevatedpos/api-client.
interface ElevatedPOSClientConfig {
apiKey: string; // required — server-side only
baseUrl?: string; // default: https://api.elevatedpos.com.au
timeout?: number; // default: 30_000 ms
}interface Product {
id: string; orgId: string; name: string; sku: string;
basePrice: number; costPrice?: number; isActive: boolean;
categoryId?: string; tags: string[]; description?: string;
imageUrl?: string; barcode?: string; taxRate?: number;
createdAt: string; updatedAt: string;
}interface Order {
id: string; orgId: string; orderNumber: string;
status: 'pending'|'confirmed'|'in_progress'|'ready'|'completed'|'cancelled'|'refunded';
subtotal: number; discountTotal: number; taxTotal: number; total: number;
customerId?: string; locationId?: string; lines: OrderLine[];
createdAt: string; updatedAt: string;
}interface Webhook {
id: string; orgId: string; label: string; url: string;
events: WebhookEvent[]; enabled: boolean;
secret?: string; // only present on creation response
createdAt: string; updatedAt: string;
}class ElevatedPOSApiError extends Error {
status: number; // HTTP status code (0 = network error)
type: string; // RFC 9457 problem type URI
message: string; // human-readable title
detail?: string; // additional detail
get isUnauthorized(): boolean // status === 401
get isForbidden(): boolean // status === 403
get isNotFound(): boolean // status === 404
get isValidationError(): boolean // status === 422
get isServerError(): boolean // status >= 500
}Issues, pull requests, and discussions welcome.