Skip to content

Content Management

Content management allows you to discover, analyze, and control access to content on your pages. The SDK automatically finds content based on configured selectors and provides methods to check access and unlock premium content.

📘 See Also: Content Metadata & Fallback Mechanism for detailed information about meta tags, element attributes, and best practices.

How It Works

  1. Configuration - Define content selectors in your config or via Scripts Host
  2. Discovery - SDK scans the page for matching content elements
  3. Metadata Extraction - Extracts metadata using a multi-layered fallback approach (configured selectors → element attributes → meta tags)
  4. Access Control - Integrates with entitlements to control access
  5. Content Unlocking - Fetches and decrypts premium content when authorized

Methods

content.list()

Discover all content items on the current page.

Returns: ContentItem[]

javascript
const articles = window.sesamy.content.list();

articles.forEach((article) => {
  console.log('Found:', article.title);
  console.log('Price:', article.price);
  console.log('Requires:', article.pass);
});

content.get()

Get a specific content item by selector or element.

Parameters: elementOrSelector (HTMLElement | string)

Returns: ContentItem | null

javascript
const article = window.sesamy.content.get('article.featured');
if (article) {
  console.log('Article:', article.title);
}

content.hasAccess()

Check if user has access to content.

Parameters: elementOrSelector (HTMLElement | string)

Returns: Promise<Entitlement | boolean | null>

javascript
const result = await window.sesamy.content.hasAccess('.premium-article');

if (result) {
  if (typeof result === 'boolean') {
    // User has access (public or logged-in content)
    console.log('Access granted');
  } else {
    // User has access via entitlement
    console.log('Access granted via:', result.type);
  }
} else {
  // User doesn't have access - show paywall
  const article = window.sesamy.content.get('.premium-article');
  showPaywall(article.paywallUrl);
}

The function returns:

  • An Entitlement object if the user has entitlement-based access
  • true if the user has access (public content or authenticated for login-only content)
  • null if the user doesn't have access

content.unlock()

Unlock and retrieve premium content.

Parameters: elementOrSelector (HTMLElement | string)

Returns: Promise<string | null>

javascript
const unlockedHTML = await window.sesamy.content.unlock('#article');

if (unlockedHTML) {
  document.querySelector('#content').innerHTML = unlockedHTML;
} else {
  // User doesn't have access
  showPaywall();
}

Configuration

Configure content selectors to define how the SDK discovers and extracts metadata:

javascript
{
  content: [
    {
      type: 'article',
      path: '/articles', // Optional: only match on this path
      queryParam: { key: 'param', value: 'value' }, // Optional: match on query parameter
      headers: { name: 'User-Agent', contains: 'app-name' }, // Optional: match on header (substring, case-insensitive)
      pass: 'premium', // Optional: required pass
      price: {
        amount: 9.99,
        currency: 'USD',
      },
      enablePaywallSettingsUrlFallback: false, // Optional: Fallback to <sesamy-paywall settings-url>
      selectors: {
        article: { selector: 'article.content' },
        title: { selector: 'h1', attribute: 'textContent' },
        image: { selector: 'img', attribute: 'src' },
        author: { selector: '.author', attribute: 'textContent' },
        paywallUrl: { selector: 'a.paywall-link', attribute: 'href' },
        // ... more selectors
      },
    },
  ];
}

Content Matching

The SDK supports multiple ways to match content configurations to specific pages:

Path Matching

Match when the URL path contains a specific string:

javascript
{
  type: 'article',
  path: '/premium', // Matches /premium, /premium/article, etc.
  selectors: { /* ... */ }
}

Query Parameter Matching

Match when a specific query parameter has an exact value:

javascript
{
  type: 'article',
  queryParam: { key: 'preview', value: 'true' }, // Matches ?preview=true
  selectors: { /* ... */ }
}

Header Matching

Match when a header contains a specific value (case-insensitive substring match):

javascript
{
  type: 'article',
  headers: { name: 'User-Agent', contains: 'mobile-app' }, // Matches User-Agent containing "mobile-app"
  selectors: { /* ... */ }
}

Browser Limitation

In browser environments, only the User-Agent header is accessible via navigator.userAgent. Other headers will not match in client-side code.

