LaunchlistLaunchlistStart free
v1.0

Introduction

Launchlist is a lightweight waitlist platform. Create a project, generate an API key, and start collecting emails — in under 2 minutes.

There are two ways to collect emails: the REST API for server-side integrations, and the embeddable widget for dropping a form directly on any webpage. Both write to the same subscriber list inside your Launchlist dashboard.

This is the API reference for developers. If you're just getting started, the Quick start section below walks through the full flow in 3 steps.

Quick start

Get from sign-up to collecting emails in about 2 minutes.

1
Create a project

Sign in at /login, then go to Dashboard → Waitlists → New waitlist. Choose a name and a URL-safe slug (e.g. my-app-beta).

2
Generate an API key

Open your project and click Generate key in the API Keys panel. Copy the key immediately — it is shown in full once. Store it as an environment variable.

3
Send your first subscriber

Make a POST request with the subscriber's email:

bash
curl -X POST https://your-domain.com/api/v1/subscribe \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ll_live_your_key_here" \
  -d '{"email": "user@example.com"}'

You'll get back a 201 Created on success. The subscriber immediately appears in your dashboard.


Creating a project

A project (also called a waitlist) is a named collection of subscriber emails. Each project has a unique slug used in API calls and embed URLs.

FieldRequiredNotes
NameYesDisplay name shown in your dashboard.
SlugYesLowercase, hyphens only. Used in API and embed URLs. Must be globally unique.
DescriptionNoInternal note — not shown to subscribers.

Navigate to Dashboard → Waitlists → New waitlist to create one. The slug is auto-generated from the name but you can edit it before saving.


API keys

Each project can have multiple API keys. Keys are scoped to a single project — a key for project my-app cannot add subscribers to other-app.

Generating a key

Open a project and click Generate key in the API Keys panel. The full key is shown once. If you lose it, revoke it and generate a new one.

Key format

All keys follow the format ll_live_ followed by 48 lowercase hex characters:

text
ll_live_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6

Security

API keys grant write access to your waitlist. Keep them in environment variables — never commit them to source control or expose them in client-side JavaScript. For browser/client use, use the widget endpoint instead.

Authentication

The /api/v1/subscribe endpoint requires an API key passed in one of two ways:

MethodHeader
Bearer token (preferred)Authorization: Bearer ll_live_your_key
HeaderX-API-Key: ll_live_your_key

Both methods are equivalent. Choose whichever your HTTP client handles more naturally.

Bearer token example

javascript
fetch('/api/v1/subscribe', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer ll_live_your_key_here'
  },
  body: JSON.stringify({ email: 'user@example.com' })
})

X-API-Key example

javascript
fetch('/api/v1/subscribe', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': 'll_live_your_key_here'
  },
  body: JSON.stringify({ email: 'user@example.com' })
})

POST /api/v1/subscribe

Add an email address to the waitlist associated with the API key. Idempotent — submitting an email twice returns 200 on the second call.

Request

HeaderValue
Content-Typeapplication/json
AuthorizationBearer <api_key> (or X-API-Key)

Body

FieldTypeRequiredDescription
emailstringYesMust be a valid email address. Stored lowercase.
namestringNoSubscriber name. Stored as-is.
json
{
  "email": "user@example.com",
  "name": "Jane Smith"
}

Responses

StatusBodyMeaning
201 Created{ "message": "Subscribed successfully" }Email added to waitlist.
200 OK{ "message": "Already subscribed" }Email already exists — no duplicate created.
400 Bad Request{ "error": "A valid email is required" }Missing or malformed email.
401 Unauthorized{ "error": "Invalid or revoked API key" }Missing, wrong, or revoked key.
500 Server Error{ "error": "Failed to subscribe" }Database error.

Code examples

JavaScript

javascript
const res = await fetch('https://your-domain.com/api/v1/subscribe', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer ' + process.env.LAUNCHLIST_API_KEY
  },
  body: JSON.stringify({ email, name })
})

const data = await res.json()
if (res.status === 201) console.log('Subscribed!')
else if (res.status === 200) console.log('Already on the list')
else console.error(data.error)

Python

python
import requests, os

res = requests.post(
    'https://your-domain.com/api/v1/subscribe',
    headers={
        'Authorization': f"Bearer {os.environ['LAUNCHLIST_API_KEY']}"
    },
    json={'email': 'user@example.com', 'name': 'Jane Smith'}
)

print(res.json())

cURL

bash
curl -X POST https://your-domain.com/api/v1/subscribe \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $LAUNCHLIST_API_KEY" \
  -d '{"email": "user@example.com", "name": "Jane Smith"}'

PHP

php
$ch = curl_init('https://your-domain.com/api/v1/subscribe');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Content-Type: application/json',
        'Authorization: Bearer ' . getenv('LAUNCHLIST_API_KEY'),
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'email' => 'user@example.com',
        'name'  => 'Jane Smith',
    ]),
]);
$res = json_decode(curl_exec($ch), true);
curl_close($ch);

POST /api/v1/widget/[slug]

