npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@safepassage/sdk

v3.5.6

Published

SafePassage SDK - Lightweight redirect-based age verification

Readme

SafePassage SDK v3.5.6

A lightweight SDK for integrating SafePassage age verification into your website or application.

Features

  • Ultra-lightweight: ~18KB minified
  • Simple integration: 5 lines of code to get started
  • Two modes: Same-tab redirect or new-tab popup
  • TypeScript support: Full type definitions included
  • Auto-environment detection: Works seamlessly across environments
  • Server-issued sessions: launches from the authenticated URL returned by the API
  • Compliant: Enforces minimum age of 25

Changelog

3.5.6

  • Removed browser HMAC state. Browser callbacks remain convenience notifications, not proof of verification.
  • Launches now require the server-issued verifyUrl; API and Verify UI origins are restricted to the selected brand/environment (localhost overrides remain available for development).
  • Browser SDK verification-policy overrides are retired. Configure policy in the dashboard or create sessions server-side with a private key.

3.5.5

  • Routes explicit staging sessions through the canonical VerityGuard-owned staging hosts for the selected SDK brand.
  • Keeps production routing unchanged.

3.5.4

  • Restored dist/safepassage.min.js as a compatibility copy of the canonical sdk.min.js bundle.
  • Kept the 3.5.3 production-default routing fix unchanged.
  • No public SDK API changes.

3.5.3

  • Fixed environment routing so customer-owned hostnames always default to production infrastructure, even when the hostname contains staging/stage/qa/etc. Internal staging is selected only via an explicit environment: 'staging' or a first-party Verity staging host.
  • No public SDK API changes.

3.5.2

  • Version alignment release across both brand packages at 3.5.2
  • No SDK API changes from 3.5.0

3.5.0

  • Added language option for UI localization (en, de, es, fr, pt, it)
  • Per-verification language override via verify({ language: 'es' })

3.4.11

  • Fixed CDN documentation URLs (files are at package root, not /dist/)

3.4.9

  • Prevents onCancel from firing after a successful new-tab verification when the popup closes.

Installation

npm install @safepassage/[email protected]

Or load directly from jsDelivr CDN (no bundler required):

<script src="https://cdn.jsdelivr.net/npm/@safepassage/[email protected]/sdk.min.js"></script>

Quick Start

With npm/bundler

import { SafePassage } from '@safepassage/sdk';

const sp = new SafePassage({
  apiKey: 'pk_...',  // Your public key from the dashboard
  returnUrl: window.location.origin + '/verified'
});

// Start verification - redirects user to SafePassage
await sp.verify();

With CDN (no bundler)

<script src="https://cdn.jsdelivr.net/npm/@safepassage/[email protected]/sdk.min.js"></script>
<script>
  const sp = new SafePassage({
    apiKey: 'pk_...',
    returnUrl: window.location.origin + '/verified'
  });

  document.getElementById('verify-btn').onclick = () => sp.verify();
</script>

That's it! The SDK handles session creation automatically.

Configuration

| Option | Type | Required | Description | |--------|------|----------|-------------| | apiKey | string | Yes | Your public API key (pk_...) | | returnUrl | string | Yes | URL to redirect after verification | | cancelUrl | string | No | URL to redirect to if user closes the verification window (new-tab mode) | | environment | string | No | 'production' or 'staging' (auto-detected) | | mode | string | No | 'redirect' (default) or 'new-tab' | | onComplete | function | No | Callback for new-tab mode | | onCancel | function | No | Called when user closes popup (new-tab mode). Return false to suppress automatic cancelUrl redirect. | | language | string | No | Default UI language ('en', 'de', 'es', 'fr', 'pt', 'it'). Can be overridden per verification. | | onError | function | No | Error handler |

Verification Options

Override settings per-verification:

await sp.verify({
  externalUserId: 'user-123', // Your user ID (returned in webhooks)
  skipIntro: true,            // Skip intro screen
  autoReturn: true,           // Auto-redirect after success
  language: 'es'              // Override UI language for this session
});

Verification Modes

  • L1: Age estimation using computer vision (faster, less friction)
  • L2: Full ID document verification (more thorough)

Integration Modes

Same-Tab Redirect (Default)

User is redirected to SafePassage, then back to your returnUrl:

const sp = new SafePassage({
  apiKey: 'pk_...',
  returnUrl: '/age-verified'
});

await sp.verify();
// User is redirected to SafePassage...
// After verification, user returns to /age-verified?sessionId=xxx&status=verified

New-Tab Mode

Verification opens in a popup window:

const sp = new SafePassage({
  apiKey: 'pk_...',
  returnUrl: '/age-verified',
  cancelUrl: '/age-cancelled',
  mode: 'new-tab',
  onComplete: (result) => {
    console.log('Verification complete:', result.sessionId, result.status);
    // Convenience notification only: validate on your server before access
  },
  onCancel: () => {
    console.log('User closed the verification window');
    // Return false if you want to handle navigation manually
    // return false;
  },
  onError: (error) => {
    console.error('Verification error:', error.message);
  }
});

