Skip to content

Sesamy JS ​

The official Sesamy browser JavaScript library (@sesamy/sesamy-js) for handling authentication, analytics, content access, and communication with the Sesamy API.

Difference from Sesamy SDK

This is the browser-focused library for content access control and user experience features. For direct API access in Node.js or server environments, use the Sesamy SDK instead.

Installation ​

Easiest Setup: Scripts Host

For the simplest integration with automatic configuration and updates, use the Scripts Host service instead of manual installation.

bash
npm install @sesamy/sesamy-js
bash
yarn add @sesamy/sesamy-js
bash
pnpm add @sesamy/sesamy-js

Requirements ​

  • Modern browser with JavaScript enabled
  • TypeScript 4.5+ (for TypeScript projects)

Quick Start ​

Recommended: Scripts Host

For production use, we recommend using the Scripts Host for automatic configuration and CDN delivery. The examples below show manual integration.

Script Tag Initialization ​

The simplest way to use the SDK is via a script tag with JSON configuration:

html
<!DOCTYPE html>
<html>
  <head>
    <script src="https://cdn.sesamy.com/sesamy-js/latest/sesamy-js.min.js"></script>
    <script type="application/json" id="sesamy-js">
      {
        "clientId": "your-client-id"
      }
    </script>
  </head>
  <body>
    <script>
      // Wait for the SDK to be ready
      window.addEventListener('sesamyJsReady', async () => {
        // Check if user is authenticated
        const isAuth = await window.sesamy.auth.isAuthenticated();
        console.log('User authenticated:', isAuth);

        // Get user profile
        if (isAuth) {
          const profile = await window.sesamy.profile.get();
          console.log('User profile:', profile);
        }
      });
    </script>
  </body>
</html>

Programmatic Initialization ​

You can also initialize the SDK programmatically:

typescript
import { init } from '@sesamy/sesamy-js';
import { createAuth0Plugin } from '@sesamy/sesamy-js/auth0-plugin';

const sesamy = await init(
  { clientId: 'your-client-id' },
  { authPlugin: createAuth0Plugin() }, // omit for BFF / cookie-based auth
);

// Check authentication
const isAuth = await sesamy.auth.isAuthenticated();

// Get user profile
if (isAuth) {
  const profile = await sesamy.profile.get();
}

Configuration ​

Basic Configuration ​

The only required configuration is the clientId:

javascript
{
  "clientId": "your-client-id"
}

Advanced Configuration ​

javascript
{
  clientId: "your-client-id",
  organization: "org_123",  // Optional: Auth0 organization ID

  // API configuration
  api: {
    namespace: "sesamy",  // Window object namespace (default: "sesamy")
    // Accepts an absolute URL or a relative path on the current origin:
    //   "https://api2.sesamy.com"   – direct / custom domain
    //   "/api"                      – proxied through your own backend
    endpoint: "https://api2.sesamy.com"
  },

  // Analytics configuration
  analytics: {
    enabled: true,
    endpoint: "https://logs.sesamy.com/events"
  },

  // Error reporting
  errors: {
    enabled: true  // Report sesamy-js failures to Sesamy (default: true)
  },

  // Authentication configuration
  auth: {
    clientId: "your-client-id",
    organization: "org_123",
    enabled: true,
    domain: "auth.example.com",  // Optional: Custom Auth0 domain
    domains: []  // Optional: Array of auth domains for multi-domain setups
  },

  // Content configuration
  content: [
    {
      type: "article",
      path: "/articles",  // Optional: URL path filter
      queryParam: { key: "preview", value: "true" },  // Optional: Query parameter filter
      headers: { name: "User-Agent", contains: "mobile" },  // Optional: Header filter (case-insensitive)
      pass: "premium",    // Optional: Pass requirement
      price: {
        amount: 9.99,
        currency: "USD"
      },
      paywallUrl: "https://example.com/paywall",
      enablePaywallSettingsUrlFallback: false,  // Optional: Fallback to <sesamy-paywall settings-url>
      selectors: {
        article: { selector: "article" },
        title: { selector: "h1", attribute: "textContent" },
        image: { selector: "img", attribute: "src" },
        // ... other selectors
      }
    }
  ],

  // Transform configuration
  transforms: {
    enabled: false,
    rules: []
  }
}

