DashboardDeveloper Portal

TypeScript SDK

The official Fly client for Node.js and TypeScript. Strongly typed, zero production dependencies — uses the platform fetch API.

Pre-release — not yet on npm. The SDK (@nexus/api-client) currently lives in the Fly monorepo and has not been published to a public registry, and its API-key authentication is not yet enabled server-side. This page documents the client as it exists in source so you can evaluate the surface ahead of release. Want early access? Email developers@useflysystems.com.

Code samples on this page are illustrative examples — they reference example IDs and do not run against the live API. To call the API against your own seeded test org, use the read-write sandbox.

pre-releasev1.2.0ESM + CJSTypeScript

Installation

Package name: @nexus/api-client — coming soon to npm. The commands below will work once the package is published:

npm
npm install @nexus/api-client    # coming soon
pnpm
pnpm add @nexus/api-client      # coming soon
yarn
yarn add @nexus/api-client      # coming soon

Authentication

The SDK is built around API Key authentication for server-to-server use cases, with OAuth 2.0 for partner integrations that act on behalf of a merchant.

API Key (server-side)Not yet available

The client presents the key as Authorization: Bearer <key>, but there is no API-key program yet — nothing validates an API key server-side and no keys are issued, so these calls will return 401 until that program launches with the npm release. The credential the platform accepts from third parties today is OAuth 2.0 client_credentials.

import { NexusClient } from '@nexus/api-client';

const epos = new NexusClient({
  apiKey: process.env.ELEVATEDPOS_API_KEY!,   // never expose this in the browser
});

OAuth 2.0 (partner apps)

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 API access is currently in private beta.

// OAuth: use a proxy that injects the bearer token server-side
// and call your own backend endpoints to avoid exposing the token.
// The Fly API accepts:  Authorization: Bearer <access_token>
// Set this at the gateway level when forwarding merchant requests.

Quick Start

Open in Sandbox

Example code — not executed.

import { NexusClient } from '@nexus/api-client';

const epos = new NexusClient({ 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',
});

Method Reference

All methods are async and return typed promises. Errors throw NexusApiError.

catalog.products

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.

catalog.categories

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.

orders

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.

customers

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.

inventory.stock

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.

loyalty.accounts

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.

webhooks

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.

Webhook Verification

Fly signs every delivery with X-ElevatedPOS-Signature: sha256=HMAC(secret, body). Use verifyWebhookSignature to validate incoming requests.

import { verifyWebhookSignature } from '@nexus/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.event);  // e.g. 'test.ping'
  console.log('Payload:', event.data);

  return new Response('OK', { status: 200 });
}

Register a webhook

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`);
});

Error Handling

All failed requests throw a typed NexusApiError. Network timeouts throw with status === 0.

import { NexusClient, NexusApiError } from '@nexus/api-client';

const epos = new NexusClient({ apiKey: process.env.ELEVATEDPOS_API_KEY! });

try {
  const { data: product } = await epos.catalog.products.get('nonexistent-id');
} catch (err) {
  if (err instanceof NexusApiError) {
    // 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 */ }
  }
}

TypeScript Types

All types are exported from @nexus/api-client.

NexusClientConfig

interface NexusClientConfig {
  apiKey: string;           // required — server-side only (API-key auth not yet live, see above)
  baseUrl?: string;         // default: https://api.useflyconnect.com
  timeout?: number;         // default: 30_000 ms
}

Product

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;
}

Order

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;
}

Webhook

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;
}

NexusApiError

class NexusApiError 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
}

Want early access to the SDK?

We'll share the package and release timeline with beta developers.

Contact us