Signup API
POST signups directly from your backend, app, or CLI. JSON in, JSON out.
https://waitlist.findutils.com
| Method | Path | Description |
|---|---|---|
| GET | /api/public/:slug/config |
Public waitlist config (title, theme, fields, Turnstile key) |
| POST | /api/public/:slug/signup |
Create a signup. Returns position + referral code. |
Returns the waitlist's public configuration. Cached at the edge for 60 seconds. Use this to render your own custom form.
{
"title": "Acme AI",
"description": "Be the first to know.",
"theme": {
"primary": "#4f46e5",
"bg": "#ffffff",
"text": "#0f172a",
"font": "system",
"radius": "soft",
"logo_url": "",
"hero_image": ""
},
"fields": {
"name": true,
"company": false,
"twitter": false
},
"branding": true,
"turnstile_site_key": "0x4AAAAAAA..."
}
Creates a signup on a waitlist. Returns the new position and a referral code.
Request body
{
"email": "alice@example.com",
"name": "Alice",
"company": "Acme Inc",
"twitter": "alice",
"ref": "AB3CD4EF",
"turnstile": "TURNSTILE_TOKEN"
}
Only email and turnstile are required. Other fields are optional and only stored if the waitlist has them enabled.
Success response (200)
{
"ok": true,
"position": 47,
"ref_code": "XY9ZW8V7",
"share_path": "/w/your-slug?ref=XY9ZW8V7"
}
Duplicate email (200)
Not an error — returns the existing position and ref code so your UI can show "you're already in".
{
"ok": true,
"duplicate": true,
"position": 47,
"ref_code": "XY9ZW8V7"
}
Error responses
| Status | Meaning |
|---|---|
400 | Invalid email, bad JSON, payload too large, or honeypot triggered. |
403 | Turnstile failed, or waitlist is full. |
404 | Slug doesn't exist. |
429 | Rate limited (120 requests per IP per minute). |
500 | Transient server error — safe to retry once. |
curl
curl -X POST https://waitlist.findutils.com/api/public/your-slug/signup \
-H "Content-Type: application/json" \
-d '{
"email": "alice@example.com",
"name": "Alice",
"turnstile": "TURNSTILE_TOKEN"
}'
JavaScript
const res = await fetch(
'https://waitlist.findutils.com/api/public/your-slug/signup',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: 'alice@example.com',
name: 'Alice',
turnstile: turnstileToken,
}),
}
);
const data = await res.json();
if (data.ok) {
console.log('Position:', data.position);
console.log('Share URL:', 'https://waitlist.findutils.com' + data.share_path);
}
Python
import httpx
res = httpx.post(
'https://waitlist.findutils.com/api/public/your-slug/signup',
json={
'email': 'alice@example.com',
'name': 'Alice',
'turnstile': turnstile_token,
},
)
data = res.json()
print('Position:', data['position'])
Node.js
const res = await fetch(
'https://waitlist.findutils.com/api/public/your-slug/signup',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: 'alice@example.com', turnstile: token }),
}
);
console.log(await res.json());
Every signup request must include a valid Cloudflare Turnstile token. Three ways to get one:
- Embed widget — the widget handles Turnstile automatically. Use this if you're rendering Waitkit's own form.
- Your own form — render Turnstile client-side with the
turnstile_site_keyfrom the config endpoint and pass the resulting token in the signup POST. See the Turnstile docs. - Server-to-server — not supported on the public endpoint. The Turnstile check fails closed.
All endpoints share a per-IP rate limit of 120 requests per minute, enforced at the edge before any DB hit. Exceeding returns 429 with a Retry-After: 60 header.
The public endpoints (/api/public/*) are CORS-enabled with Access-Control-Allow-Origin: * — you can POST from any origin, including localhost during development.
Listing your waitlists, updating them, viewing signups, exporting CSVs, and upgrading to Pro live under /api/waitlists/*. They require a valid waitkit_session cookie set by the magic-link login flow and are consumed by the dashboard UI. If you need programmatic access, reach out.