await sp.verify();

If cancelUrl is provided, the SDK will redirect the opener to cancelUrl with status=cancelled and sessionId when the user closes the verification window. Return false from onCancel to suppress the automatic redirect.

Server-Side Validation (Required!)

After verification completes, always validate the result on your server before granting access:

// Node.js / Express example
app.get('/age-verified', async (req, res) => {
  const { sessionId } = req.query;

  // Validate with your SECRET key (sk_...)
  const response = await fetch(
    'https://api.safepassageapp.com/api/v1/sessions/validate',
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-API-Key': process.env.SAFEPASSAGE_SECRET_KEY
      },
      body: JSON.stringify({ sessionId })
    }
  );

  const session = await response.json();

  if (session.verified && session.accessGranted) {
    // Grant access
    req.session.ageVerified = true;
    res.redirect('/content');
  } else {
    res.redirect('/age-verification-failed');
  }
});

Security Note: Never trust client-side verification status alone. Always validate server-side using your secret key.

Webhooks (Recommended)

For reliable verification tracking, configure webhooks in your dashboard:

// Webhook payload example
{
  "event": "verification.completed",
  "sessionId": "abc-123",
  "verified": true,
  "externalUserId": "your-user-id",  // If provided during verify()
  "timestamp": "2025-01-15T10:30:00Z"
}

Complete Example

HTML + CDN

<!DOCTYPE html>
<html>
<head>
  <title>Age Verification</title>
  <script src="https://cdn.jsdelivr.net/npm/@safepassage/[email protected]/sdk.min.js"></script>
</head>
<body>
  <button id="verify-btn">Verify Your Age</button>

  <script>
    const sp = new SafePassage({
      apiKey: 'pk_...',
      returnUrl: window.location.origin + '/verified'
    });

    document.getElementById('verify-btn').onclick = () => sp.verify();
  </script>
</body>
</html>

React Component

import { useState } from 'react';
import { SafePassage } from '@safepassage/sdk';

function AgeGate() {
  const [verifying, setVerifying] = useState(false);

  const sp = new SafePassage({
    apiKey: process.env.NEXT_PUBLIC_SAFEPASSAGE_KEY,
    returnUrl: window.location.origin + '/verified'
  });

  const handleVerify = async () => {
    setVerifying(true);
    try {
      await sp.verify();
    } catch (error) {
      console.error('Failed to start verification:', error);
      setVerifying(false);
    }
  };

  return (
    <button onClick={handleVerify} disabled={verifying}>
      {verifying ? 'Redirecting...' : 'Verify Your Age'}
    </button>
  );
}

TypeScript

Full TypeScript support included:

import { SafePassage, SafePassageConfig, VerificationResult } from '@safepassage/sdk';

const config: SafePassageConfig = {
  apiKey: process.env.NEXT_PUBLIC_SAFEPASSAGE_KEY!,
  returnUrl: '/verified',
  mode: 'new-tab',
  onComplete: (result: VerificationResult) => {
    // Convenience notification only; request server-side validation.
    console.log(`Session ${result.sessionId}: ${result.status}`);
  }
};

const sp = new SafePassage(config);

Skip Parameters

For streamlined embedded flows:

// Skip intro screen (go directly to camera)
await sp.verify({ skipIntro: true });

// Auto-redirect after success (no success screen)
await sp.verify({ autoReturn: true });

// Both - minimal user interaction
await sp.verify({ skipIntro: true, autoReturn: true });

Error Handling

const sp = new SafePassage({
  apiKey: 'pk_...',
  returnUrl: '/verified',
  onError: (error) => {
    if (error.message.includes('popup')) {
      alert('Please allow popups for age verification');
    } else if (error.message.includes('rate limit')) {
      alert('Too many attempts. Please wait a moment.');
    } else {
      console.error('Verification error:', error);
    }
  }
});

API Keys

SafePassage uses two types of API keys:

| Key Type | Prefix | Use Case | |----------|--------|----------| | Public Key | pk_ | Client-side SDK (this package) | | Secret Key | sk_ | Server-side validation only |

Important: This SDK only works with public keys (pk_). For server-side integrations using secret keys, use the Direct API instead.

Browser Support

  • Chrome 60+
  • Firefox 60+
  • Safari 12+
  • Edge 79+
  • Mobile browsers (iOS Safari, Chrome for Android)

Security Best Practices

  1. Use public keys client-side - Never expose secret keys in browser code
  2. Validate server-side - Always verify results using your secret key
  3. Configure webhooks - For reliable, tamper-proof verification notifications
  4. Register callback URLs - Pre-register your returnUrl in the dashboard
  5. Use HTTPS - SDK enforces HTTPS in production

Cleanup

When done with the SDK (e.g., in SPA route changes):

sp.destroy();

Migration from v3.0.x

If you were using merchant-generated session IDs:

// Old (v3.0.x)
sp.verify({ sessionId: crypto.randomUUID() });

// New (v3.4+) - sessionId is auto-generated
await sp.verify();

The SDK now creates sessions automatically via the API when using public keys.

Support