# Custom frontend guide | Waitlyze Docs

> Ship your own signup UI on the Signup API, embeds, referrals, domains, and email.

Canonical: https://waitlyze.com/docs/custom-frontend


This guide wires the whole public surface together: a form you own, Waitlyze-owned verification and storage, referral sharing, your domain, and your sender. Follow it in order - each step assumes the previous one works.

## 1. Collect signups with the API [#1-collect-signups-with-the-api]

Build any form (React, plain HTML, mobile app backend) and POST from **your server** with a [Signup API](/docs/api) key. Never ship the key to the browser - proxy through your backend.

```js title="Your backend → Waitlyze"
const response = await fetch("https://waitlyze.com/api/waitlist", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": orderId, // reuse on retry; see Signup API
  },
  body: JSON.stringify({
    apiKey: process.env.WAITLYZE_API_KEY,
    waitlistId: "YOUR_WAITLIST_ID",
    email,
    referral_code: refCode, // optional; snake_case or referralCode
  }),
});
const { data } = await response.json();
// data.position, data.referral_code, data.referral_link → render confirmation UI
```

Show `data.position` (“You're #9 in line”) and, when present, `data.referral_link` with a copy button. On `409`, read `data.position` - the email already joined, so show its place instead of an error wall. Poll `GET /api/waitlist?waitlistId=…&email=…` for a “check my spot” box.

## 2. When to use the embed instead [#2-when-to-use-the-embed-instead]

If you do **not** need custom logic, skip the API: paste the [embed snippet](/docs/embed) (`data-key-id` is a public waitlist ID, no secret). The embed forwards `?r=CODE` and UTM parameters automatically, sizes itself, and needs no backend. Many teams combine both - embed for the marketing site, API for in-product or mobile flows - because both feed the same waitlist, position engine, and referral graph.

## 3. Turn on referrals [#3-turn-on-referrals]

In Waitlist settings → **Referral sharing**, enable sharing ([Referrals](/docs/referrals)). From then on:

- API responses include `referral_code` + `referral_link` for every signup.
- Pass a friend's code as `referral_code` (API), `?r=CODE` (hosted page), or inherit it from your page URL (embed).
- Credit is awarded when the invitee verifies and the referral is `CLEAR`/`APPROVED` - gate rewards on the [`referral.completed` webhook](/docs/webhooks), never on signup alone. (Queue positions additionally require the inviter to be verified.)
- Review flagged referrals in Subscribers → Referral review.

## 4. Serve it from your domain [#4-serve-it-from-your-domain]

Add `join.example.com` in Waitlist settings → **Custom domain** ([Custom domains](/docs/domains)). It serves the same published revision as the hosted `/w/slug` page, so your API-built confirmation screens can link to a first-party URL. `referral_link` values in API responses still point at the Waitlyze hosted URL - rewrite the origin to your domain when rendering if you prefer (`/w/slug?r=CODE` path shape is identical).

## 5. Send from your domain [#5-send-from-your-domain]

Connect [Resend](/docs/resend) (Settings → Integrations → Resend) so waitlist email can use your verified sender. The workspace connection is the shared fallback for verification, broadcast, and automation email. Keep it **Active** before launch so verification messages can be delivered reliably.

Per-waitlist sender credentials are also supported through authenticated waitlist settings endpoints; see [Resend](/docs/resend#per-waitlist-sender-identity). They are not a complete override yet: broadcast, automation, and test-send paths still use the workspace sender because they do not pass a waitlist ID through sender resolution. Do not assume those paths use a waitlist-specific From address.

## Launch checklist [#launch-checklist]

- [ ] Waitlist is **Published** (drafts reject every channel).
- [ ] Test signup via API returns `200` with `position` + `referral_link`.
- [ ] Duplicate email returns `409` with the existing `position`.
- [ ] Status lookup returns the same position for the test email.
- [ ] `referral.completed` webhook arrives on a verified referral (test with two real addresses).
- [ ] Resend status is **Active**; verification email arrives from your domain.
- [ ] Custom domain reads **Ready** (or ship on the hosted URL first).

## Next [#next]

<Cards>
<Card title="Signup API" href="/docs/api" description="Exact request contract, idempotency, and status codes." />
<Card title="API Reference" href="/docs/api/reference" description="All supported public endpoints and their scope." />
<Card title="Webhooks" href="/docs/webhooks" description="Stream signup, referral, and milestone events." />
<Card title="Hosted pages and embeds" href="/docs/embed" description="The no-backend alternative." />
<Card title="Resend" href="/docs/resend" description="Connect your sending domain." />
</Cards>

## Related Links
- [API Reference](https://waitlyze.com/docs/api/reference) - REST API endpoints
- [Embed Widget](https://waitlyze.com/docs/embed) - Embed instructions
- [Waitlyze Home](https://waitlyze.com/) - Product overview
