npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@dxsoft/sdk

v2.0.0

Published

DXSoft SDK for Shopify Apps - Event tracking, revenue analytics, engagement scoring, and automation

Readme

@dxsoft/sdk

npm version TypeScript License: MIT Node.js

Official SDK for integrating Shopify Apps with DXSoft — the central platform for Support, Marketing, Revenue Analytics & Automation.

Requirements

  • Node.js >= 18 (uses native fetch and Web Crypto API)
  • A DXSoft API key (get one at your DXSoft dashboard)

Installation

npm install @dxsoft/sdk
# or
yarn add @dxsoft/sdk
# or
pnpm add @dxsoft/sdk

Quick Start

import { createDXSoft } from '@dxsoft/sdk';

const dxsoft = createDXSoft({
  apiKey: 'dxs_your-api-key',
});

// Track app installation
await dxsoft.trackAppInstalled('example-shop.myshopify.com', {
  name: 'Example Shop',
  email: '[email protected]',
  locale: 'de',
});

// Auto-identify from Shopify (fetches all shop data automatically)
await dxsoft.identifyFromShopify('shop.myshopify.com', shopifyAccessToken);

// Track revenue
await dxsoft.trackRecurringCharge('shop.myshopify.com', {
  amount: 29.99,
  currency: 'USD',
  interval: 'monthly',
  plan: 'pro',
});

Configuration

import { createDXSoft } from '@dxsoft/sdk';

const dxsoft = createDXSoft({
  apiKey: 'dxs_your-api-key',    // Required: Your DXSoft API key
  baseUrl: 'https://...',         // Optional: Custom API endpoint
  timeout: 10000,                 // Optional: Request timeout in ms (default: 10000, 0 = no timeout)
  retries: 3,                     // Optional: Retries for 5xx/network errors (default: 3, 0 = no retries)
  debug: false,                   // Optional: Enable debug logging
  shopifyApiVersion: '2026-04',   // Optional: Shopify API version (default: '2026-04')
});

API Reference

| Method | Description | |--------|-------------| | Event Tracking | | | track(shop, type, payload, opts?) | Track any custom event | | trackSystem(shop, type, payload, opts?) | Track typed system event | | trackBatch(events) | Track up to 100 events in one call | | trackAppInstalled(shop, meta?) | Track app installation | | trackAppUninstalled(shop, reason?) | Track app uninstall | | trackPlanUpgraded(shop, data) | Track plan upgrade | | trackPlanDowngraded(shop, data) | Track plan downgrade | | trackPlanCancelled(shop, data) | Track plan cancellation | | trackFeatureUsed(shop, data) | Track feature usage | | trackPaymentSucceeded(shop, data) | Track successful payment | | trackPaymentFailed(shop, data) | Track failed payment | | trackError(shop, data) | Track error occurrence | | Revenue Tracking | | | trackRecurringCharge(shop, data) | Track subscription (auto MRR/ARR) | | trackOneTimeCharge(shop, data) | Track one-time purchase | | trackUsageCharge(shop, data) | Track usage-based charge | | trackRefund(shop, data) | Track refund | | trackTrialStarted(shop, data) | Track trial start | | trackTrialEnded(shop, data) | Track trial end/conversion | | Shop Management | | | identify(shop, identity) | Full shop profile enrichment | | identifyFromShopify(shop, token) | Auto-identify via Shopify API | | identifyFromShopifyData(shop, data) | Identify from raw Shopify data | | updateShop(shop, data) | Partial shop update | | getShop(shop, includeEvents?) | Get shop details | | listShops(options?) | List/search shops | | addShopTags(shop, tags) | Add tags | | removeShopTags(shop, tags) | Remove tags | | setShopTags(shop, tags) | Replace all tags | | setShopFlags(shop, flags) | Set flags (VIP, churned, etc.) | | updateShopKpis(shop, kpis) | Update KPIs | | getShopApps(shop) | Get installed apps | | getEngagementScore(shop) | Get engagement score (0-100) | | Webhooks | | | createWebhookHandler(options?) | Create Shopify webhook handler | | Translations | | | translations.listLanguages() | List all 95+ supported languages | | translations.getTenantLanguages() | Get tenant's active languages | | translations.addLanguage(lang) | Enable a language for tenant | | translations.removeLanguage(lang) | Disable a language | | translations.setDefaultLanguage(lang) | Set default language | | translations.translateContent(opts) | Translate text on-the-fly | | translations.getArticleTranslations(id) | Get article translations | | translations.startBatch(opts) | Start batch translation job | | translations.getJobStatus(jobId) | Check job progress | | translations.listJobs() | List all translation jobs | | translations.getCoverage() | Get translation coverage stats |


