One POST. We keep the count.
Unwaited is a waitlist backend with an optional widget. Render your own form, send JSON to one endpoint, and let us handle order, capacity, dedupe, bots, and notifications.
The problem
Every waitlist you build is the same 400 lines nobody wants to own
A table, a unique index on email, and a counter that races under load. A captcha you bolt on after the first bot run. A CSV export marketing asks for on Friday. Unwaited is that code, done once, behind a JSON endpoint.
The recipe
Exactly what the button sets up
Capacity, reset time, opening hours, and the form fields. It is a starting point, not a cage. Every value is editable in Settings and the builder.
Recipe
API preview
- No cap. The list stays open until you pause it.
- No opening hours.
- Asks for stack and GitHub handle, both optional.
- The position is returned in the API response and shown on the success screen.
- The running count is shown on the form and returned by the API.
- Capacity
- Unlimited
- Signups open
- Any time
- Form asks for
- Email, Stack, GitHub handle
- Counter on the form
- Shown
How it works here
How the list works
POST JSON
Field keys are yours. Numbers can arrive as strings, checkboxes as "on". We coerce like a browser would.
Get a position back
201 with position, or 200 already_joined with the existing one. Full, paused, and closed are explicit codes.
API keys skip the captcha
Server-to-server calls use a Bearer key. Browser calls send a captcha token.
Signed webhooks
On Pro, HMAC-SHA256 over a timestamp and the raw body. Retries with backoff, redeliver from the dashboard.
Fetch the form definition
GET the schema and theme and render it yourself, or hydrate a native form.
Plain HTML fallback
A form with action set to the endpoint works with no JavaScript. It redirects back with the result.
Why it holds up
What matters most
Correct under concurrency
Positions and capacity are decided under a database row lock. Fifty concurrent joins at capacity 10 get exactly 10.
Predictable errors
validation_failed lists the fields. waitlist_full and waitlist_closed carry reopensAt. Nothing is a 500 by design.
Recurring capacity is built in
Per day, week, or month with a time zone and reset time. You get windowLabel and reopensAt in every response.
The docs are the product
Every endpoint, header, and code on one page, with curl and fetch examples.
FAQ
Questions people here ask
Is there a client library?
Not yet. The endpoint is a single JSON POST. A fetch call is shorter than a library import.
What are the rate limits?
10 submits per minute per IP per waitlist and 120 per minute per waitlist. 429 comes with Retry-After.
Can I self-host?
No. The value is the hosted endpoint, the notifications, and the dashboard.
Does it email the people who join?
Yes: a confirmation with their number and a status link, which you can turn off per waitlist. For anything else, use the webhook to trigger your own email from Resend, Postmark, or whatever you already run.
Other lines