Official package · Realtime building blocks

A virtual waiting room for ticket drops and flash sales

When too many people arrive at once, visitors take a queue number, watch the line move live and are let in at the rate you choose. Your edge or server checks their signed pass before serving the page.

npm versionLicense

Fastest way: one prompt with the Hibira plugin

Install the plugin in your AI coding agent and paste the prompt. The agent signs you in, creates a free app if you need one, writes the code and sends a test event. GitHub ↗

  1. Install the plugin

    claude plugin marketplace add hibira-io/agent-plugin
    claude plugin install hibira@hibira
  2. Paste this prompt

    Add Hibira Waiting room (@hibira-io/waiting-room) to this project.
    Follow https://hibira.io/guides/waiting-room, adapted to how this project is already set up.
    If I have no Hibira app yet, create a Free one that allows http://localhost so I can test right away.
    Keep the app key and secret in environment variables, add placeholders to .env.example, and never commit real values or send the secret to a browser.
    When you are done, send a test event, show me it arriving, and list the files you changed.
  • A hosted waiting page in seven languages; no page of your own to build.
  • protect() for Cloudflare Workers, Next.js and Vercel middleware, Deno, Bun and Express.
  • Schedule the start, pause, close when sold out, or let the next few hundred in at once.
  • Visitors keep their place on refresh. When the app is at its connection limit, the page polls instead of turning anyone away.
The hosted waiting page for a presale: the visitor’s queue number, how many are ahead and the estimated wait.
The hosted waiting page for a presale: the visitor’s queue number, how many are ahead and the estimated wait.

Sign in to see these snippets with your own cluster host. Sign in

  1. Install

    npm install @hibira-io/waiting-room pusher
  2. Create a room

    import Pusher from 'pusher';
    
    const pusher = new Pusher({
      appId: process.env.HIBIRA_APP_ID,
      key: process.env.HIBIRA_KEY,
      secret: process.env.HIBIRA_SECRET,
      host: 'ws.hibira.io',
      useTLS: true,
    });
    
    await pusher.post({
      path: '/waiting_rooms/presale',
      body: {
        admit_per_minute: 300,
        state: 'open', // or 'paused', 'closed'
        title: 'Winter tour presale',
        return_urls: ['https://shop.example.com/'],
      },
    });
  3. Protect the page

    // middleware.js (Next.js)
    import { NextResponse } from 'next/server';
    import { protect } from '@hibira-io/waiting-room/server';
    
    export async function middleware(request) {
      const blocked = await protect(request, {
        appKey: process.env.HIBIRA_KEY,
        secret: process.env.HIBIRA_SECRET,
        room: 'presale',
      });
      return blocked ?? NextResponse.next();
    }
    
    export const config = { matcher: ['/drop/:path*'] };

Before you start

  • return_urls must include the page visitors come back to, or the waiting page will not send them there.
  • A pass lasts 30 minutes by default. If each queue number may check out only once, record its ticketId at checkout.
  • The app secret is shown only once, when you create the app or rotate its secret. Keep it on your server, never in a browser or game build.

Use the app key from your dashboard; the secret stays on your server. Channels, events and authorization endpoints work exactly as they do with Pusher. Create your free account →