A public endpointfor browser-side subscriptions. No API key required. Used automatically by the embeddable widget — you generally don't call this directly unless you're building a custom widget UI.

Because this endpoint is public (no API key), it is rate-limited and only writes to projects that are active. For server-to-server integrations, always use /api/v1/subscribe with an API key.

Path parameter

ParamDescription
[slug]Your project slug, e.g. my-saas-beta

Body

FieldTypeRequired
emailstringYes
namestringNo

Responses

StatusMeaning
201Subscribed successfully
200Already subscribed
400Invalid email
404Project not found or inactive
500Server error
javascript
// Custom widget calling the public endpoint
const res = await fetch('/api/v1/widget/my-saas-beta', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ email: 'user@example.com' })
})

Widget builder

The widget builder lets you design an embeddable signup form without writing any code. Open it from your project page by clicking Widget in the top-right.

Templates

TemplateBest for
CardStandalone signup section, landing pages, sidebar.
MinimalInline forms inside existing content or blog posts.
BannerFull-width sections with copy on the left, form on the right.
DarkDark-themed websites or footer CTAs.

Customization options

OptionDescription
Accent colorButton and highlight color. Supports any hex value.
Border radiusSharp (4px), Rounded (12px), or Pill (999px).
HeadingMain headline text.
SubtextSupporting description below the heading.
Button textThe submit button label.
PlaceholderEmail input placeholder text.
Success messageText shown after a successful subscription.
Ask for nameAdds an optional name field above the email input.

Saving changes

Click Save changes to persist your config. The saved config is served to all embeds automatically — you never need to update your embed code when you change the widget design.


Script embed

The script embed is the recommended way to add a Launchlist widget to your site. It creates an auto-resizing iframe and requires no additional configuration.

html
<!-- Place where you want the widget to appear -->
<div id="ll-widget-your-slug"></div>
<script>
(function(){
  var i = document.createElement('iframe');
  i.src = 'https://your-domain.com/embed/your-slug';
  i.style.cssText = 'width:100%;height:0;border:none;display:block;';
  i.setAttribute('scrolling', 'no');
  var el = document.getElementById('ll-widget-your-slug');
  if (el) el.appendChild(i);
  window.addEventListener('message', function(e) {
    if (e.data && e.data.ll_slug === 'your-slug')
      i.style.height = e.data.ll_height + 'px';
  });
})();
</script>

The iframe sends a postMessage with its height whenever the content changes (including after a successful subscription). This keeps the iframe perfectly fitted to its content with no scrollbars.

Copy the exact script from the Embed code panel in your widget builder — it will have your real slug already substituted in.

iFrame embed

For simpler setups or CMSs that don't allow script tags, use the plain iframe variant. You'll need to set a fixed height manually.

html
<iframe
  src="https://your-domain.com/embed/your-slug"
  style="width:100%;border:none;min-height:280px;"
  frameborder="0"
  scrolling="no"
></iframe>
AttributeRecommended value
width100% (fluid)
min-height180px for Minimal, 280px for Card/Dark, 120px for Banner
frameborder0
scrollingno

Error codes

400Bad Request
Response body
{ "error": "A valid email is required" }
CauseThe email field is missing, empty, or not a valid email address.
FixValidate the email client-side before sending. Ensure you're sending JSON, not form-encoded data.
401Unauthorized
Response body
{ "error": "Invalid or revoked API key" }
CauseThe Authorization header is missing, the key is wrong, or it has been revoked.
FixCheck that the key starts with ll_live_. Regenerate if lost. Make sure you include the "Bearer " prefix.
404Not Found
Response body
{ "error": "Waitlist not found or inactive" }
CauseThe project slug doesn't exist, or the project has been paused.
FixDouble-check the slug. Verify the project is active in your dashboard.
500Server Error
Response body
{ "error": "Failed to subscribe" }
CauseUnexpected database error.
FixRetry with exponential backoff. If the error persists, contact support.

FAQ

Is there a rate limit on the subscribe endpoint?

The API key endpoint has no hard rate limit in the current version. The widget endpoint (no auth) has basic protection against abuse at the infrastructure level. Both will gain configurable rate limits in a future release.

Can I export my subscribers?

Yes — open a project in the dashboard, view the subscribers table, and export functionality is coming soon. In the meantime, you can query the subscribers table directly from your Launchlist dashboard.

Will a subscriber see an error if they submit twice?

No. The subscribe endpoint is intentionally idempotent — a duplicate email returns 200 OK with the message "Already subscribed" rather than an error. This lets you safely call the endpoint without de-duplicating on the client.

Does the widget work inside a React app?

Yes. Use the script embed or iframe — both work in React, Next.js, or any framework. Alternatively, call /api/v1/widget/[slug] directly from your own form component.

Is the embed CORS-friendly?

Yes. Both subscribe endpoints include Access-Control-Allow-Origin: * headers so they can be called from any domain.

What data is stored per subscriber?

email, name (optional), source (api or widget), and created_at. You can pass extra metadata via the metadata field in direct database inserts.

How do I delete a subscriber?

A subscriber management UI with delete support is on the roadmap.