Multiple Conditions

All conditions specified on a content node must match. Place more specific configurations first:

javascript
content: [
  {
    type: 'article',
    path: '/articles',
    headers: { name: 'User-Agent', contains: 'mobile-app' },
    selectors: {
      /* mobile app specific selectors */
    },
  },
  {
    type: 'article',
    path: '/articles',
    selectors: {
      /* default article selectors */
    },
  },
];

Priority Order

Content nodes are matched in order. The first node where all conditions match is used:

  1. Check path condition (if specified)
  2. Check queryParam condition (if specified)
  3. Check headers condition (if specified)
  4. If all conditions pass (or no conditions specified), use this node

Best Practice

Place content nodes with more specific conditions (multiple filters) at the top of the array, and generic/fallback configurations at the bottom.

Pass Collection

The pass attribute is collected from the article element and all its ancestor elements up to the document root. This allows for flexible, hierarchical access control:

html
<body pass="https://example.com/site-wide">
  <main pass="https://example.com/members">
    <section pass="https://example.com/premium">
      <sesamy-article pass="https://example.com/article-123">
        <!-- All four passes are collected and checked -->
      </sesamy-article>
    </section>
  </main>
</body>

How It Works

When retrieving content with content.get() or content.list(), the SDK:

  1. Starts at the article element
  2. Collects any pass attributes
  3. Traverses up through parent elements
  4. Collects all pass attributes from ancestors
  5. Splits comma-separated values
  6. Removes duplicates
  7. Combines into a single comma-separated string

Use Cases

Section-based access:

html
<section class="members-only" pass="https://example.com/members">
  <sesamy-article>Article 1</sesamy-article>
  <sesamy-article>Article 2</sesamy-article>
  <!-- Both articles inherit the members pass -->
</section>

Dynamic passes from CMS:

html
<!-- Lab plugin adds passes dynamically based on tags -->
<section class="article" pass="https://example.com/tag1,https://example.com/tag2">
  <sesamy-article>
    <!-- Inherits tag-based passes -->
  </sesamy-article>
</section>

Multiple access levels:

html
<article pass="https://example.com/basic">
  <div pass="https://example.com/premium">
    <sesamy-article>
      <!-- Requires either basic OR premium pass -->
    </sesamy-article>
  </div>
</article>

Best Practices

  • Place site-wide passes on <body> or high-level containers
  • Use section/category passes on section elements
  • Use article-specific passes on <sesamy-article> elements
  • Let plugins like Labrador Tags handle dynamic passes

Common Patterns

Content List with Access Check

javascript
window.addEventListener('sesamyJsReady', async () => {
  const articles = window.sesamy.content.list();

  for (const article of articles) {
    const entitlement = await window.sesamy.content.hasAccess(article.element);

    if (!entitlement) {
      // Add paywall overlay
      const overlay = document.createElement('div');
      overlay.className = 'paywall-overlay';
      overlay.innerHTML = `
        <h3>Premium Content</h3>
        <p>Subscribe to read this article</p>
        <button onclick="location.href='${article.paywallUrl}'">
          Subscribe Now
        </button>
      `;
      article.element.appendChild(overlay);
    }
  }
});

Dynamic Content Unlocking

javascript
async function unlockArticle(selector) {
  const entitlement = await window.sesamy.content.hasAccess(selector);

  if (entitlement) {
    const content = await window.sesamy.content.unlock(selector);
    if (content) {
      document.querySelector('.article-body').innerHTML = content;
      document.querySelector('.paywall').remove();

      // Track unlock event
      await window.sesamy.analytics.track('content_unlocked', {
        contentId: entitlement.publisherContentId,
      });
    }
  } else {
    // Show paywall with article info
    const article = window.sesamy.content.get(selector);
    showPaywall({
      title: article.title,
      price: article.price,
      paywallUrl: article.paywallUrl,
    });
  }
}

Paywall with Purchase Flow

