Skip to main content
The @kelviq/js-sdk is a lightweight, plain TypeScript library designed for interacting with the kelviq entitlement service. It allows you to:
  • Fetch all feature entitlements for a specific organization and customer.
  • Cache these entitlements client-side for efficient and repeated access.
  • Check if a user has access to a particular feature.
  • Retrieve specific entitlement details, including boolean flags, numeric configurations, and metered usage data.
  • Manually refresh entitlement data from the server.
If you are using React, you can use the React SDK instead.

Installation

Install the SDK using your preferred package manager or include it directly via CDN.
npm install @kelviq/js-sdk
# or
yarn add @kelviq/js-sdk
# or
pnpm add @kelviq/js-sdk
CDN Installation:
<script src="https://unpkg.com/@kelviq/js-sdk/dist/kelviq-sdk.umd.js"></script>

Basic Usage

Get started with the SDK in just a few lines of code.
import kelviqSDK from '@kelviq/js-sdk';

// Initialize the SDK (fetches entitlements automatically)
const kq = kelviqSDK({
  customerId: "your-customer-id",
  accessToken: "your-access-token",
});

// Wait for the initial fetch to complete
await kq.ready();

// Check if user has access to a feature
if (kq.hasAccess('premium-features')) {
  console.log("User has access to premium features!");
}

// Get specific entitlement details
const emailQuota = kq.getEntitlement('email-sends');
if (emailQuota && emailQuota.hasAccess) {
  console.log(`Emails remaining: ${emailQuota.remaining}`);
}

API Reference

Complete documentation of all SDK methods and configuration options.

Initialization

Create and configure your SDK instance with the required parameters.
Security Warning: Never use your Server API Key in the browser. Use the Client API Key which is safe to expose in client-side code. You can find both keys in the Developers section of your Kelviq dashboard.
// Initialize the SDK
const kq = kelviqSDK({
  customerId: "your-customer-id",
  accessToken: "your-access-token",
});

Options

OptionTypeDescription
customerIdstringUnique identifier for the customer
accessTokenstringAuthentication token for API requests
environmentsandbox|production’production’The environment to use. If you want to use the sandbox, you must provide this.
initializeAndFetchbooleantrueAutomatically fetch entitlements on initialization
onError(error: Error) => voidError callback function

Core Methods

hasAccess(featureId: string): boolean

Check if the user has access to a specific feature. Returns false if the feature doesn’t exist or data hasn’t been fetched.
if (kq.hasAccess('premium-features')) {
  // Show premium features
  showPremiumUI();
}

getEntitlement(featureId: string): Entitlement | null

Get the aggregated entitlement for a specific feature. Returns the unified Entitlement object which includes the .items array of raw API objects.
// Boolean entitlement
const canUseAnalytics = kq.getEntitlement('analytics');
if (canUseAnalytics?.hasAccess) {
  enableAnalytics();
}

// Metered entitlement
const emailQuota = kq.getEntitlement('email-sends');
if (emailQuota?.hasAccess && emailQuota.remaining !== null && emailQuota.remaining > 0) {
  console.log(`Usage: ${emailQuota.currentUsage} / ${emailQuota.usageLimit}`);
  console.log(`Emails remaining: ${emailQuota.remaining}`);
}

getEntitlements(): Record<string, Entitlement> | null

Get all aggregated entitlements as a map keyed by featureId.
const allEntitlements = kq.getEntitlements();
if (allEntitlements) {
  Object.entries(allEntitlements).forEach(([featureId, entitlement]) => {
    console.log(`${featureId}: ${entitlement.hasAccess ? 'Access granted' : 'Access denied'}`);
  });
}

getRawEntitlement(featureId: string): RawEntitlement[] | null

Get the raw, un-aggregated API items for a specific feature. Useful when you need per-item details like resetAt or hardLimit.
const rawItems = kq.getRawEntitlement('email-sends');
if (rawItems) {
  rawItems.forEach(item => {
    console.log(`Reset at: ${item.resetAt}, Limit: ${item.usageLimit}`);
  });
}

getRawEntitlements(): RawEntitlementsApiResponse | null

