ExisOne Node.js SDK

Embed our cross-platform Node.js/TypeScript library to generate hardware IDs, activate and validate licenses, and send support tickets. Perfect for Node.js backends and Electron desktop apps.

Package: exisone-client · Version: 0.10.0 · Node.js: 18+
View on npm →

Install

npm install exisone-client

Or with yarn/pnpm:

yarn add exisone-client
pnpm add exisone-client

Initialize

import { ExisOneClient } from 'exisone-client';

const client = new ExisOneClient({
    baseUrl: 'https://your-api-host',  // must be https
    accessToken: 'exo_at_<public>_<secret>',  // create in Access Tokens UI
    offlinePublicKey: undefined,  // optional: set for offline license validation
    hardwareIdOverride: undefined  // optional: per-user/per-tenant ID (e.g. Docker)
});

// Optional: change base URL later
client.withBaseUrl('https://another-host');

// Library version
const sdkVersion = client.getVersion();

baseUrl can also be set via environment variable EXISONE_BASEURL.

For offline license validation, set offlinePublicKey to your tenant's RSA public key (PEM format). Obtain this from the Crypto Keys page.

Capabilities

Quick Start

import { ExisOneClient } from 'exisone-client';

// Initialize
const client = new ExisOneClient({
    baseUrl: 'https://www.exisone.com',
    accessToken: 'exo_at_xxx_yyy'
});

// 1) Hardware ID (store locally)
const hwid = client.generateHardwareId();

// 2) Activation with version (user enters key/email)
const result = await client.activate({
    activationKey,
    email: userEmail,
    hardwareId: hwid,
    productName: 'MyProduct',
    version: '1.0.0'
});

if (!result.success) {
    if (result.errorCode === 'version_outdated') {
        console.log(`Please upgrade to version ${result.minimumRequiredVersion}`);
    } else {
        console.log(result.errorMessage);
    }
}

// 3) Validate on app start (requires 'verify' permission)
const validation = await client.validate({
    activationKey,
    hardwareId: hwid,
    productName: 'MyProduct',
    version: '1.0.0'
});

if (validation.status === 'version_outdated') {
    console.log(`Update required: minimum version is ${validation.minimumRequiredVersion}`);
} else if (validation.isValid) {
    console.log(`Licensed until: ${validation.expirationDate}`);
    console.log(`Features: ${validation.features.join(', ')}`);
}

// 4) Optional: Deactivate from the same hardware
const success = await client.deactivate({
    activationKey,
    hardwareId: hwid,
    productName: 'MyProduct'
});

// 5) (Publisher tooling) Generate a new key for a product
const key = await client.generateActivationKey({
    productName: 'MyProduct',
    email: 'user@example.com',
    planId: 1
});

// 6) Submit support ticket from client
await client.sendSupportTicket({
    productName: 'MyProduct',
    email: userEmail,
    subject: 'Subject',
    message: 'Message body'
});

TypeScript Support

Full TypeScript support with all types exported:

import {
    ExisOneClient,
    ExisOneClientOptions,
    ActivationResult,
    ValidationResult,
    OfflineValidationResult,
    SmartValidationResult,
    DeactivationResult,
    generateHardwareId
} from 'exisone-client';

Offline License Validation

For customers without internet access, generate offline activation codes from the License Keys page by providing their Hardware ID.

Setup

const client = new ExisOneClient({
    baseUrl: 'https://your-api-host',
    accessToken: 'your-token',
    offlinePublicKey: `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...
-----END PUBLIC KEY-----`
});

Smart Validation (Recommended)

Auto-detects online vs offline keys. Falls back to offline if server is unreachable.

const hwid = client.generateHardwareId();

// Works with both online keys (XXXX-XXXX-XXXX-XXXX) and offline codes
const result = await client.validateSmart({
    activationKeyOrOfflineCode: licenseKeyOrOfflineCode,
    hardwareId: hwid,
    productName: 'MyProduct'
});

if (result.isValid) {
    console.log(`Licensed until: ${result.expirationDate}`);
    console.log(`Offline mode: ${result.wasOffline}`);
    console.log(`Features: ${result.features.join(', ')}`);
} else {
    console.log(`Invalid: ${result.errorMessage}`);
}

Direct Offline Validation

// Validate completely offline - no server calls
const result = client.validateOffline(offlineCode, hwid);

if (result.isValid) {
    console.log(`Product: ${result.productName}`);
    console.log(`Expires: ${result.expirationDate}`);
} else if (result.isExpired) {
    console.log('License expired');
} else if (result.hardwareMismatch) {
    console.log('Wrong machine - license bound to different hardware');
} else {
    console.log(`Invalid: ${result.errorMessage}`);
}

CommonJS Usage

const { ExisOneClient } = require('exisone-client');

const client = new ExisOneClient({
    baseUrl: 'https://www.exisone.com',
    accessToken: 'your-api-token'
});

Electron Apps

The SDK works in both main and renderer processes (with Node.js integration enabled):

