Skip to content

Sesamy SDK

The official TypeScript SDK (@sesamy/sdk) for direct API access to Sesamy services. This is a lower-level SDK that provides direct access to all Sesamy API endpoints with TypeScript support and built-in request deduplication and retry logic.

Difference from Sesamy JS

This is the lower-level API SDK for direct service integration. For browser-based content access control and user experience features, use Sesamy JS instead.

Features

  • Full TypeScript support with comprehensive type definitions
  • Request deduplication - Automatically deduplicates identical concurrent requests
  • Automatic retries - Built-in retry logic for failed requests with exponential backoff
  • Direct API access to all Sesamy endpoints including complete management APIs
  • Promise-based API with automatic token injection
  • Separate client and management APIs for different use cases
  • Tree-shaking support for optimal bundle sizes

Installation

bash
npm install @sesamy/sdk
bash
yarn add @sesamy/sdk
bash
pnpm add @sesamy/sdk

Requirements

  • Node.js 18+
  • TypeScript 5.0+ (recommended for full type support)

Quick Start

Client SDK

For client-side operations (user-facing features):

typescript
import { client } from '@sesamy/sdk';

const sdk = client({
  baseUrl: 'https://api.sesamy.com',
  tokenProvider: async () => {
    // Return JWT token for authentication
    return await getUserToken();
  },
});

// Get user profile
const profile = await sdk.profile.get('user-id');

// List entitlements
const entitlements = await sdk.entitlements.list();

// Check subscription status
const subscriptions = await sdk.subscriptions.list();

Management SDK

For server-side operations (admin/management features):

typescript
import { management } from '@sesamy/sdk';

const sdk = management({
  baseUrl: 'https://api.sesamy.com',
  tokenProvider: async () => {
    // Return management JWT token
    return await getManagementToken();
  },
});

// List all users
const users = await sdk.users.list();

// Create a new user
const newUser = await sdk.users.create({
  email: 'user@example.com',
  name: 'John Doe',
});

// Update user metadata
await sdk.users.metadata.set('user-id', 'preferences', 'theme=dark');

API Structure

The SDK is organized into logical domains for better discoverability:

Client API

  • profile - User profile management
  • entitlements - Access rights and permissions
  • subscriptions - Subscription management
  • products - Product information and onboarding
  • contracts - Contract management and amendments
  • userMetadata - User-specific metadata
  • tallies - Usage tracking and limits
  • tags - User tagging system
  • checkouts - Payment processing
  • bills - Billing and invoices
  • fulfillments - Content delivery
  • paywalls - Paywall configuration
  • paymentIssues - Payment problem handling
  • proxy - Content proxying
  • transactions - Transaction history
  • vendor - Vendor information

Management API

  • users - User management (CRUD operations, password management, metadata)
  • entitlements - Entitlement management and querying
  • products - Product management (CRUD operations, fulfillments)
  • contracts - Contract management and querying
  • bills - Billing and invoice management
  • transactions - Transaction history and management
  • vendors - Vendor management (CRUD operations)
  • accessLists - Access list and grant management
  • customers - Customer information retrieval (XML format)

Paywalls

Getting Paywall Configuration

The paywalls.get() method accepts either a paywall ID or a direct URL:

typescript
// Using paywall ID
const paywallConfig = await sdk.paywalls.get('paywall-id-123');

// Using direct URL (useful for external paywall services)
const paywallConfig = await sdk.paywalls.get('https://example.com/paywall');

The method automatically detects whether the input is a valid URL or a paywall ID:

  • If the input is a valid URL, the request is made directly to that URL
  • If the input is not a valid URL, it's treated as a paywall ID and the request is made to /paywalls/{id}

Checking Leaky Paywall Access

The paywalls.checkAccess() method checks whether a user can access content under a leaky (metered) paywall:

typescript
const result = await sdk.paywalls.checkAccess('paywall-id-123', {
  publisherContentId: 'article-2024-01-15',
  url: 'https://example.com/articles/my-article', // optional
});

if (result.status === 'allowed') {
  // User has free reads remaining
  console.log('Remaining free reads:', result.remaining);
  console.log('Signed link:', result.signedLink); // present when url was provided
} else {
  // result.status === 'paywall' — free limit reached
  console.log('Show paywall:', result.paywall);
}

Parameters:

  • paywallId (string) — the paywall ID
  • body.publisherContentId (string, required) — unique identifier of the content
  • body.url (string, optional) — article URL; when provided, the response includes a signedLink with a temporary access token

Executing a Paywall Strategy

The paywalls.executeStrategy() method evaluates a paywall strategy by ID against the visitor's context and returns the resolved paywall plus which node matched. The strategy's rules are evaluated against attribution (UTM tags, referrer) you supply, plus the visitor's device, geo and referral, which are derived server-side from the request — so you do not send user-agent or location:

typescript
const result = await sdk.paywalls.executeStrategy('strategy-acme-123', {
  attribution: {
    source: 'newsletter', // utm_source
    medium: 'email', // utm_medium
    campaign: 'acme-launch', // utm_campaign
    referrer: 'https://referrer.example.com/', // full referrer URL
  },
  content: { contentId: 'acme-article-001' }, // optional
});

console.log('Resolved paywall:', result.paywall);
console.log('Matched node:', result.matchedNode); // omitted when the fallback matched

// Debug mode: evaluate as if from a specific device and inspect the trace.
// `debugUserAgent` overrides the User-Agent and is honoured only when debug is true.
const debugResult = await sdk.paywalls.executeStrategy('strategy-acme-123', {
  attribution: { source: 'newsletter', medium: 'email' },
  debug: true,
  debugUserAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_2 like Mac OS X)',
});

console.log(debugResult.debug); // requester object, cache, timing, trace

Parameters:

  • strategyId (string) — the strategy ID
  • params.attribution (object, optional) — UTM tags (source, medium, campaign, term, content) and the full referrer URL
  • params.content (object, optional) — metadata about the gated content (contentId, title, type, price, currency, publisherId)
  • params.debug (boolean, optional) — include a debug envelope (requester object, cache, timing) in the response
  • params.debugUserAgent (string, optional) — debug-only override for the User-Agent the strategy is evaluated against (e.g. to test device rules); honoured only when debug is true

Returns: Promise<StrategyExecutionResult>{ paywallId, matchedNode?, paywall, debug? }

Configuration

SDK Configuration

typescript
interface SDKConfig {
  baseUrl: string; // API base URL
  tokenProvider: () => Promise<string>; // Function to provide auth tokens
  vendorId?: string; // Optional vendor ID for multi-tenant setups
}

Token Provider

The tokenProvider function should return a valid JWT token:

typescript
const sdk = client({
  baseUrl: 'https://api.sesamy.com',
  tokenProvider: async () => {
    // Implement your token retrieval logic
    const token = await fetchTokenFromYourAuthSystem();
    return token;
  },
});

Authentication

The SDK handles authentication automatically through the tokenProvider. Tokens are injected into API requests as Bearer tokens.

Token Management

typescript
// For client-side usage with user tokens
const clientSdk = client({
  baseUrl: 'https://api.sesamy.com',
  tokenProvider: async () => {
    return localStorage.getItem('user_token') || (await refreshToken());
  },
});

// For server-side usage with service tokens
const managementSdk = management({
  baseUrl: 'https://api.sesamy.com',
  tokenProvider: async () => {
    return await getServiceToken();
  },
});

HTTP Features

The SDK includes advanced HTTP features for robust and efficient API communication.

Request Deduplication

Identical concurrent requests are automatically deduplicated to prevent unnecessary API calls and improve performance:

typescript
// Multiple calls to the same endpoint with same parameters
// will be deduplicated automatically
const [profile1, profile2] = await Promise.all([
  sdk.profile.get('user-123'),
  sdk.profile.get('user-123'), // This will reuse the first request
]);

Automatic Retries

Failed requests are automatically retried with exponential backoff:

typescript
// Network errors, 5xx responses, and certain 4xx errors are retried
try {
  const data = await sdk.entitlements.list();
} catch (error) {
  // Only throws after all retry attempts are exhausted
  console.error('Request failed after retries:', error);
}

The retry logic includes:

  • Exponential backoff with jitter
  • Configurable retry attempts
  • Smart error classification (retryable vs non-retryable errors)

Error Handling

The SDK throws typed errors for different scenarios:

typescript
import { client } from '@sesamy/sdk';

const sdk = client({
  /* config */
});

try {
  const profile = await sdk.profile.get('user-id');
} catch (error) {
  if (error.status === 401) {
    // Token expired, refresh and retry
    await refreshToken();
  } else if (error.status === 403) {
    // Insufficient permissions
    console.error('Access denied');
  } else if (error.status === 404) {
    // Resource not found
    console.error('User not found');
  } else {
    // Other errors
    console.error('API error:', error.message);
  }
}

TypeScript Support

Full TypeScript support with comprehensive type definitions:

typescript
import { client, type Profile, type Entitlement } from '@sesamy/sdk';

const sdk = client({
  /* config */
});

// Fully typed responses
const profile: Profile = await sdk.profile.get('user-id');
const entitlements: Entitlement[] = await sdk.entitlements.list();

Examples

Complete User Onboarding Flow

typescript
import { client } from '@sesamy/sdk';

const sdk = client({
  baseUrl: 'https://api.sesamy.com',
  tokenProvider: () => getAuthToken(),
});