Error Reporting ​

sesamy-js reports its own failures to Sesamy, so problems on your site can be found before readers report them. It is on by default.

It reports:

  • API requests that never complete, such as a blocked request or a failed CORS preflight. Browsers only expose these to JavaScript as a generic network error, so the report records whether the request was cross-origin.
  • API responses with a 5xx status, after retries. 4xx responses are normal answers and are not reported.
  • Failures while initialising authentication or the other services. A failed authentication init is retried after 2 and 10 seconds, and each retry and its outcome is reported.
  • Sessions that could not be read: a failed silent token refresh (with the recovery sesamy-js took, the error code and whether the reader appeared signed in), a cached session cleared during the startup session check, the result of a silent prompt=none redirect, and entitlement lookups refused because no access token could be obtained.
  • Uncaught errors and unhandled promise rejections thrown from sesamy-js. Errors from other scripts on your page are ignored.
  • Failures reported by components through errors.report().

Reports are sent in small batches to https://logs.sesamy.com/errors, or to https://logs.sesamy.dev/errors when environment is set to 'dev'. Query strings and fragments are removed from URLs, and tokens are redacted from error messages before anything leaves the browser. A single page load sends at most 20 reports. Each report carries the session ID, and the persistent anonymous ID only when statistics consent allows it.

To turn error reporting off:

javascript
{
  errors: {
    enabled: false
  }
}

Multi-Domain Authentication ​

For applications across multiple domains (white-label, multi-region):

javascript
{
  auth: {
    clientId: "your-client-id",
    domains: [
      "auth.brand1.com",
      "auth.brand2.com",
      "auth.example.co.uk"
    ],
    domain: "default-auth.example.com"  // Fallback domain
  }
}

The SDK automatically selects the appropriate auth domain based on the current page's top-level domain.

For publishers deploying on a custom domain, sesamy-js supports the Token Handler pattern where the Sesamy API proxy manages the OAuth flow and stores tokens in HttpOnly cookies. JavaScript never sees a raw access token.

When to use this

Use useHttpCookies: true when your site is served from a custom domain behind the Sesamy API proxy (configured via Scripts Host). It is the recommended option for paywalls and premium content sites.

How auth mode is selected ​

sesamy-js chooses its auth plugin at startup:

  1. If authPlugin is passed to init() as the second argument → use it (explicit and preferred).
  2. If auth.useHttpCookies: true is set → use the cookie-based BFF plugin regardless of what else is present.
  3. If the Auth0 plugin IIFE was loaded as a <script> before sesamy-js → detected automatically via the window.auth0Plugin global.
  4. Otherwise → fall back to the cookie-based BFF plugin.

Setting auth.useHttpCookies: true is the explicit, unambiguous way to opt in. It makes the cookie plugin redirect login() to /auth/{vendorId}/login (and logout() to /auth/{vendorId}/logout) instead of opening the Auth0 Universal Login popup/redirect.

