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.
@hibira-io/waiting-roomnpm ↗GitHub ↗
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 ↗
Install the plugin
claude plugin marketplace add hibira-io/agent-plugin claude plugin install hibira@hibiraPaste 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.
Prefer to do it by hand? These are the steps below:
- 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.

Sign in to see these snippets with your own cluster host. Sign in
Install
npm install @hibira-io/waiting-room pusherCreate 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/'], }, });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 →