javascript
window.addEventListener('sesamyJsReady', async () => {
  const article = window.sesamy.content.get('article.main');
  const access = await window.sesamy.content.hasAccess(article.element);

  if (!access) {
    document.querySelector('#purchase-btn').addEventListener('click', async () => {
      const checkout = await window.sesamy.checkouts.create({
        items: [
          {
            url: article.url,
            price: article.price.amount,
            currency: article.price.currency,
          },
        ],
        redirectUrl: window.location.href,
      });

      window.location.href = checkout.checkoutUrl;
    });
  }
});

Next Steps

Modals & Notification Bars

The SDK provides helpers for displaying modal dialogs and notification bars. These are useful for showing custom UI like forms, alerts, or banners—typically containing web components.

content.showModal()

Display a centered modal overlay with custom content.

Parameters:

  • content (string | Element) - HTML string or DOM element to display
  • options (ModalOptions) - Optional configuration

Returns: ModalResult with element and close() function

javascript
// Show a modal with an HTML string
const { close } = window.sesamy.content.showModal(`
  <my-custom-form></my-custom-form>
`);

// Show a modal with a DOM element
const form = document.createElement('my-signup-form');
const modal = window.sesamy.content.showModal(form);

// Close programmatically
modal.close();

Options:

OptionTypeDefaultDescription
closeOnBackdropClickbooleantrueClose when clicking outside the content
closeOnEscapebooleantrueClose when pressing Escape key
zIndexnumber2147483647CSS z-index for the overlay
backdropBlurstring'5px'Blur amount for backdrop filter
overlayBackgroundstring'rgba(0,0,0,0.5)'Background color of overlay
javascript
// Modal that requires explicit close action
const { close } = window.sesamy.content.showModal(formElement, {
  closeOnBackdropClick: false,
  closeOnEscape: false
});

content.showNotificationBar()

Display a notification bar at the top or bottom of the viewport.

Parameters:

  • content (string | Element) - HTML string or DOM element to display
  • options (NotificationBarOptions) - Optional configuration

Returns: NotificationBarResult with element and close() function

javascript
// Show a simple notification at the top
const { close } = window.sesamy.content.showNotificationBar(`
  <my-announcement-banner></my-announcement-banner>
`);

// Show at bottom
window.sesamy.content.showNotificationBar(bannerElement, {
  position: 'bottom'
});

Options:

OptionTypeDefaultDescription
position'top' | 'bottom''top'Position of the bar
fixedbooleantrueWhether bar stays fixed on scroll
zIndexnumber2147483647CSS z-index
autoDismissMsnumber-Auto-close after milliseconds
pushContentbooleanfalsePush page content to make room
heightstring-Bar height (required when pushContent is true)
transitionDurationstring'0.3s'Animation duration
javascript
// Push page content down with animation
window.sesamy.content.showNotificationBar(`
  <div style="background:#28a745;color:white;padding:12px;text-align:center;">
    🎉 Welcome! You have a new subscription.
  </div>
`, {
  pushContent: true,
  height: '50px'
});

// Auto-dismiss after 5 seconds
window.sesamy.content.showNotificationBar(alertElement, {
  autoDismissMs: 5000,
  pushContent: true,
  height: '40px'
});

Use Cases

Custom signup form modal:

javascript
window.sesamy.content.showModal(`
  <sesamy-signup-form 
    on-success="this.closest('[data-sesamy-modal]').remove()">
  </sesamy-signup-form>
`);

Cookie consent banner:

javascript
const { close } = window.sesamy.content.showNotificationBar(`
  <div style="background:#333;color:white;padding:16px;display:flex;justify-content:center;gap:16px;align-items:center;">
    <span>We use cookies to improve your experience.</span>
    <button onclick="acceptCookies()">Accept</button>
  </div>
`, {
  position: 'bottom',
  pushContent: true,
  height: '60px'
});

function acceptCookies() {
  localStorage.setItem('cookies-accepted', 'true');
  close();
}

Subscription success notification:

javascript
window.addEventListener('sesamyPurchaseComplete', () => {
  window.sesamy.content.showNotificationBar(`
    <div style="background:#28a745;color:white;padding:12px;text-align:center;">
      ✅ Thank you for subscribing! Your content is now unlocked.
    </div>
  `, {
    pushContent: true,
    height: '50px',
    autoDismissMs: 5000
  });
});

Released under the MIT License.