async function onboardUser(userId: string, productSku: string) {
  try {
    // Check if user has access
    const entitlements = await sdk.entitlements.list({
      sku: productSku,
      waitForEntitlementAfter: new Date().toISOString(),
    });

    if (entitlements.length > 0) {
      console.log('User already has access');
      return;
    }

    // Auto-onboard to product
    const newEntitlements = await sdk.products.autoOnboard(productSku);

    // Update user profile
    await sdk.profile.update(userId, {
      preferences: { onboarded: true },
    });

    console.log('User onboarded successfully');
  } catch (error) {
    console.error('Onboarding failed:', error);
  }
}

Subscription Management

typescript
async function manageSubscription(userId: string) {
  // Get user's subscriptions
  const subscriptions = await sdk.subscriptions.list();

  // Check for active subscriptions
  const activeSubscription = subscriptions.find((sub) => sub.status === 'active');

  if (activeSubscription) {
    // Cancel subscription
    await sdk.contracts.cancel(activeSubscription.contractId);
  }
}

User Management (Management API)

typescript
import { management } from '@sesamy/sdk';

const mgmtSdk = management({
  baseUrl: 'https://api.sesamy.com',
  tokenProvider: () => getManagementToken(),
});

async function createAndSetupUser(email: string, initialMetadata: Record<string, string>) {
  // Create user
  const user = await mgmtSdk.users.create({
    email,
    name: email.split('@')[0], // Simple name from email
  });

  // Set initial metadata
  for (const [key, value] of Object.entries(initialMetadata)) {
    await mgmtSdk.users.metadata.set(user.id, key, value);
  }

  return user;
}

Product Management (Management API)

typescript
// List all products
const products = await mgmtSdk.products.list();

// Get specific product
const product = await mgmtSdk.products.get('premium-article-sku');

// Create new product
const newProduct = await mgmtSdk.products.create({
  sku: 'new-product-sku',
  name: 'New Premium Product',
  price: { amount: 9.99, currency: 'USD' },
});

// Update product
const updatedProduct = await mgmtSdk.products.update('premium-article-sku', {
  name: 'Updated Product Name',
});

// Get product fulfillments
const fulfillments = await mgmtSdk.products.getFulfillments('premium-article-sku');

Entitlement Management (Management API)

typescript
// List entitlements for a specific user
const userEntitlements = await mgmtSdk.entitlements.list({
  userId: 'user-123',
  type: 'article',
});

// List entitlements by product SKU
const productEntitlements = await mgmtSdk.entitlements.list({
  sku: 'premium-article-sku',
});

Contract Management (Management API)

typescript
// List all contracts
const contracts = await mgmtSdk.contracts.list();

// Get specific contract
const contract = await mgmtSdk.contracts.get('contract-123');

Billing and Transaction Management (Management API)

typescript
// List all bills
const bills = await mgmtSdk.bills.list();

// Get specific bill
const bill = await mgmtSdk.bills.get('bill-123');

// List transactions
const transactions = await mgmtSdk.transactions.list();

Vendor Management (Management API)

typescript
// List all vendors
const vendors = await mgmtSdk.vendors.list();

// Create new vendor
const newVendor = await mgmtSdk.vendors.create({
  name: 'New Publisher',
  contactEmail: 'contact@publisher.com',
});

Access Lists and Grants Management (Management API)

Access Lists are sources of purchases/grants that allow you to manage bulk user entitlements:

typescript
// List all access lists
const accessLists = await mgmtSdk.accessLists.list();

// Create a new access list
const newAccessList = await mgmtSdk.accessLists.create({
  name: 'B2B Integration',
  description: 'Bulk purchases from partner system',
  type: 'BASIC_API',
  status: 'ACTIVE',
});

// Update an access list
const updated = await mgmtSdk.accessLists.update('access_list_123', {
  name: 'Updated B2B Integration',
  excludeFromStats: false,
});

// Get specific access list
const accessList = await mgmtSdk.accessLists.get('access_list_123');

// Manage grants (purchases) under an access list
const grants = await mgmtSdk.accessLists.grants.list('access_list_123');

// Create a new grant for a user
const grant = await mgmtSdk.accessLists.grants.create('access_list_123', {
  email: 'user@example.com',
  sku: 'premium-article-123',
  poId: 'po_456',
  status: 'ACTIVE',
});

// Update a grant
const updatedGrant = await mgmtSdk.accessLists.grants.update('access_list_123', 'grant_456', {
  status: 'INACTIVE',
});

// Delete a grant
await mgmtSdk.accessLists.grants.delete('access_list_123', 'grant_456');

Customer Information (Management API)

typescript
// Get customer information in XML format (for external systems)
const customerXml = await mgmtSdk.customers.get('customer-123');
console.log(customerXml); // XML string

API Reference

For complete API documentation, see the API Reference.

Migration from Direct API Calls

If you're currently making direct HTTP requests to Sesamy APIs:

typescript
// Before: Direct API calls
const response = await fetch('/api/v1/profile', {
  headers: { Authorization: `Bearer ${token}` },
});
const profile = await response.json();

// After: Using SDK
const profile = await sdk.profile.get();

Next Steps

Released under the MIT License.