Event Tracking

Custom Events

await dxsoft.track('shop.myshopify.com', 'custom.event', {
  anyKey: 'anyValue',
  count: 42,
});

// With idempotency key (prevents duplicate processing)
await dxsoft.track(
  'shop.myshopify.com',
  'order.created',
  { orderId: '12345', total: 99.99 },
  { idempotencyKey: 'order-12345' }
);

System Events

// App lifecycle
await dxsoft.trackAppInstalled('shop.myshopify.com', { name: 'Shop', email: '[email protected]' });
await dxsoft.trackAppUninstalled('shop.myshopify.com', 'Too expensive');

// Plan changes
await dxsoft.trackPlanUpgraded('shop.myshopify.com', {
  fromPlan: 'free', toPlan: 'pro', price: 29.99, currency: 'USD',
});

// Feature usage
await dxsoft.trackFeatureUsed('shop.myshopify.com', {
  feature: 'bulk-edit', action: 'update-prices', value: 150,
});

Batch Events

Send up to 100 events per call. Automatically chunks larger arrays.

const result = await dxsoft.trackBatch([
  { shopDomain: 'shop1.myshopify.com', eventType: 'feature.used', payload: { feature: 'export' } },
  { shopDomain: 'shop2.myshopify.com', eventType: 'app.installed' },
  { shopDomain: 'shop3.myshopify.com', eventType: 'plan.upgraded', payload: { fromPlan: 'free', toPlan: 'pro' } },
]);

console.log(`${result.succeeded}/${result.total} events sent`);

Revenue Tracking

Automatic MRR/ARR calculation for recurring charges.

// Recurring charge — MRR and ARR are auto-calculated
await dxsoft.trackRecurringCharge('shop.myshopify.com', {
  amount: 29.99,
  currency: 'USD',
  interval: 'monthly',   // 'monthly' | 'annual' | 'every_30_days'
  plan: 'pro',
  status: 'active',
});

// One-time charge
await dxsoft.trackOneTimeCharge('shop.myshopify.com', {
  amount: 49.99, currency: 'USD', name: 'Premium Export Pack',
});

// Usage-based charge
await dxsoft.trackUsageCharge('shop.myshopify.com', {
  amount: 5.00, currency: 'USD', description: '500 extra API calls',
});

// Refund
await dxsoft.trackRefund('shop.myshopify.com', {
  amount: 29.99, currency: 'USD', reason: 'Customer request',
  originalChargeId: 'charge_123',
});

// Trial tracking
await dxsoft.trackTrialStarted('shop.myshopify.com', {
  plan: 'pro', trialDays: 14,
});

await dxsoft.trackTrialEnded('shop.myshopify.com', {
  plan: 'pro', converted: true, // false + reason if not converted
});

Shop Identification

Auto-Identify from Shopify

Fetches all shop data from the Shopify Admin API and enriches your DXSoft profile automatically:

// In your Shopify OAuth callback:
await dxsoft.identifyFromShopify('shop.myshopify.com', shopifyAccessToken);
// → Pulls: name, email, owner, phone, country, locale, timezone, currency, plan, etc.

