List entries
Page through one waitlist's entries — for rendering the queue in your own app or syncing it into your own systems. This is the read half of running a waitlist headless: capture with signup, react with webhooks, render with this.
GET /sdk/v1/waitlist/{waitlistId}/entries
- Auth: Server key with the
waitlist:readgrant —Authorization: Bearer <key>. Product keys (ck_srv_waitlist_…) have it; scoped keys must select it at creation. - Order: oldest signup first, stable across pages.
- Page size: 1–100 entries per request (default 50).
Query parameters
| Param | Type | Required | Description |
|---|---|---|---|
cursor | string | No | The nextCursor from the previous page. Omit for the first page. |
limit | number | No | Entries per page, 1–100. Defaults to 50. |
status | string | No | Only entries in this state: waiting, invited, or joined. |
The cursor is opaque — store it and pass it back, never parse it. It stays valid while you scan even if the queue renumbers underneath you: pages never skip or repeat an entry, though each entry's position is a snapshot taken when its page was read.
Response
| Field | Type | Description |
|---|---|---|
entries | Entry[] | One page of entries. |
nextCursor | string | null | Pass as ?cursor= for the next page; null on the last page. |
Entry object
| Field | Type | Description |
|---|---|---|
id | string | The entry's id. |
waitlistId | string | The waitlist's short id. |
identifierKind | string | Which channel identifies this person: email or phone. |
email | string | The signup's email address. Empty when identifierKind is phone — read phone instead. |
phone | string | The signup's phone number in E.164, when they have one. Empty otherwise. |
metadata | object | The string→string map submitted at signup — contact fields (name, company, …) and anything else you stored. {} when none. |
position | number | Queue position; meaningful while status is waiting. |
referrals | number | Signups this person has referred. |
referralCode | string | The short code in their share link (…?ref=<code>). |
referredBy | string | The referrer's own identifier — their email, or their number if that is what identifies them. Absent for direct signups. |
status | string | waiting, invited, or joined. |
source | string | widget, landing_page, api, import, or manual. |
signedUpAt | string | ISO 8601 timestamp. |
invitedAt | string | ISO 8601 timestamp; absent until invited. |
inviteCode | string | The live invite code (from the invite link / ); present once invited, absent while waiting. |
Example
bash
curl "https://api.uselucerna.app/sdk/v1/waitlist/wl_launch/entries?limit=50&status=waiting" \
-H "Authorization: Bearer $LUCERNA_SERVER_KEY"ts
const base = "https://api.uselucerna.app/sdk/v1/waitlist/wl_launch/entries";
let cursor: string | null = null;
do {
const url = new URL(base);
if (cursor) url.searchParams.set("cursor", cursor);
const res = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.LUCERNA_SERVER_KEY}` },
});
if (!res.ok) {
const error = await res.json();
throw new Error(`${error.code}: ${error.message}`);
}
const page = await res.json();
for (const entry of page.entries) {
console.log(entry.position, entry.email, entry.status);
}
cursor = page.nextCursor;
} while (cursor);json
{
"entries": [
{
"id": "5f0c6a8e-…",
"waitlistId": "wl_launch",
"identifierKind": "email",
"email": "ada@example.com",
"phone": "",
"metadata": { "name": "Ada Lovelace", "plan": "pro" },
"position": 1,
"referrals": 3,
"referralCode": "481920",
"status": "waiting",
"source": "landing_page",
"signedUpAt": "2026-07-01T09:30:00.000Z"
}
],
"nextCursor": "MTc1MTM2MjIwMDAwMC41ZjBjNmE4ZS…"
}Errors
| Status | code | When |
|---|---|---|
400 | invalid_request | Malformed cursor, limit, or status. |
401 | unauthorized | Missing or invalid server key. |
403 | forbidden | The key was not granted waitlist:read. |
404 | not_found | No waitlist matches waitlistId. |
See Errors for the response shape and retry guidance.