Get the full raw API response including the customerId wrapper, exactly as received from the server.
const raw = kq.getRawEntitlements();
if (raw) {
  console.log(`Customer: ${raw.customerId}`);
  console.log(`Total items: ${raw.entitlements.length}`);
}

ready(): Promise<void>

Wait for the initial fetch (triggered by initializeAndFetch: true) to complete. Resolves immediately if no initial fetch was triggered or if the data is already available.
const kq = kelviqSDK({
  customerId: "your-customer-id",
  accessToken: "your-access-token",
});

await kq.ready();
// Entitlements are now available
if (kq.hasAccess('premium-features')) {
  showPremiumUI();
}

fetchAllEntitlements(forceRefresh?: boolean): Promise<Record<string, Entitlement>>

Manually trigger a network request to refresh the entitlements. If a fetch is already in progress, the same promise is returned (calls are deduplicated).
// Fetch fresh data
try {
  const entitlements = await kq.fetchAllEntitlements(true);
  console.log('Entitlements refreshed:', entitlements);
} catch (error) {
  console.error('Failed to refresh entitlements:', error);
}

isLoading(): boolean

Check if entitlements are currently being fetched.
if (kq.isLoading()) {
  showLoadingSpinner();
} else {
  hideLoadingSpinner();
}

getLastError(): Error | null

Get the last error that occurred during entitlement fetching.
const lastError = kq.getLastError();
if (lastError) {
  console.error('Last SDK error:', lastError.message);
}

clearCache(): void

Clear the cached entitlements.
kq.clearCache();
// Next call to hasAccess or getEntitlement will need a new fetch

Entitlement Types

The SDK uses a single, unified Entitlement interface. When the API returns duplicate featureIds (e.g., from Subscriptions + Top-ups), they are aggregated automatically.
interface Entitlement {
  featureId: string;
  featureType: "METER" | "BOOLEAN" | "CUSTOMIZABLE";
  hasAccess: boolean;         // METER: remaining === null || remaining > 0; others: true if any item grants access
  hardLimit: boolean;         // true if any item has a hard limit
  currentUsage: number;       // METER: summed across items; CUSTOMIZABLE: from first item; BOOLEAN: 0
  usageLimit: number | null;  // METER: summed across items; CUSTOMIZABLE: from first item; BOOLEAN: null
  remaining: number | null;   // METER: usageLimit - currentUsage; CUSTOMIZABLE: from first item; BOOLEAN: null
  items: RawEntitlement[];    // The raw array of original API objects for this featureId
}
Per-item details such as resetAt and hardLimit are available on each item in the items array.

Examples

Real-world usage patterns and common implementation scenarios.

Feature Gating

Control access to features based on user entitlements.
// Simple feature gate
if (kq.hasAccess('beta-features')) {
  showBetaFeatures();
}

// Conditional rendering
const canUseAdvancedSearch = kq.hasAccess('advanced-search');
return (
  <div>
    <BasicSearch />
    {canUseAdvancedSearch && <AdvancedSearch />}
  </div>
);

Usage-Based Features

Implement usage limits and quota management.
const apiCalls = kq.getEntitlement('api-calls');
if (apiCalls?.hasAccess) {
  if (apiCalls.remaining === null || apiCalls.remaining > 0) {
    // Allow API call
    makeApiCall();
    console.log(`Usage: ${apiCalls.currentUsage} / ${apiCalls.usageLimit}`);
  } else {
    // Show upgrade message
    showUpgradeMessage();
  }
}

Configuration-Based Features

Apply dynamic configuration based on user entitlements.
const teamSizeEntitlement = kq.getEntitlement('max-team-size');

if (teamSizeEntitlement?.hasAccess) {
  if (currentTeam.length >= (teamSizeEntitlement.usageLimit ?? Infinity)) {
    showUpgradePrompt(); // or disable the "Add Member" button
  } else {
    enableTeamInvite();
  }
}

Accessing Per-Item Details