// If you already have the Shopify shop data:
await dxsoft.identifyFromShopifyData('shop.myshopify.com', shopifyShopObject);

Manual Identification

await dxsoft.identify('shop.myshopify.com', {
  email: '[email protected]',
  ownerName: 'Max Mustermann',
  country: 'DE',
  city: 'Berlin',
  timezone: 'Europe/Berlin',
  shopifyPlan: 'advanced',
  kpis: { totalOrders: 5000, monthlyRevenue: 12000 },
  flags: { vip: true, verified: true },
  tags: ['high-value', 'german-market', 'enterprise'],
});

Engagement Score

Get a 0-100 engagement score based on event frequency, feature adoption, recency, and revenue:

const { data } = await dxsoft.getEngagementScore('shop.myshopify.com');

console.log(data.score);  // 75
console.log(data.level);  // 'cold' | 'warm' | 'hot' | 'power-user'
console.log(data.factors); // { eventFrequency, featureAdoption, recency, revenue }

if (data.level === 'cold') {
  // Trigger re-engagement campaign
}

Webhook Handler

Automatically maps Shopify webhooks to DXSoft events. Supports HMAC signature verification.

const handler = dxsoft.createWebhookHandler({
  secret: process.env.SHOPIFY_WEBHOOK_SECRET,  // Enable HMAC verification
  onUninstall: async (shop) => {
    console.log(`${shop} uninstalled the app`);
  },
  onShopUpdate: async (shop, body) => {
    console.log(`${shop} updated their shop`);
  },
  onSubscriptionUpdate: async (shop, body) => {
    console.log(`${shop} changed their subscription`);
  },
});

// Express.js example
app.post('/webhooks/:topic', express.raw({ type: 'application/json' }), async (req, res) => {
  await handler(
    req.params.topic.replace('-', '/'),
    req.headers['x-shopify-shop-domain'] as string,
    JSON.parse(req.body),
    req.headers['x-shopify-hmac-sha256'] as string,  // HMAC header
    req.body,                                          // Raw body for verification
  );
  res.sendStatus(200);
});

Handled webhook topics:

  • app/uninstalled → tracks uninstall, sets churned flag
  • shop/update → auto-enriches shop profile (email, country, plan, locale, timezone)
  • app_subscriptions/update → tracks revenue changes with correct billing interval, handles active, accepted (trial), frozen, cancelled
  • All others → tracked as generic webhook.* events

Recommended Shopify webhook subscriptions:

app/uninstalled              — Required for churn tracking
shop/update                  — Required for shop data enrichment (E10)
app_subscriptions/update     — Required for revenue tracking + billingInterval (E9)

Note: The shop/update webhook provides owner email, country, locale, timezone, and Shopify plan. Call identifyFromShopify() during OAuth for initial enrichment, then rely on webhooks for updates.

Error Handling

The SDK provides structured error classes:

import {
  DXSoftError,              // Base error (all SDK errors)
  DXSoftNetworkError,       // Network/DNS failures
  DXSoftApiError,           // API 4xx/5xx responses
  DXSoftValidationError,    // Invalid input
  DXSoftTimeoutError,       // Request timeout
  DXSoftWebhookVerificationError, // HMAC mismatch
} from '@dxsoft/sdk';

try {
  await dxsoft.track('', 'event', {});
} catch (error) {
  if (error instanceof DXSoftValidationError) {
    console.log(error.code);    // 'VALIDATION_ERROR'
    console.log(error.message); // 'shopDomain is required...'
  }
}

// Most methods return { success, data?, error? } instead of throwing:
const result = await dxsoft.track('shop.myshopify.com', 'event', {});
if (!result.success) {
  console.error(result.error); // { code: 'RATE_LIMIT_EXCEEDED', message: '...' }
}

Debug Mode

Enable verbose logging to see all API calls:

const dxsoft = createDXSoft({
  apiKey: 'dxs_...',
  debug: true, // Logs to console.error with [DXSoft SDK v0.4.0] prefix
});

