The official JavaScript SDK for CrowdHandler waiting room and queue management. Works in both Node.js and browser environments.
- π Easy Integration - Add queue management to any JavaScript application with a single function call
- π Flexible Deployment - Works in Node.js servers, browsers, serverless functions, and CDN edge locations
- β‘ Performance Options - Choose between real-time API validation or local signature validation based on your needs
- π Queue Continuity - Maintains user position across page refreshes and sessions
- π TypeScript Support - Full type definitions for better development experience
- π§ API Access - Manage waiting rooms, monitor queues, and access analytics programmatically
npm install crowdhandler-sdk<!-- Load from unpkg -->
<script src="https://unpkg.com/crowdhandler-sdk/dist/crowdhandler.umd.min.js"></script>
<!-- Or specify a version -->
<script src="https://unpkg.com/[email protected]/dist/crowdhandler.umd.min.js"></script>The SDK is available in multiple formats:
- ES Modules -
import { init } from 'crowdhandler-sdk' - CommonJS -
const crowdhandler = require('crowdhandler-sdk') - UMD - Available as
window.crowdhandlerwhen loaded via script tag - Dynamic Import -
await import('crowdhandler-sdk')
const crowdhandler = require('crowdhandler-sdk');
// or ES modules: import { init } from 'crowdhandler-sdk';
// Initialize SDK
const { client, gatekeeper } = crowdhandler.init({
publicKey: 'YOUR_PUBLIC_KEY',
// Optional: add privateKey for private API access
privateKey: 'YOUR_PRIVATE_KEY',
request: req, // Express request object
response: res // Express response object
});
// Validate the request
const result = await gatekeeper.validateRequest();
// Check for errors first
if (result.error) {
console.error(`Validation error: ${result.error.message}`);
// 4xx errors: promoted = false (always block access)
// 5xx errors: promoted depends on trustOnFail setting
}
// Handle the validation result
if (result.setCookie) {
gatekeeper.setCookie(result.cookieValue, result.domain);
}
if (result.stripParams) {
return gatekeeper.redirectToCleanUrl(result.targetURL);
}
if (!result.promoted) {
return gatekeeper.redirectIfNotPromoted();
}
// User is promoted - continue with your application
// ... your protected content here ...
// Record performance (optional but recommended)
await gatekeeper.recordPerformance();// Using script tag
const { client, gatekeeper } = window.crowdhandler.init({
publicKey: 'YOUR_PUBLIC_KEY',
options: {
mode: 'clientside'
}
});
// Or using ES Modules
import { init } from 'crowdhandler-sdk';
const { client, gatekeeper } = init({
publicKey: 'YOUR_PUBLIC_KEY',
options: {
mode: 'clientside'
}
});
// Validate the request
const result = await gatekeeper.validateRequest();
// Handle the validation result
if (result.setCookie) {
gatekeeper.setCookie(result.cookieValue, result.domain);
}
if (result.stripParams) {
// Redirect to clean URL
window.location.href = result.targetURL;
return;
}
if (!result.promoted) {
// Redirect to waiting room
gatekeeper.redirectIfNotPromoted();
return;
}
// User is promoted - your application continues
console.log('User granted access');
// Record performance (optional but recommended)
await gatekeeper.recordPerformance();The primary method for validating requests against CrowdHandler's queue system. This method determines whether a user should be granted access to your protected resource or sent to a waiting room.
// Basic usage
const result = await gatekeeper.validateRequest();
// With custom parameters
const result = await gatekeeper.validateRequest({
custom: {
code: 'ABC123',
captcha: 'xK9mN2pQ5vL8wR3tY6uZ1aS4dF7gH0j'
}
});Parameters:
params(optional) - Object containing custom parameterscustom- Object with any key-value pairs to send to the CrowdHandler API
How it works:
- Token Check: First checks for an existing CrowdHandler session token in cookies
- API Validation: Sends the token (or generates a new one) to CrowdHandler's API, including any custom parameters
- Queue Position: Determines if the user is promoted based on current capacity
- Response: Returns instructions on how to handle the request
Return Object:
{
promoted: boolean, // true = grant access, false = send to waiting room
setCookie: boolean, // true = update the user's session cookie
cookieValue: string, // The session token to store in the cookie
stripParams: boolean, // true = remove CrowdHandler URL parameters
targetURL: string, // Where to redirect (clean URL or waiting room)
slug: string, // The waiting room slug (when not promoted)
responseID: string, // Response ID for performance tracking (when promoted)
deployment: string, // Deployment identifier from the API
token: string, // The session token
hash: string | null, // Signature hash for validation (when available)
requested: string, // Timestamp when the request was made
liteValidatorRedirect: boolean, // true = redirect to lite validator
liteValidatorUrl: string // URL for lite validator redirect
}Mode-Specific Behavior:
- Full Mode (default): Makes an API call on every request for real-time validation
- Hybrid Mode: Validates signatures locally for promoted users, reducing API calls
- Clientside Mode: Validates entirely in the browser using cookies
Error Handling:
try {
const result = await gatekeeper.validateRequest();
// ... handle result ...
} catch (error) {
console.error('Validation failed:', error.message);
console.error('Status code:', error.statusCode);
// Handle based on trustOnFail setting
}
### gatekeeper.setCookie(value, domain?)
Sets the CrowdHandler session cookie. Always call this when `result.setCookie` is true to maintain the user's queue position. The optional `domain` parameter (provided in `result.domain`) enables proper cookie scoping for wildcard domains.
```javascript
if (result.setCookie) {
gatekeeper.setCookie(result.cookieValue, result.domain);
}Removes CrowdHandler tracking parameters from URLs. Use when result.stripParams is true to keep URLs clean.
if (result.stripParams) {
return gatekeeper.redirectToCleanUrl(result.targetURL);
}Convenience method that handles the complete redirect flow for non-promoted users. Automatically manages cookies and redirects.
if (!result.promoted) {
return gatekeeper.redirectIfNotPromoted();
}Redirects promoted users from a waiting room implementation back to the target site with fresh CrowdHandler parameters. This method is specifically for use in waiting room implementations.
// In waiting room implementation
if (result.promoted) {
return gatekeeper.redirectIfPromoted();
}Use Case: When building a custom waiting room that runs on your infrastructure, this method handles the redirect back to the protected resource with proper CrowdHandler parameters.
Records performance metrics to help CrowdHandler optimize queue flow and capacity.
// Simple usage (recommended)
await gatekeeper.recordPerformance();
// With custom options
await gatekeeper.recordPerformance({
sample: 0.2, // Sample 20% of requests
factor: 100 // Custom timing factor
});Overrides the default CrowdHandler waiting room with your custom URL.
// Redirect to your custom queue page
gatekeeper.overrideWaitingRoomUrl('https://mysite.com/custom-queue');const instance = crowdhandler.init({
// Required
publicKey: 'YOUR_PUBLIC_KEY',
// Optional
privateKey: 'YOUR_PRIVATE_KEY', // Required for private API methods
// Request context (choose one based on your environment)
request: req, // Express/Node.js request
response: res, // Express/Node.js response
lambdaEdgeEvent: event, // Lambda@Edge event
// (none) // Browser environment (auto-detected)
// Options
options: {
mode: 'full', // 'full' (default), 'hybrid', 'clientside'
apiUrl: 'https://api.crowdhandler.com', // Custom API endpoint
debug: false, // Enable debug logging
timeout: 5000, // API timeout in milliseconds
trustOnFail: true, // Allow access if API fails
fallbackSlug: '', // Fallback room slug when trustOnFail is false
cookieName: 'crowdhandler', // Custom cookie name (default: 'crowdhandler')
waitingRoom: false, // Set to true if SDK is running in a waiting room context
liteValidator: false, // Enable lite validator mode (default: false)
roomsConfig: [{ // Array of room configurations for lite validator
domain: string, // e.g. 'https://example.com'
slug: string, // Room identifier
urlPattern?: string, // URL pattern to match
patternType?: 'regex' | 'contains' | 'all',
queueActivatesOn?: number, // Unix timestamp
timeout?: number // Timeout in seconds
}]
}
});Best for most server-side integrations.
- β Pros: Simple setup, no private key required, full features
- β Cons: API call on every request (20-100ms latency)
For performance-critical applications.
- β Pros: Minimal latency (2-10ms), fewer API calls
- β Cons: Requires private key, needs client-side JavaScript for auxilery functionality
const instance = crowdhandler.init({
publicKey: 'YOUR_PUBLIC_KEY',
privateKey: 'YOUR_PRIVATE_KEY', // Required for hybrid mode
options: { mode: 'hybrid' }
});For single-page applications and static sites.
- β Pros: Works without server, easy integration
- β Cons: Client-side only, requires JavaScript
By default, CrowdHandler uses crowdhandler as the cookie name. You can override this with a custom name:
const { gatekeeper } = crowdhandler.init({
publicKey: 'YOUR_PUBLIC_KEY',
options: {
cookieName: 'my-custom-queue' // Use custom cookie name
}
});This is useful when:
- Running multiple CrowdHandler instances on the same domain
- Avoiding conflicts with existing cookies
- Meeting specific naming conventions
The SDK provides a unified client for both public and private APIs:
// List all waiting rooms
const rooms = await client.rooms().get();
// Get specific room
const room = await client.rooms().get('room_id');
// Create a new room (requires privateKey)
const newRoom = await client.rooms().post({
name: 'Product Launch',
domain: 'example.com'
});
// Update room settings
await client.rooms().put('room_id', {
capacity: 1000
});
// Delete a room
await client.rooms().delete('room_id');Public API (publicKey only):
client.requests()- Request validationclient.responses()- Response trackingclient.rooms()- Waiting room information
Private API (requires privateKey):
client.account()- Account informationclient.accountPlan()- Account plan detailsclient.codes()- Access code managementclient.domains()- Domain configurationclient.domainIPs()- Domain IP addressesclient.domainReports()- Domain analyticsclient.domainRequests()- Domain request logsclient.domainRooms()- Rooms for a domainclient.domainURLs()- Protected URLsclient.groups()- Access code groupsclient.groupBatch()- Batch code operationsclient.groupCodes()- Codes in a groupclient.ips()- IP address managementclient.reports()- Analytics reportsclient.rooms()- Waiting room managementclient.roomReports()- Room analyticsclient.roomSessions()- Active room sessionsclient.sessions()- Session managementclient.templates()- Waiting room templates
All methods support standard REST operations where applicable:
.get()- List all or get specific resource by ID.post(data)- Create new resource.put(id, data)- Update existing resource.patch(id, data)- Partial update.delete(id)- Delete resource
Full API documentation with request/response examples is available in your CrowdHandler dashboard under Account β API.
All SDK errors are instances of CrowdHandlerError with consistent structure:
try {
const rooms = await client.rooms().get();
} catch (error) {
console.error(error.message); // The actual API error message
console.error(error.statusCode); // HTTP status code (e.g., 401, 404)
console.error(error.suggestion); // Helpful guidance for resolution
console.error(error.code); // Error code for programmatic handling
}The SDK preserves the exact error messages from the CrowdHandler API, allowing you to handle specific error scenarios:
try {
const result = await gatekeeper.validateRequest({
custom: { code: 'user-code' }
});
} catch (error) {
// Check the specific error message from the API
if (error.message.includes('Invalid priority code')) {
// Handle invalid access code
}
// For advanced debugging, access the full API response
const apiResponse = error.context?.apiResponse;
}INVALID_CONFIG- Invalid SDK configurationMISSING_PRIVATE_KEY- Private key required for this operationAPI_CONNECTION_FAILED- Cannot reach CrowdHandler APIAPI_INVALID_RESPONSE- API returned an errorRATE_LIMITED- Too many requests (includes retry-after)
See the examples directory for complete working examples including:
- Express.js implementations (full protection, API-only, private API)
- Lambda@Edge handlers
- React integration
- Error handling patterns
- TypeScript usage
const express = require('express');
const crowdhandler = require('crowdhandler-sdk');
const app = express();
// Middleware to protect routes
async function protectRoute(req, res, next) {
try {
const { gatekeeper } = crowdhandler.init({
publicKey: process.env.CROWDHANDLER_PUBLIC_KEY,
request: req,
response: res
});
const result = await gatekeeper.validateRequest();
// Check if there was an error during validation
if (result.error) {
console.error(`API Error ${result.error.statusCode}: ${result.error.message}`);
// 4xx errors (e.g., invalid key, bad request): promoted = false (user blocked)
// 5xx errors (e.g., server error): promoted based on trustOnFail setting
}
if (result.setCookie) {
gatekeeper.setCookie(result.cookieValue, result.domain);
}
if (result.stripParams) {
return gatekeeper.redirectToCleanUrl(result.targetURL);
}
if (!result.promoted) {
return gatekeeper.redirectIfNotPromoted();
}
// User is promoted, continue
res.locals.gatekeeper = gatekeeper;
next();
} catch (error) {
// This catches unexpected errors (e.g., network issues, config errors)
console.error('CrowdHandler SDK error:', error.message);
// trustOnFail: true (default) = allow access on error
// trustOnFail: false = block access on error
next();
}
}
// Protect specific routes
app.get('/limited-product', protectRoute, (req, res) => {
res.send('This is a limited product page!');
// Record performance after response
if (res.locals.gatekeeper) {
res.locals.gatekeeper.recordPerformance();
}
});const crowdhandler = require('crowdhandler-sdk');
exports.handler = async (event) => {
const { gatekeeper } = crowdhandler.init({
publicKey: process.env.CROWDHANDLER_PUBLIC_KEY,
lambdaEdgeEvent: event
});
const result = await gatekeeper.validateRequest();
if (!result.promoted) {
// Redirect to waiting room
return {
status: '302',
statusDescription: 'Found',
headers: {
location: [{
key: 'Location',
value: result.targetURL
}]
}
};
}
// Continue with normal request processing
return event.Records[0].cf.request;
};import { useEffect, useState } from 'react';
import { init } from 'crowdhandler-sdk';
function ProtectedComponent() {
const [isPromoted, setIsPromoted] = useState(null);
useEffect(() => {
const checkAccess = async () => {
const { gatekeeper } = init({
publicKey: 'YOUR_PUBLIC_KEY',
options: { mode: 'clientside' }
});
const result = await gatekeeper.validateRequest();
if (!result.promoted) {
window.location.href = result.targetURL;
} else {
setIsPromoted(true);
}
};
checkAccess();
}, []);
if (isPromoted === null) return <div>Checking access...</div>;
if (!isPromoted) return <div>Redirecting to waiting room...</div>;
return <div>Protected content here!</div>;
}gatekeeper.overrideWaitingRoomUrl('https://custom-wait.example.com');// Don't check these paths
gatekeeper.setIgnoreUrls(/\.(css|js|png|jpg)$/);gatekeeper.overrideHost('example.com');
gatekeeper.overridePath('/special-path');
gatekeeper.overrideIP('203.0.113.0');
gatekeeper.overrideLang('en-US');
gatekeeper.overrideUserAgent('Custom Bot 1.0');// Basic usage (records automatically)
await gatekeeper.recordPerformance();
// With options
await gatekeeper.recordPerformance({
sample: 1.0, // Record 100% of requests (default 0.2)
statusCode: 200, // HTTP status code
overrideElapsed: 1234 // Custom timing in ms
});For testing your error handling without making real API calls, you can simulate errors:
const { gatekeeper } = init({
publicKey: 'YOUR_PUBLIC_KEY',
request: req,
response: res,
options: {
testError: {
statusCode: 500, // Simulate a 500 error
message: 'Simulated server error for testing'
}
}
});
// This will return immediately with a simulated error
const result = await gatekeeper.validateRequest();
// result.error will contain the test error
// result.promoted will be true (with default trustOnFail: true)This is useful for:
- Testing error handling logic in development
- Verifying fallback behavior for different error types
- Integration testing without affecting production metrics
Note: 4xx test errors will always set promoted: false, while 5xx test errors respect your trustOnFail setting.
Lite validator mode provides token refresh without API calls by checking room configuration locally. To enable it:
- Set
liteValidator: truein options - Fetch and provide your rooms configuration from the CrowdHandler API
// First, fetch your rooms configuration
const { client } = init({ publicKey: 'YOUR_PUBLIC_KEY' });
const roomsResponse = await client.rooms().get();
// Then initialize with lite validator enabled
const { gatekeeper } = init({
publicKey: 'YOUR_PUBLIC_KEY',
request: req,
response: res,
options: {
liteValidator: true, // Enable lite validator
roomsConfig: roomsResponse.result // Pass the rooms array from API
}
});
// Handle the lite validator redirect
const result = await gatekeeper.validateRequest();
if (result.liteValidatorRedirect) {
// Redirect to refresh token/session
return gatekeeper.redirect(result.liteValidatorUrl);
}When lite validator activates:
- URL matches a room in your config
- Token is missing or >12 hours old
- Redirects to CrowdHandler to refresh session
The SDK includes comprehensive testing tools:
# Start test server with your keys
npm run test:server -- --publicKey=YOUR_KEY --privateKey=YOUR_PRIVATE_KEY
# With custom options
npm run test:server -- --apiUrl=https://staging-api.crowdhandler.com --mode=hybrid
# Development mode (auto-rebuilds SDK)
npm run test:server:dev- Start the test server
- Open http://localhost:3000/test/browser-test.html
- Interactive testing with real API integration
# Run automated tests against test server
npm run test:clientFull TypeScript support with type definitions included:
import { init, CrowdHandlerError, ErrorCodes } from 'crowdhandler-sdk';
import type { Mode, Room, Domain, ValidationResult } from 'crowdhandler-sdk';
// All types are properly inferred
const { client, gatekeeper } = init({
publicKey: 'YOUR_KEY'
});
// TypeScript knows this returns Room[]
const rooms = await client.rooms().get();
// Error handling with types
try {
await client.domains().get();
} catch (error) {
if (error instanceof CrowdHandlerError) {
if (error.code === ErrorCodes.MISSING_PRIVATE_KEY) {
// Handle missing key
}
}
}The SDK is distributed in multiple formats:
- CommonJS (
dist/crowdhandler.cjs.js) - For Node.jsrequire() - ES Modules (
dist/crowdhandler.esm.js) - For modernimport - UMD (
dist/crowdhandler.umd.js) - For browsers via<script> - UMD Minified (
dist/crowdhandler.umd.min.js) - Production browser build
- π Knowledge Base
- π API Documentation
- π¬ Email Support
- π Report Issues
BSD 3-Clause License - see LICENSE file for details.