Skip to content

Errors

Every non-2xx response uses the same JSON shape:

json
{
  "code": "invalid_request",
  "message": "entries: Array must contain at least 1 element(s)"
}
  • code — a stable, machine-readable string. Branch on this.
  • message — a human-readable explanation. It may change; don't parse it.

Check the HTTP status code first, then code for the specifics.

Status codes

StatuscodeMeaning
400invalid_requestThe request body or path parameter failed validation. message names the offending field.
401unauthorizedThe server key is missing or invalid. (signup only)
403forbiddenThe waitlist is closed or rejects this signup (e.g. a blocked email).
404not_foundThe waitlist or invite code doesn't exist.
410expired / revokedThe invite code is no longer redeemable. (accept only)
429rate_limitedToo many requests — slow down and retry with backoff.
500internal_errorSomething went wrong on our side. Safe to retry.

Handling errors

ts
const res = await fetch(url, options);

if (!res.ok) {
  const { code, message } = await res.json();

  switch (code) {
    case "unauthorized":
      throw new Error("Check LUCERNA_SERVER_KEY");
    case "invalid_request":
      throw new Error(`Bad request: ${message}`);
    case "rate_limited":
    case "internal_error":
      // Transient — retry with exponential backoff.
      break;
    default:
      throw new Error(`${code}: ${message}`);
  }
}

Retrying safely

The signup endpoint is idempotent by email — retrying a request never creates duplicate entries (already-present emails are counted as skipped). Accepting an invite is idempotent too. That makes 429 and 500 responses safe to retry with backoff.

Lucerna Waitlist API