The api.endpoint field controls where sesamy-js sends data API requests (/entitlements, /contracts, etc.) and accepts either a relative path or a full URL. The /auth/* routes are independently controlled by auth.baseUrl — same-origin by default, or prefixed when running behind a first-party proxy that namespaces auth under a sub-path (see Sub-path namespace below):

Relative path (proxy on same origin) ​

Set api.endpoint to a path such as "/api" when your backend proxies Sesamy API routes under that prefix:

javascript
{
  "clientId": "your-vendor-id",
  "vendorId": "your-vendor-id",
  "auth": {
    "useHttpCookies": true
  },
  "api": {
    "endpoint": "/api"
  }
}

All API calls (/api/entitlements, /api/contracts, etc.) are made to the same origin as the page, so HttpOnly cookies are sent automatically.

Custom domain ​

Set api.endpoint to a fully-qualified URL when your Sesamy API proxy lives on a dedicated subdomain:

javascript
{
  "clientId": "your-vendor-id",
  "vendorId": "your-vendor-id",
  "auth": {
    "useHttpCookies": true
  },
  "api": {
    "endpoint": "https://api.yoursite.com"
  }
}

The @auth0/auth0-spa-js script is not required in either mode.

Sub-path namespace ​

Set auth.baseUrl when your reverse proxy reserves a sub-path for Sesamy auth (e.g. to avoid colliding with your own /auth/* routes). The BFF plugin prepends the value to every /auth/* path it touches — auth.baseUrl sits in front of the /auth/* routes, it does not replace them:

javascript
{
  "clientId": "your-vendor-id",
  "vendorId": "your-vendor-id",
  "auth": {
    "useHttpCookies": true,
    "baseUrl": "/sesamy-auth"
  },
  "api": {
    "endpoint": "/sesamy-api"
  }
}

With auth.baseUrl: "/sesamy-auth", login navigates to /sesamy-auth/auth/{vendorId}/login, logout to /sesamy-auth/auth/{vendorId}/logout, and the userinfo check fetches /sesamy-auth/auth/userinfo. auth.baseUrl is independent of api.endpoint — the two paths can sit on different sub-paths or hosts.

Leave auth.baseUrl unset (or "") for the default same-origin /auth/* behaviour. See the Proxy Setup page for full reverse-proxy examples.

Login & Logout ​

In BFF mode the SDK's login() method navigates to /auth/{vendorId}/login (vendor ID comes from your vendorId / clientId config). The logout() method navigates to /auth/{vendorId}/logout:

javascript
// Trigger login (redirects to token.sesamy.com via /auth/<vendorId>/login, then back)
await window.sesamy.auth.login();

// Trigger logout
await window.sesamy.auth.logout();

Auth Status ​

The SDK automatically checks auth status on load by calling /auth/userinfo. You can also check it manually:

javascript
const isAuth = await window.sesamy.auth.isAuthenticated();

Local Development with Vite ​

When developing locally, you need a reverse proxy so browser cookies are scoped to localhost. Configure Vite to forward /auth and API routes to the Sesamy API proxy:

typescript
// vite.config.ts
import { defineConfig } from 'vite';

const API_TARGET = process.env.VITE_API_TARGET ?? 'https://api2.sesamy.dev';
const FORWARDED_HOST = process.env.VITE_FORWARDED_HOST ?? 'localhost:5173';

function apiProxy() {
  return {
    target: API_TARGET,
    changeOrigin: true,
    headers: {
      // Tells the Worker which hostname to use for cookie scoping
      // and token-handler lookup. Must match the entry in your
      // token-handler config (auth_hostname).
      'x-forwarded-host': FORWARDED_HOST,
    },
  };
}

export default defineConfig({
  server: {
    proxy: {
      // Forward /auth/* for the BFF token-handler flow
      '/auth': apiProxy(),
      // If using a prefix like api.endpoint = "/api", a single rule covers
      // all Sesamy API routes. Otherwise list each route individually.
      '/api':  apiProxy(),

      // Example: individual routes when not using a prefix
      // '/contracts':    apiProxy(),
      // '/entitlements': apiProxy(),
      // '/products':     apiProxy(),
      // '/profile':      apiProxy(),
    },
  },
});

Using a local API Worker instead

To run the API Worker locally (e.g. wrangler dev --remote on port 8787) instead of forwarding to the deployed dev API:

bash
VITE_API_TARGET=http://localhost:8787 pnpm dev

Because login and callback URLs now include the vendor ID in the path (/auth/<vendorId>/login), cookie scoping no longer depends on x-forwarded-host. You can navigate directly to http://localhost:8787/auth/<vendorId>/login and the full BFF flow will work. Remember to add http://localhost:8787/auth/<vendorId>/callback to the Allowed Callback URLs in your Auth0 application settings.

Deployed API target and localhost cookies

When VITE_API_TARGET points to the deployed api2.sesamy.dev Worker (the default), cookies set by that Worker will be scoped to .sesamy.dev — not localhost — and won't be sent back by your browser. For a fully working BFF flow in local development, use a local wrangler dev instance (VITE_API_TARGET=http://localhost:8787) or navigate directly to http://localhost:8787/auth/<vendorId>/login without the Vite proxy.

Core API ​

Safely Accessing the API ​

When building components or modules that need to access Sesamy JS, use this helper function to safely wait for the SDK to be ready:

typescript
export async function getApi(): Promise<SesamyAPI> {
  // If already ready, return immediately
  if (window.sesamy?.isReady()) {
    return window.sesamy;
  }

  // Wait for the ready event with timeout
  return new Promise((resolve, reject) => {
    const timeout = setTimeout(() => {
      window.removeEventListener('sesamyJsReady', onSesamyJsReady);
      if (window.sesamy) {
        resolve(window.sesamy);
      } else {
        reject(new Error('sesamyJsReady event did not occur within the expected time.'));
      }
    }, 5000);

    function onSesamyJsReady() {
      if (!window.sesamy) {
        reject(new Error('Sesamy API is not available'));
      } else {
        clearTimeout(timeout);
        window.removeEventListener('sesamyJsReady', onSesamyJsReady);
        resolve(window.sesamy);
      }
    }

    window.addEventListener('sesamyJsReady', onSesamyJsReady);
  });
}

Usage:

typescript
// In your component or module
async function initComponent() {
  try {
    const sesamy = await getApi();

    // Now safely use the API
    const isAuth = await sesamy.auth.isAuthenticated();
    if (isAuth) {
      const profile = await sesamy.profile.get();
      console.log('User profile:', profile);
    }
  } catch (error) {
    console.error('Failed to initialize Sesamy:', error);
  }
}

initComponent();

This approach:

  • Returns immediately if the SDK is already ready
  • Waits for the sesamyJsReady event if not ready yet
  • Includes a 5-second timeout to prevent hanging
  • Properly cleans up event listeners

Checking SDK Readiness ​

For simple synchronous checks:

javascript
// Check if SDK is ready
if (window.sesamy?.isReady()) {
  console.log('SDK is ready');
}

// Or listen for the ready event
window.addEventListener('sesamyJsReady', () => {
  console.log('SDK is ready');
});

API Documentation ​

Sesamy JS provides a comprehensive set of APIs organized by functionality:

Authentication ​

User login, logout, session management, and profile operations. Authentication is separate because it's fundamental to all other operations.

Content ​

Content discovery, access control, paywalls, and content unlocking. This is a distinct browser-specific feature for managing locked content.

API Reference ​

Complete API reference for all SDK wrapper methods:

  • Entitlements - Check and manage user access to content
  • Subscriptions (Contracts) - Manage user subscriptions
  • Checkouts - Create and manage purchase flows
  • Products - Retrieve product information
  • Bills & Transactions - Access billing history
  • Attribution - Manage tracking attribution

These are grouped together as they're all SDK wrapper methods providing similar functionality.

Analytics ​

Event tracking and analytics. Separate because it's an optional feature that can be enabled/disabled independently.

TypeScript Support ​

The library includes comprehensive TypeScript definitions:

typescript
import { init, SesamyAPI } from '@sesamy/sesamy-js';

const sesamy: SesamyAPI = await init({
  clientId: 'your-client-id',
});

// Full type safety
const profile: Profile | null = await sesamy.profile.get();
const entitlements: Entitlement[] = await sesamy.entitlements.list();

Next Steps ​

Released under the MIT License.