// main.js (Electron main process)
const { ExisOneClient } = require('exisone-client');

const client = new ExisOneClient({
    baseUrl: 'https://www.exisone.com',
    accessToken: process.env.EXISONE_TOKEN
});

// Generate hardware ID once at startup
const hardwareId = client.generateHardwareId();

// Expose to renderer via IPC
ipcMain.handle('validate-license', async (event, activationKey) => {
    return client.validate({ activationKey, hardwareId });
});

Error Handling

import { ExisOneClient } from 'exisone-client';

const client = new ExisOneClient(options);

try {
    const result = await client.validate({ activationKey, hardwareId });
} catch (err) {
    if (err instanceof Error) {
        console.log(`Error: ${err.message}`);
    }
}

API Reference

// Version
getVersion(): string

// Hardware
generateHardwareId(): string

// Activation
activate(params: {
    activationKey: string;
    email: string;
    hardwareId: string;
    productName: string;
    version?: string;
}): Promise<ActivationResult>

// Validation (Online)
validate(params: {
    activationKey: string;
    hardwareId: string;
    productName?: string;
    version?: string;
}): Promise<ValidationResult>

// Validation (Offline)
validateOffline(offlineCode: string, hardwareId: string): OfflineValidationResult
validateSmart(params: {
    activationKeyOrOfflineCode: string;
    hardwareId: string;
    productName?: string;
}): Promise<SmartValidationResult>

// Deactivation
deactivate(params: {
    activationKey: string;
    hardwareId: string;
    productName: string;
}): Promise<boolean>
deactivateSmart(params: {
    activationKeyOrOfflineCode: string;
    hardwareId: string;
    productName: string;
}): Promise<DeactivationResult>

// Generation (publisher)
generateActivationKey(params: {
    productName: string;
    email: string;
    planId?: number;
    validityDays?: number;
}): Promise<string>

// Features
getLicensedFeatures(activationKey: string): Promise<string[]>

// Support
sendSupportTicket(params: {
    productName: string;
    email: string;
    subject: string;
    message: string;
}): Promise<void>

// Configuration
withBaseUrl(baseUrl: string): ExisOneClient

Environment Variables

VariableDescription
EXISONE_BASEURLDefault base URL if not specified in options

Requirements

Corporate Seat Usage New in 0.10.0

For corporate (multi-seat) licenses, getSeatUsage() returns the current consumption and remaining capacity so your app can show customers how much of their license is in use and warn them before they hit the cap. Only meaningful for corporate licenses — standard single-device licenses return 404 and the method throws.

const usage = await client.getSeatUsage(corporateKey);

if (usage.isUnlimited) {
    console.log(`${usage.currentSeats} seats in use (unlimited)`);
} else {
    console.log(`${usage.currentSeats} / ${usage.maxSeats} seats in use`);
    if (usage.remaining !== null && usage.remaining <= 5) {
        console.log(`WARNING: only ${usage.remaining} seat(s) remaining`);
    }
    if (!usage.hasCapacity) {
        console.log('License is FULL. Release a seat or upgrade before adding new users.');
    }
}

Fields on SeatUsage:

Hardware ID Override (Docker / Multi-Tenant) New in 0.8.0

By default, generateHardwareId() derives a fingerprint from the local machine (CPU, MAC, machine-id, etc.). In environments where that fingerprint is unreliable or shared across users — most commonly Docker containers, multi-tenant servers, or hosted SaaS deployments — set hardwareIdOverride on the options to a stable per-user identifier (e.g. a UUID stored in your user record). When set, generateHardwareId() returns it verbatim and skips all local fingerprinting.

Combined with a Corporate (multi-seat) license, this lets each user inside the container consume one seat against the same license key:

import { ExisOneClient } from 'exisone-client';

// One client instance per signed-in user
const userPseudoHwid = currentUser.licenseHardwareId; // UUID stored on the user row

const client = new ExisOneClient({
    baseUrl: 'https://your-api-host',
    accessToken: 'exo_at_PUBLIC_SECRET',
    hardwareIdOverride: userPseudoHwid,
});

// generateHardwareId() now returns the override (no machine-id reads)
const hwid = client.generateHardwareId();

// Activate consumes one seat on the corporate license for this user
const result = await client.activate({
    activationKey: corporateKey,
    email: currentUser.email,
    hardwareId: hwid,
    productName: 'MyProduct',
});

// On every container start (or periodic re-check), validate the same hwid
const validation = await client.validate({
    activationKey: corporateKey,
    hardwareId: hwid,
    productName: 'MyProduct',
});

// When a user is removed, release their seat
await client.deactivate({
    activationKey: corporateKey,
    hardwareId: hwid,
    productName: 'MyProduct',
});
Persist the override. Generate the per-user UUID once and store it on the user record. If you regenerate it on each request you will consume a new seat every time.

For a complete Docker integration walkthrough (including admin trial check on container startup), see the Docker AI Prompt.

Common Errors

Changelog

See Also