@dxsoft/sdk
v2.0.0
Published
DXSoft SDK for Shopify Apps - Event tracking, revenue analytics, engagement scoring, and automation
Maintainers
Readme
@dxsoft/sdk
Official SDK for integrating Shopify Apps with DXSoft — the central platform for Support, Marketing, Revenue Analytics & Automation.
Requirements
- Node.js >= 18 (uses native
fetchand 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/sdkQuick 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, setschurnedflagshop/update→ auto-enriches shop profile (email, country, plan, locale, timezone)app_subscriptions/update→ tracks revenue changes with correct billing interval, handlesactive,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/updatewebhook provides owner email, country, locale, timezone, and Shopify plan. CallidentifyFromShopify()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=frto 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
- Use idempotency keys for events that shouldn't be duplicated (orders, payments)
- Call
identifyFromShopify()in your OAuth callback to enrich shop profiles automatically - Set up the webhook handler for real-time uninstall tracking and profile enrichment
- Track revenue to enable MRR/ARR dashboards and churn detection
- Use engagement scores to segment and target shops for campaigns
- Register all 3 recommended webhooks (
app/uninstalled,shop/update,app_subscriptions/update) for complete data coverage app_subscriptions/updateis critical — it provides billing interval data that the Partner API no longer exposes (2026-01 breaking change)- Handle errors gracefully — most methods return
{ success, error }instead of throwing - Enable debug mode in development to see all API calls
- Add languages first via
translations.addLanguage()before starting batch translations - Use batch jobs for translating large content libraries — they run in the background
- Check translation coverage to see which languages need more translations
License
MIT