When you need item-level details like resetAt or hardLimit, use the items array or the getRawEntitlement method.
// Via the aggregated entitlement's items array
const emailQuota = kq.getEntitlement('email-sends');
if (emailQuota) {
  emailQuota.items.forEach(item => {
    console.log(`Reset at: ${item.resetAt}, Hard limit: ${item.hardLimit}`);
  });
}

// Or via getRawEntitlement
const rawItems = kq.getRawEntitlement('email-sends');
if (rawItems) {
  rawItems.forEach(item => {
    console.log(`Reset at: ${item.resetAt}, Hard limit: ${item.hardLimit}`);
  });
}

Error Handling

Robust error handling for production applications.
const kq = kelviqSDK({
  customerId: "user123",
  accessToken: "token456",
  onError: (error) => {
    // Log to monitoring service
    logError('kelviq SDK Error', error);

    // Show user-friendly message
    showNotification('Unable to load feature settings', 'error');
  }
});

// Handle fetch errors
try {
  await kq.fetchAllEntitlements();
} catch (error) {
  console.error('Failed to load entitlements:', error);
  // Fallback to default behavior
  showDefaultFeatures();
}

Custom API Request Service

Integrate with your existing HTTP client or add custom logic.
// Custom API request function
async function customApiRequest<T>(config: ApiRequestConfig): Promise<ApiResponse<T>> {
  const response = await fetch(config.url, {
    method: config.method || 'GET',
    headers: {
      'Authorization': `Bearer ${config.accessToken}`,
      'Content-Type': 'application/json',
      ...config.headers
    },
    body: config.body ? JSON.stringify(config.body) : undefined
  });

  if (!response.ok) {
    throw new Error(`HTTP ${response.status}: ${response.statusText}`);
  }

  return {
    status: response.status,
    statusText: response.statusText,
    data: await response.json()
  };
}

// Use custom API request
const kq = kelviqSDK({
  customerId: "user123",
  accessToken: "token456"
}, customApiRequest);

TypeScript Support

Full TypeScript support with comprehensive type definitions. The SDK is written in TypeScript and provides full type safety:
import kelviqSDK, {
  KelviqClientOptions,
  Entitlement,
  RawEntitlement,
  RawEntitlementsApiResponse
} from '@kelviq/js-sdk';

const options: KelviqClientOptions = {
  customerId: "user123",
  accessToken: "token456"
};

const kq = kelviqSDK(options);

// Type-safe entitlement access
const emailQuota: Entitlement | null = kq.getEntitlement('emails');

Advanced Configuration

Advanced options for customizing SDK behavior and API integration.

API Configuration

Configure API request behavior and retry logic.
const kq = kelviqSDK({
  customerId: "user123",
  accessToken: "token456",
  apiConfig: {
    maxRetries: 5,
    timeout: 10000,
    backoffBaseDelay: 2000
  }
});

Custom Error Handling

Implement sophisticated error handling and recovery strategies.
const kq = kelviqSDK({
  customerId: "user123",
  accessToken: "token456",
  onError: (error) => {
    // Send to error tracking service
    Sentry.captureException(error);

    // Retry logic
    if (error.message.includes('network')) {
      setTimeout(() => kq.fetchAllEntitlements(), 5000);
    }
  }
});

Advanced Options

Additional configuration options for specialized use cases. Default values are:
const kq = kelviqSDK({
  customerId: "",
  accessToken: "",
  apiUrl: "https://edge.api.kelviq.com/api/v1",
  entitlementsPath: "entitlements/",
  apiConfig: {
    maxRetries: 3,
    timeout: 5000,
    backoffBaseDelay: 1000
  }
});
OptionTypeDescription
apiUrlstringBase URL for the API
entitlementsPathstringPath for fetching entitlements
apiConfig.maxRetriesnumberMaximum number of retries for API requests
apiConfig.timeoutnumberTimeout for API requests in milliseconds
apiConfig.backoffBaseDelaynumberBase delay for exponential backoff strategy

Using the Sandbox

If you want to use the sandbox, you need to set the environment option to sandbox in the init function.
const kq = kelviqSDK({
  customerId: "your-customer-id",
  accessToken: "your-access-token",
  environment: "sandbox"
});