Waitlist API
Launch waitlist with explicit consent, double opt-in, unsubscribe and verified deletion. Tokens arrive in URL fragments and are posted back in the body.
/v1/waitlist
Join the waitlist (double opt-in)
Local prototype · simulated data
Records explicit consent to the current waitlist privacy notice and emails a confirmation link. The response is identical whether or not the address is already on the list. Requires an allowed website Origin. No API key.
Authentication
No API key. Requests must come from an allowed website origin and are rate-limited per client.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | max length 254 · format email |
name | string | No | max length 100 |
company | string | No | max length 120 |
plan_interest | enum: "starter", "growth", "enterprise", "not_sure" | No | |
expected_volume | string | No | max length 60 |
country | string | No | pattern |
source_page | string | No | max length 200 |
website | string | No | Honeypot. Must be empty; humans never see this field. |
consent | object | Yes | |
consent.privacy_notice_accepted | boolean (always true) | Yes | |
consent.privacy_notice_version | string | Yes | Must equal the version currently shown on the site. |
consent.marketing_updates | boolean | No | Separate, optional opt-in. Defaults to false. |
Responses
- 202 Waitlist operation result. —
WaitlistStatusBody - 400 Invalid parameters. —
Error - 403 Key not allowed for this Origin or endpoint. —
Error - 405 Invalid parameters. —
Error - 413 Invalid parameters. —
Error - 415 Invalid parameters. —
Error - 429 Rate limit exceeded. —
Error
Response fields (202)
| Field | Type | Required | Description |
|---|---|---|---|
data | object | Yes | |
data.status | enum: "pending_confirmation", "confirmed", "unsubscribed", "deletion_email_sent_if_registered", "deleted" | Yes | |
meta | object | Yes | |
meta.request_id | string | Yes |
Example
curl -X POST "http://localhost:8787/v1/waitlist" \
-H "Origin: http://localhost:8787" \
-H "Content-Type: application/json" \
-d '{"email":"dev@example.com","plan_interest":"growth","consent":{"privacy_notice_accepted":true,"privacy_notice_version":"waitlist-notice-draft-2026-10-25","marketing_updates":false}}'
// Browser code on an allowed origin. The browser sends the Origin header itself.
const res = await fetch("http://localhost:8787/v1/waitlist", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
"email": "dev@example.com",
"plan_interest": "growth",
"consent": {
"privacy_notice_accepted": true,
"privacy_notice_version": "waitlist-notice-draft-2026-10-25",
"marketing_updates": false
}
}),
});
const body = await res.json();
if (!res.ok) throw new Error(body.error.code);
Response 202 from the local prototype (simulated data):
{
"data": {
"status": "pending_confirmation"
},
"meta": {
"request_id": "req_0000000000000000docs"
}
}
/v1/waitlist/confirm
Confirm a signup with the emailed one-time token
Local prototype · simulated data
Authentication
No API key. Requests must come from an allowed website origin and are rate-limited per client.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
token | string | Yes | min length 20 · max length 100 |
Responses
- 200 Waitlist operation result. —
WaitlistStatusBody - 400 Invalid parameters. —
Error - 403 Key not allowed for this Origin or endpoint. —
Error - 429 Rate limit exceeded. —
Error
Response fields (200)
| Field | Type | Required | Description |
|---|---|---|---|
data | object | Yes | |
data.status | enum: "pending_confirmation", "confirmed", "unsubscribed", "deletion_email_sent_if_registered", "deleted" | Yes | |
meta | object | Yes | |
meta.request_id | string | Yes |
Example
curl -X POST "http://localhost:8787/v1/waitlist/confirm" \
-H "Origin: http://localhost:8787" \
-H "Content-Type: application/json" \
-d '{"token":"TOKEN_FROM_EMAIL_LINK"}'
// Browser code on an allowed origin. The browser sends the Origin header itself.
const res = await fetch("http://localhost:8787/v1/waitlist/confirm", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
"token": "TOKEN_FROM_EMAIL_LINK"
}),
});
const body = await res.json();
if (!res.ok) throw new Error(body.error.code);
Response 200 from the local prototype (simulated data):
{
"data": {
"status": "confirmed"
},
"meta": {
"request_id": "req_0000000000000000docs"
}
}
/v1/waitlist/unsubscribe
Stop all waitlist emails (idempotent)
Local prototype · simulated data
Authentication
No API key. Requests must come from an allowed website origin and are rate-limited per client.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
token | string | Yes | min length 20 · max length 100 |
Responses
- 200 Waitlist operation result. —
WaitlistStatusBody - 400 Invalid parameters. —
Error - 403 Key not allowed for this Origin or endpoint. —
Error - 429 Rate limit exceeded. —
Error
Response fields (200)
| Field | Type | Required | Description |
|---|---|---|---|
data | object | Yes | |
data.status | enum: "pending_confirmation", "confirmed", "unsubscribed", "deletion_email_sent_if_registered", "deleted" | Yes | |
meta | object | Yes | |
meta.request_id | string | Yes |
Example
curl -X POST "http://localhost:8787/v1/waitlist/unsubscribe" \
-H "Origin: http://localhost:8787" \
-H "Content-Type: application/json" \
-d '{"token":"TOKEN_FROM_EMAIL_LINK"}'
// Browser code on an allowed origin. The browser sends the Origin header itself.
const res = await fetch("http://localhost:8787/v1/waitlist/unsubscribe", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
"token": "TOKEN_FROM_EMAIL_LINK"
}),
});
const body = await res.json();
if (!res.ok) throw new Error(body.error.code);
Response 200 from the local prototype (simulated data):
{
"data": {
"status": "unsubscribed"
},
"meta": {
"request_id": "req_0000000000000000docs"
}
}
/v1/waitlist/deletion-requests
Ask for erasure; a verification link is emailed if the address is registered
Local prototype · simulated data
The response does not reveal whether the address is on the list.
Authentication
No API key. Requests must come from an allowed website origin and are rate-limited per client.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | max length 254 · format email |
Responses
- 202 Waitlist operation result. —
WaitlistStatusBody - 400 Invalid parameters. —
Error - 403 Key not allowed for this Origin or endpoint. —
Error - 429 Rate limit exceeded. —
Error
Response fields (202)
| Field | Type | Required | Description |
|---|---|---|---|
data | object | Yes | |
data.status | enum: "pending_confirmation", "confirmed", "unsubscribed", "deletion_email_sent_if_registered", "deleted" | Yes | |
meta | object | Yes | |
meta.request_id | string | Yes |
Example
curl -X POST "http://localhost:8787/v1/waitlist/deletion-requests" \
-H "Origin: http://localhost:8787" \
-H "Content-Type: application/json" \
-d '{"email":"dev@example.com"}'
// Browser code on an allowed origin. The browser sends the Origin header itself.
const res = await fetch("http://localhost:8787/v1/waitlist/deletion-requests", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
"email": "dev@example.com"
}),
});
const body = await res.json();
if (!res.ok) throw new Error(body.error.code);
Response 202 from the local prototype (simulated data):
{
"data": {
"status": "deletion_email_sent_if_registered"
},
"meta": {
"request_id": "req_0000000000000000docs"
}
}
/v1/waitlist/delete
Erase the waitlist entry using the emailed one-time deletion token
Local prototype · simulated data
Authentication
No API key. Requests must come from an allowed website origin and are rate-limited per client.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
token | string | Yes | min length 20 · max length 100 |
Responses
- 200 Waitlist operation result. —
WaitlistStatusBody - 400 Invalid parameters. —
Error - 403 Key not allowed for this Origin or endpoint. —
Error - 429 Rate limit exceeded. —
Error
Response fields (200)
| Field | Type | Required | Description |
|---|---|---|---|
data | object | Yes | |
data.status | enum: "pending_confirmation", "confirmed", "unsubscribed", "deletion_email_sent_if_registered", "deleted" | Yes | |
meta | object | Yes | |
meta.request_id | string | Yes |
Example
curl -X POST "http://localhost:8787/v1/waitlist/delete" \
-H "Origin: http://localhost:8787" \
-H "Content-Type: application/json" \
-d '{"token":"TOKEN_FROM_EMAIL_LINK"}'
// Browser code on an allowed origin. The browser sends the Origin header itself.
const res = await fetch("http://localhost:8787/v1/waitlist/delete", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
"token": "TOKEN_FROM_EMAIL_LINK"
}),
});
const body = await res.json();
if (!res.ok) throw new Error(body.error.code);
Response 200 from the local prototype (simulated data):
{
"data": {
"status": "deleted"
},
"meta": {
"request_id": "req_0000000000000000docs"
}
}