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)
);
}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