TypeScript Support

Full TypeScript support with exported types:

import type {
  DXSoftConfig, TrackOptions, TrackResult, ShopIdentity, ShopData,
  ApiResult, ShopListResult, ShopAppData, ShopMetadata,
  EventInput, SystemEventType, BatchEventInput, BatchResult,
  RecurringChargeData, OneTimeChargeData, UsageChargeData, RefundData, TrialData,
  ShopifyShopData, ShopifyWebhookTopic, WebhookHandler, EngagementScore,
} from '@dxsoft/sdk';

Translations API

Manage multi-language support for your knowledge base articles and templates. DXSoft uses automatic translation powered by Google Translate with manual override support.

Language Management

// List all supported languages (95+)
const { data: languages } = await dxsoft.translations.listLanguages();
// → [{ code: 'de', name: 'German', nativeName: 'Deutsch' }, ...]

// Get languages enabled for your tenant
const { data: tenantLangs } = await dxsoft.translations.getTenantLanguages();
// → [{ language: 'en', isDefault: true, isActive: true }, { language: 'de', ... }]

// Add a language to your tenant
await dxsoft.translations.addLanguage('fr');

// Remove a language
await dxsoft.translations.removeLanguage('fr');

// Set default language
await dxsoft.translations.setDefaultLanguage('en');

Content Translation

// Translate any text content on-the-fly
const { data } = await dxsoft.translations.translateContent({
  texts: ['Hello World', 'How can we help?'],
  targetLanguage: 'de',
  sourceLanguage: 'en', // optional, auto-detected if omitted
});
// → { translations: ['Hallo Welt', 'Wie können wir helfen?'] }

// Get all translations for a knowledge base article
const { data: translations } = await dxsoft.translations.getArticleTranslations(articleId);
// → [{ language: 'de', title: '...', body: '...', isComplete: true }, ...]

Batch Translation (Background Jobs)

// Start a batch translation job for all untranslated content
const { data: job } = await dxsoft.translations.startBatch({
  targetLanguage: 'fr',
  scope: 'knowledge', // 'knowledge' | 'templates' | 'all'
});
// → { jobId: 'job_abc123', status: 'pending' }

// Check job status
const { data: status } = await dxsoft.translations.getJobStatus(job.jobId);
// → { status: 'completed', progress: 100, itemsTotal: 42, itemsTranslated: 42 }

// List all translation jobs
const { data: jobs } = await dxsoft.translations.listJobs();

Translation Coverage

// Get translation coverage statistics
const { data: coverage } = await dxsoft.translations.getCoverage();
// → { languages: [{ code: 'de', articles: 42, total: 50, percent: 84 }, ...] }

Help Center Multi-Language

Your public help center (/help/[appSlug]) automatically supports multi-language:

  • Browser detection: Visitors see articles in their browser language (if translated)
  • URL parameter: Use ?lang=fr to force a specific language
  • Language switcher: Built-in UI dropdown for available languages
  • Fallback: If no translation exists, the original article is shown

Best Practices

  1. Use idempotency keys for events that shouldn't be duplicated (orders, payments)
  2. Call identifyFromShopify() in your OAuth callback to enrich shop profiles automatically
  3. Set up the webhook handler for real-time uninstall tracking and profile enrichment
  4. Track revenue to enable MRR/ARR dashboards and churn detection
  5. Use engagement scores to segment and target shops for campaigns
  6. Register all 3 recommended webhooks (app/uninstalled, shop/update, app_subscriptions/update) for complete data coverage
  7. app_subscriptions/update is critical — it provides billing interval data that the Partner API no longer exposes (2026-01 breaking change)
  8. Handle errors gracefully — most methods return { success, error } instead of throwing
  9. Enable debug mode in development to see all API calls
  10. Add languages first via translations.addLanguage() before starting batch translations
  11. Use batch jobs for translating large content libraries — they run in the background
  12. Check translation coverage to see which languages need more translations

License

MIT