Email Waitlist API

Plug-and-play email collection microservice. Multi-tenant, rate-limited, CORS-aware.

Quick Start

1

Create a project (admin only, one-time)

curl -X POST https://emailwaitlist.ayushojha.com/api/v1/projects \
  -H "X-Admin-Key: YOUR_ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"My App","slug":"my-app","allowed_origins":["https://myapp.com"]}'
2

Collect emails from your frontend

fetch('https://emailwaitlist.ayushojha.com/api/v1/subscribe', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': 'wl_your_project_api_key'
  },
  body: JSON.stringify({
    email: 'user@example.com',
    metadata: { name: 'Jane', source: 'landing-page' }
  })
})

Authentication

Two auth mechanisms — use the right header for each endpoint type:

HeaderValueUsed for
X-API-KeyProject API key (wl_...)All project-scoped endpoints
X-Admin-KeyServer admin keyProject management endpoints

Endpoints

POST /api/v1/subscribe Collect an email API Key
Request Body
FieldTypeDescription
emailstringrequiredEmail address (max 320 chars)
metadataobjectoptionalArbitrary JSON (max 4KB)
Example
curl -X POST https://emailwaitlist.ayushojha.com/api/v1/subscribe \
  -H "Content-Type: application/json" \
  -H "X-API-Key: wl_abc123" \
  -d '{"email":"user@example.com","metadata":{"name":"Jane"}}'
Response 201
{
  "message": "Successfully joined the waitlist!",
  "subscriber": {
    "id": "uuid",
    "project_id": "uuid",
    "email": "user@example.com",
    "metadata": {"name": "Jane"},
    "subscribed_at": "2026-03-12T10:00:00Z"
  }
}
Errors
CodeReason
400Invalid email or request body
401Missing or invalid API key
409Email already subscribed to this project
429Rate limit exceeded (30 req/min/IP)
GET /api/v1/subscribers List emails (paginated) API Key
Query Parameters
ParamTypeDefaultDescription
limitint50Results per page (max 500)
offsetint0Skip N results
Example
curl https://emailwaitlist.ayushojha.com/api/v1/subscribers?limit=20&offset=0 \
  -H "X-API-Key: wl_abc123"
Response 200
{
  "subscribers": [...],
  "total": 142,
  "limit": 20,
  "offset": 0
}
GET /api/v1/subscribers/export CSV download API Key
Example
curl https://emailwaitlist.ayushojha.com/api/v1/subscribers/export \
  -H "X-API-Key: wl_abc123" \
  -o subscribers.csv
Response

Returns a CSV file with columns: email, metadata, subscribed_at

DELETE /api/v1/subscribers/{email} Unsubscribe API Key
Example
curl -X DELETE https://emailwaitlist.ayushojha.com/api/v1/subscribers/user@example.com \
  -H "X-API-Key: wl_abc123"
Response 200
{"message": "subscriber removed"}
Errors
CodeReason
404Email not found in this project
GET /api/v1/stats Dashboard stats API Key
Example
curl https://emailwaitlist.ayushojha.com/api/v1/stats \
  -H "X-API-Key: wl_abc123"
Response 200
{
  "total": 142,
  "today": 8,
  "this_week": 34,
  "this_month": 89,
  "by_day": [
    {"date": "2026-03-10", "count": 12},
    {"date": "2026-03-11", "count": 14},
    {"date": "2026-03-12", "count": 8}
  ]
}
POST /api/v1/projects Create project Admin
Request Body
FieldTypeDescription
namestringrequiredDisplay name
slugstringrequiredURL-safe identifier (lowercase, hyphens)
allowed_originsstring[]optionalCORS origins. Empty = allow all. Use ["*"] for wildcard.
Example
curl -X POST https://emailwaitlist.ayushojha.com/api/v1/projects \
  -H "Content-Type: application/json" \
  -H "X-Admin-Key: YOUR_ADMIN_KEY" \
  -d '{"name":"My App","slug":"my-app","allowed_origins":["https://myapp.com"]}'
Response 201
{
  "message": "Project created. Save your API key — it won't be shown again.",
  "project": {
    "id": "uuid",
    "name": "My App",
    "slug": "my-app",
    "api_key": "wl_abc123...",
    "allowed_origins": ["https://myapp.com"],
    "created_at": "2026-03-12T10:00:00Z"
  }
}
GET /api/v1/projects List projects Admin
Example
curl https://emailwaitlist.ayushojha.com/api/v1/projects \
  -H "X-Admin-Key: YOUR_ADMIN_KEY"
Response 200
{"projects": [...]}
GET /health Health check None
Response 200
{"status": "ok"}

Integration Guide

Follow these three steps to add email collection to any website.

Step 1 — Register your site

Create a project to get an API key. Run this once per website (requires the admin key).

curl -X POST https://emailwaitlist.ayushojha.com/api/v1/projects \
  -H "X-Admin-Key: YOUR_ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My Website",
    "slug": "my-website",
    "allowed_origins": ["https://mywebsite.com"]
  }'

Save the api_key from the response — it starts with wl_ and won't be shown again.

Step 2 — Add the form to your frontend

Drop this into any page. Works with React, Vue, Svelte, plain HTML — anything that can call fetch.

React / Next.js
async function subscribe(email, name) {
  const res = await fetch('https://emailwaitlist.ayushojha.com/api/v1/subscribe', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': 'wl_your_project_api_key'
    },
    body: JSON.stringify({
      email,
      metadata: { name, source: 'landing-page' }
    })
  });

  const data = await res.json();

  if (res.ok) {
    // Success — show confirmation
    return { success: true, message: data.message };
  } else if (res.status === 409) {
    // Already subscribed
    return { success: false, message: 'You're already on the list!' };
  } else if (res.status === 429) {
    // Rate limited
    return { success: false, message: 'Too many requests. Try again shortly.' };
  } else {
    return { success: false, message: data.error || 'Something went wrong.' };
  }
}
Plain HTML + Vanilla JS
<form id="waitlist-form">
  <input type="email" id="wl-email" placeholder="you@example.com" required />
  <button type="submit">Join Waitlist</button>
  <p id="wl-msg"></p>
</form>

<script>
document.getElementById('waitlist-form').addEventListener('submit', async (e) => {
  e.preventDefault();
  const email = document.getElementById('wl-email').value;
  const msg = document.getElementById('wl-msg');

  const res = await fetch('https://emailwaitlist.ayushojha.com/api/v1/subscribe', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': 'wl_your_project_api_key'
    },
    body: JSON.stringify({ email })
  });

  const data = await res.json();
  msg.textContent = res.ok ? data.message : data.error;
});
</script>

Step 3 — Manage your subscribers

Use these endpoints with the same API key to view, export, or remove subscribers.

ActionRequest
List subscribersGET /api/v1/subscribers?limit=50&offset=0
Export CSVGET /api/v1/subscribers/export
UnsubscribeDELETE /api/v1/subscribers/{email}
Dashboard statsGET /api/v1/stats

All requests require the X-API-Key header.

Response Handling Cheatsheet

StatusMeaningWhat to show the user
201SubscribedSuccess message
400Bad email"Please enter a valid email"
409Duplicate"You're already on the waitlist"
429Rate limited"Please try again in a minute"
401Bad API keyCheck your X-API-Key header

Rate Limiting

The POST /api/v1/subscribe endpoint is rate-limited to 30 requests per minute per IP. When exceeded, the API returns 429 Too Many Requests. Other endpoints are not rate-limited.

CORS

Each project can define allowed_origins to restrict which domains can call the API from browsers. If no origins are set, all origins are allowed. The API handles OPTIONS preflight requests automatically.