API Documentation
Everything you need to integrate WaitStack into your application.
Quick Start
Get your waitlist running in 3 steps:
- Create a project in your dashboard
- Generate an API key
- Make a POST request to add users to your waitlist
curl -X POST https://your-app.com/api/public/v1/join \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com"}'Authentication
All API requests require a Bearer token in the Authorization header:
Authorization: Bearer YOUR_API_KEYAPI keys can be created in your project's API Keys section. Keep your keys secure and never expose them in client-side code.
Join Endpoint
Add a user to your waitlist.
/api/public/v1/joinRequest Body
{
"email": "user@example.com",
"referral_code": "abc123" // optional - credits the referrer
}Response
{
"id": "uuid",
"email": "user@example.com",
"referral_code": "xyz789", // user's unique referral code
"rank_position": 42, // position in waitlist
"referral_link": "https://your-app.com/join?ref=xyz789"
}Error Responses
400- Invalid email or already registered401- Invalid or missing API key429- Rate limit exceeded
Status Endpoint
Check a user's position and referral stats.
/api/public/v1/status/[referralCode]Response
{
"rank_position": 42,
"total_participants": 1234,
"referral_count": 5,
"email_verified": true
}Verify Endpoint
Verify a user's email address using the token sent to them.
/api/public/v1/verifyRequest Body
{
"token": "verification_token_from_email"
}Response
{
"success": true,
"rank_position": 42
}Webhooks
Receive real-time notifications when events occur on your waitlist. (Launch and Scale tiers)
Events
participant.createdTriggered when someone joins your waitlist.
participant.verifiedTriggered when someone verifies their email.
participant.referredTriggered when someone joins via a referral.
Webhook Payload
{
"event": "participant.created",
"timestamp": "2024-01-15T12:00:00Z",
"data": {
"id": "uuid",
"email": "user@example.com",
"referral_code": "xyz789",
"rank_position": 42
}
}Verifying Signatures
All webhooks include a signature header for verification:
X-WaitStack-Signature: sha256=abc123...
// Verify in Node.js:
const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}Squad Mode
Squad Mode lets users form teams of 3 to earn bonus points. Enable it in your project settings to encourage viral growth.
How It Works
- A verified user creates a squad and gets a unique code (e.g., SQUAD-ABC123)
- They share the code or squad link with friends
- When 3 members join and all verify their emails, everyone gets bonus points
Squad API
/api/public/v1/squadCreate Squad
{
"action": "create",
"participant_id": "uuid",
"project_id": "uuid"
}Join Squad
{
"action": "join",
"participant_id": "uuid",
"squad_code": "SQUAD-ABC123"
}Response (Create)
{
"squad_id": "uuid",
"code": "SQUAD-ABC123",
"message": "Squad created! Share this code with friends."
}/api/public/v1/squad?code=SQUAD-ABC123Get Squad Info
{
"code": "SQUAD-ABC123",
"is_complete": false,
"bonus_applied": false,
"members": [
{ "id": "uuid", "email": "j***@example.com", "verified": true },
{ "id": "uuid", "email": "s***@example.com", "verified": false }
],
"spots_remaining": 1
}/api/public/v1/squadLeave Squad
{
"participant_id": "uuid"
}Users cannot leave after the squad bonus has been applied.
Rules
- Maximum 3 members per squad
- Maximum 2 members from the same email domain
- All members must verify their email for bonus to apply
- Users can only be in one squad at a time
- Configure bonus points in project settings (default: 500 pts)
Code Examples
Preview
Join 1,234 others on the waitlist
Success State
You're #42 on the waitlist!
Share to move up the queue
Code
"use client";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { CheckCircle, Copy, Loader2 } from "lucide-react";
import { toast } from "sonner";
const API_KEY = process.env.NEXT_PUBLIC_WAITSTACK_KEY!;
interface WaitlistResult {
referral_code: string;
rank_position: number;
referral_link: string;
}
export function WaitlistForm() {
const [email, setEmail] = useState("");
const [status, setStatus] = useState<"idle" | "loading" | "success">("idle");
const [result, setResult] = useState<WaitlistResult | null>(null);
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setStatus("loading");
try {
const res = await fetch("/api/public/v1/join", {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({ email })
});
if (!res.ok) throw new Error("Failed to join");
const data = await res.json();
setResult(data);
setStatus("success");
} catch {
toast.error("Something went wrong");
setStatus("idle");
}
}
if (status === "success" && result) {
return (
<Card>
<CardContent className="pt-6 text-center space-y-4">
<CheckCircle className="h-12 w-12 text-green-500 mx-auto" />
<div>
<h3 className="text-xl font-semibold">
You're #{result.rank_position}!
</h3>
<p className="text-muted-foreground">
Share to move up the queue
</p>
</div>
<div className="flex gap-2">
<Input value={result.referral_link} readOnly />
<Button
variant="outline"
onClick={() => {
navigator.clipboard.writeText(result.referral_link);
toast.success("Copied!");
}}
>
<Copy className="h-4 w-4" />
</Button>
</div>
</CardContent>
</Card>
);
}
return (
<Card>
<CardHeader>
<CardTitle>Join the Waitlist</CardTitle>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
placeholder="you@example.com"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
</div>
<Button type="submit" className="w-full" disabled={status === "loading"}>
{status === "loading" ? (
<>
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
Joining...
</>
) : (
"Join Waitlist"
)}
</Button>
</form>
</CardContent>
</Card>
);
}Need help?
Have questions or need assistance? Email us at support@waitstack.co