About QueueZero
QueueZero is a digital queue management platform for small businesses, clinics, banks, salons, government offices, anywhere people still stand in physical lines. Customers join remotely with a business code, see their position and estimated wait, and check in with a signed QR when they arrive. Operators run the queue from a real-time dashboard: call the next person, scan arrivals, mark served or no-show, and review analytics.
Live app: queuezero-mvp-build.vercel.app
What inspired us
Waiting in line feels like dead time. You don't know how long it will take, you can't leave without losing your spot, and staff spend energy managing crowds instead of serving people.
We wanted something simple: join from your phone, know your place in line, and show up when it's your turn. For the business, we wanted a console that feels modern and fast, not another spreadsheet taped to a window.
QueueZero started as a v0 prototype and grew into a full product for the H0: Hack the Zero Stack with Vercel v0 and AWS Databases hackathon, with Amazon Aurora DSQL as the production database.
What we learned
Aurora DSQL is powerful, but not vanilla Postgres. we learned to design without foreign keys, use UUID primary keys everywhere, create indexes with CREATE INDEX ASYNC, and handle optimistic concurrency conflicts (SQLSTATE 40001) with automatic transaction retries. A migration that worked locally failed in production because DSQL does not support expression indexes like LOWER(email) — uniqueness has to live on plain columns with normalization in application code.
Queue correctness needs transactions. Ticket position, queue_days counters, and check-in state must update atomically. A naive read-then-write approach breaks under concurrent joins. Wrapping queue operations in withTransaction() with retry logic was essential.
Auth has two audiences. Business operators and customers use different Clerk flows (/sign-in vs /customer/sign-in). Staff can be invited to a business without being the owner, which meant extending access beyond owner_clerk_id to a business_members table with roles and pending invitations.
Security hygiene matters. Environment files should never live in git history. We rewrote the full commit history to strip .env artifacts and kept secrets in Vercel environment variables and local .env.local only.
How we built it
Stack
| Layer | Technology |
|---|---|
| Frontend | Next.js 16 (App Router), React 19, Tailwind CSS |
| Auth | Clerk (business + customer paths) |
| Hosting | Vercel |
| Database (prod) | Amazon Aurora DSQL via Vercel OIDC + @aws-sdk/dsql-signer |
| Database (local) | Docker Postgres |
| QR check-in | HMAC-signed payloads verified server-side |
Architecture
Customers hit public join pages (/join). API routes create tickets inside retry-safe transactions. Staff use a protected dashboard (/dashboard) for live queue control, QR scanning, ticket management, and analytics.
flowchart LR
Customer[Customer Web] --> NextJS[Next.js on Vercel]
Operator[Business Dashboard] --> NextJS
NextJS --> API[API Routes + Queue Engine]
API --> DSQL[(Amazon Aurora DSQL)]
Core features shipped
- Public join flow — Customers enter a business code, pick a service, join the queue, and receive a ticket with live ETA.
- Signed QR check-in — Tamper-proof tokens verified with
QUEUEZERO_QR_SECRET; manual 6-character fallback when scanning fails. - Live queue console — Call next, pause queues, mark served / no-show / cancelled from dashboard or scanner.
- Multi-business support — Owners can create multiple businesses, switch between them, and land on a business picker.
- Staff access — Invite team members by email with roles (Staff, Manager, Admin); pending invites activate when the user signs up with Clerk.
- Settings & lifecycle — Edit business profile, delete a business (cascading cleanup of related data), and manage operating hours.
- Mobile-responsive operator UI — Slide-out navigation drawer on small screens so every dashboard route is reachable on a phone.
Wait time model
Estimated wait scales with queue depth and average service time. For position (p) and current serving position (s), customers ahead are roughly (p - s). With average service duration (\bar{t}) minutes:
$$ \text{ETA} \approx \max(0,\; p - s) \times \bar{t} $$
This is recalculated as the queue moves so join-page ETAs stay honest.
Challenges we faced
1. Aurora DSQL migration failures in production
Local Postgres accepted indexes that DSQL rejected. The business_members migration failed with “expressions as index keys not supported” until I replaced LOWER(email) indexes with normalized lowercase emails stored at write time.
2. Concurrent queue joins
Multiple customers joining at once could get duplicate positions without transactional inserts and queue_days counter updates. OCC retries solved race conditions but required careful error handling in API routes.
3. Clerk + multi-tenant access
Early versions assumed one owner per business. Adding staff meant reworking getUserBusinesses, active-business cookies, and authorization on every protected API route — owners manage staff; staff can operate queues but cannot delete the business.
5. Dev / prod parity
Local Docker Postgres on port 5433 (to avoid Windows Postgres on 5432), Vercel OIDC tokens for Aurora, and separate DATABASE_PROVIDER modes meant environment setup was a challenge. Documenting the path in README.md and keeping secrets out of git were part of the solution.
6. Making it feel like one product
v0 generated useful UI quickly, but pages diverged in styling (light onboarding vs dark dashboard). Unifying typography, card surfaces (bg-panel / bg-ink), mono labels, and shared shell components turned a collection of screens into a coherent operator console.
Closing thought
QueueZero is our attempt to kill the dumb waiting line, not with hype, but with a clear join flow, honest ETAs, verifiable check-ins, and tools operators can actually use on a busy day, including on a phone. Building it on Vercel + Aurora DSQL pushed us to think about distributed data, transaction safety, and production constraints early, which is exactly what we wanted from this hackathon.
Built With
- amazon-aurora-dsql
- aws-sdk-(dsql-signer)
- clerk
- docker
- framer-motion
- html5-qrcode
- lucide-react
- next.js-16
- node.js
- pg-(node-postgres)
- postgresql
- qrcode
- react-19
- shadcn/ui
- sharp
- tailwind-css-4
- typescript
- vercel
- vercel-analytics
- vercel-cli
- vercel-oidc
- zod
Log in or sign up for Devpost to join the conversation.