TypeScript SDK

Complete reference for the @slootly/sdk npm package.

Installation

bash
npm install @slootly/sdk

Initialization

Simple (API key only)

typescript
import { Slootly } from '@slootly/sdk';

const gate = new Slootly('sk_live_your_api_key');

Full Configuration

typescript
import { Slootly } from '@slootly/sdk';

const gate = new Slootly({
  apiKey: 'sk_live_your_api_key',
  baseUrl: 'https://api.slootly.dev/v1',       // default
  cacheTtlMs: 300_000,                          // 5 minutes, default
  enableJwtVerification: true,                   // default
  timeout: 5_000,                                // 5 seconds, default
  checkoutBaseUrl: 'https://pay.slootly.dev',   // default
});

SlootlyConfig Options

PropertyTypeDefaultDescription
apiKeystring(required)API key starting with sk_live_ or sk_test_
baseUrlstringhttps://api.slootly.dev/v1Slootly API base URL
cacheTtlMsnumber300000In-memory cache TTL in milliseconds
enableJwtVerificationbooleantrueEnable JWT offline verification
timeoutnumber5000HTTP request timeout in milliseconds
checkoutBaseUrlstringhttps://pay.slootly.devHosted checkout base URL
fetchtypeof fetchglobalThis.fetchCustom fetch (for testing or edge runtimes)

gate.check()

Check subscription status for a user on a platform. This is the primary SDK method. It checks in order: in-memory cache, JWT offline verification, then API call.

signaturetypescript
const sub = await gate.check(userId: string, platform: Platform): Promise<Subscription>

Parameters

ParameterTypeDescription
userIdstringPlatform-specific user ID (e.g., Telegram from.id as string)
platformPlatform'telegram' | 'discord' | 'whatsapp' | 'slack' | 'custom'

Returns: Subscription

PropertyTypeDescription
activebooleanWhether the user has an active subscription
planstring | nullPlan slug (e.g., 'pro'). null if no subscription
planIdstring | nullPlan ID
statusSubscriptionStatus'active' | 'trialing' | 'past_due' | 'canceled' | 'expired' | 'none'
currentPeriodStartDate | nullCurrent billing period start
currentPeriodEndDate | nullCurrent billing period end
cancelAtPeriodEndbooleanWhether the sub will cancel at period end
trialEndDate | nullTrial end date. null if not on trial
subscriberIdstring | nullSlootly subscriber ID
platformsPlatform[]All platforms linked to this subscriber
metadataRecord<string, unknown>Developer-defined metadata

Example

typescript
const sub = await gate.check('123456789', 'telegram');

if (sub.active) {
  console.log('Plan:', sub.plan);           // 'pro'
  console.log('Status:', sub.status);       // 'active'
  console.log('Expires:', sub.currentPeriodEnd);
  console.log('Platforms:', sub.platforms); // ['telegram', 'discord']
} else {
  console.log('No active subscription');
}

gate.checkoutUrl()

Generate a checkout URL for a user to subscribe to a plan. This is a synchronous method -- no API call needed.

signaturetypescript
const url = gate.checkoutUrl(
  userId: string,
  planSlug: string,
  platform: Platform,
  options?: CheckoutOptions
): string

Parameters

ParameterTypeDescription
userIdstringPlatform-specific user ID
planSlugstringPlan slug (e.g., 'pro')
platformPlatformPlatform name
options.successUrlstring?Custom success redirect URL
options.cancelUrlstring?Custom cancel redirect URL
options.emailstring?Pre-fill customer email
options.metadataRecord<string, string>?Additional metadata

Example

typescript
// Simple
const url = gate.checkoutUrl('123456789', 'pro', 'telegram');
// => https://pay.slootly.dev/checkout/pro?uid=123456789&platform=telegram

// With options
const url = gate.checkoutUrl('123456789', 'pro', 'telegram', {
  successUrl: 'https://t.me/mybot',
  email: 'user@example.com',
  metadata: { referral: 'homepage' },
});

gate.getPlans()

Fetch all active plans for the project.

signaturetypescript
const plans = await gate.getPlans(): Promise<Plan[]>

Plan Type

PropertyTypeDescription
idstringPlan ID
namestringDisplay name
slugstringURL-safe identifier
descriptionstring | nullPlan description
amountnumberPrice in smallest currency unit (cents)
currencystringCurrency code (e.g., 'usd')
intervalBillingInterval'day' | 'week' | 'month' | 'year'
intervalCountnumberBilling interval count
trialDaysnumberFree trial days
featuresstring[]Feature list
metadataRecord<string, unknown>Custom metadata

Example

typescript
const plans = await gate.getPlans();

for (const plan of plans) {
  console.log(`${plan.name}: $${plan.amount / 100}/${plan.interval}`);
  console.log('Features:', plan.features.join(', '));
}

gate.cancelSubscription()

Cancel a user's subscription. The subscription remains active until the end of the current billing period.

signaturetypescript
await gate.cancelSubscription(subscriberId: string): Promise<void>
typescript
const sub = await gate.check('123456789', 'telegram');
if (sub.subscriberId) {
  await gate.cancelSubscription(sub.subscriberId);
  // Subscription remains active until sub.currentPeriodEnd
}

gate.onEvent()

Create Express/Connect-compatible middleware for receiving Slootly webhooks.

typescript
import express from 'express';

const app = express();

app.post('/webhooks/slootly', gate.onEvent((event) => {
  switch (event.type) {
    case 'subscription.created':
      console.log('New subscriber:', event.data.subscriberId);
      break;
    case 'subscription.canceled':
      console.log('Canceled:', event.data.subscriberId);
      break;
    case 'payment.failed':
      console.log('Payment failed:', event.data.subscriberId);
      break;
  }
}));

gate.verifyWebhook()

Manually verify a webhook signature and parse the event.

signaturetypescript
const event = gate.verifyWebhook(
  payload: string,      // raw request body
  signature: string,    // Slootly-Signature header
  secret: string        // webhook secret (whsec_...)
): WebhookEvent
typescript
const event = gate.verifyWebhook(
  req.body,
  req.headers['slootly-signature'],
  'whsec_your_webhook_secret'
);
console.log(event.type);       // 'subscription.created'
console.log(event.data);       // WebhookEventData
console.log(event.createdAt);  // Date

Cache Management

typescript
// Clear all cached subscription data
gate.clearCache();

// Clean up SDK resources when shutting down
gate.destroy();

Error Handling

typescript
import { Slootly, ValidationError } from '@slootly/sdk';

try {
  const sub = await gate.check(userId, 'telegram');
} catch (err) {
  if (err instanceof ValidationError) {
    // Invalid input (empty userId, invalid platform)
    console.error('Validation error:', err.message);
  } else {
    // Network error, auth error, etc.
    console.error('Slootly error:', err);
  }
}

Info

The SDK gracefully handles network failures. If the API is unreachable and a cached JWT is available, the SDK falls back to JWT verification automatically. Your bot continues working even if Slootly is temporarily down.

Type Reference

typescript
type Platform = 'telegram' | 'discord' | 'whatsapp' | 'slack' | 'custom';

type SubscriptionStatus = 'active' | 'trialing' | 'past_due'
  | 'canceled' | 'expired' | 'none';

type BillingInterval = 'day' | 'week' | 'month' | 'year';

type WebhookEventType =
  | 'subscription.created'
  | 'subscription.renewed'
  | 'subscription.updated'
  | 'subscription.canceled'
  | 'payment.failed'
  | 'test.ping';