Taldres/laravel-waitlist

Build the pages of a taldres/laravel-waitlist integration: the signup form with the registered wording, and the confirm, unsubscribe and preference pages, either in Laravel controllers (Blade, Livewire, Inertia) or in an SPA or static site over the package's JSON API, including CORS and bot protection.

O que é laravel-waitlist?

laravel-waitlist is a Claude Code agent skill that build the pages of a taldres/laravel-waitlist integration: the signup form with the registered wording, and the confirm, unsubscribe and preference pages, either in Laravel controllers (Blade, Livewire, Inertia) or in an SPA or static site over the package's JSON API, including CORS and bot protection.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Taldres/laravel-waitlist/tree/HEAD/resources/boost/skills/laravel-waitlist-frontend

Installed? Explore more Programação e Desenvolvimento skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Laravel Waitlist: frontend pages

Use this skill when building or changing the signup form, the confirmation page, the unsubscribe page or the preference page for taldres/laravel-waitlist.

Primary Goal

  • forms show exactly the registered wording and post back what they showed; pages behind mail links never change state on GET

Workflow

1. Pick the architecture

  • Laravel renders the pages (Blade, Livewire, Inertia): call the Waitlist facade from controllers. The package routes can stay off, unless mails should carry RFC 8058 one-click List-Unsubscribe headers, which need them; the pages still render the UI either way.
  • SPA or static site without a Laravel backend for the pages: set WAITLIST_ROUTES_ENABLED=true and call the JSON API under /waitlist.

Point the mail links at the app's own pages either way, in the project's definition (app/Providers/WaitlistServiceProvider.php). Read the environment through config(), never env():

$project->urls(
    confirm: config('app.frontend_url').'/waitlist/confirm/{token}',
    unsubscribe: config('app.frontend_url').'/waitlist/leave/{token}',
    manage: config('app.frontend_url').'/waitlist/preferences/{token}',
);

The same call takes confirmed, expired, invalid, unsubscribed and erased, the pages a browser lands on after posting to the package routes.

2. Signup form

Render the wording from the package, never type it into the template:

return view('waitlist.signup', [
    'purposes' => Waitlist::purposes('default', locale: app()->getLocale()),
]);
@foreach ($purposes as $wording)
    <input type="hidden" name="purposes[{{ $wording->purpose }}][version]" value="{{ $wording->version }}">
    <input type="hidden" name="purposes[{{ $wording->purpose }}][locale]" value="{{ $wording->locale }}">
    <input type="hidden" name="purposes[{{ $wording->purpose }}][hash]" value="{{ $wording->hash() }}">

    @if ($wording->required)
        <input type="hidden" name="agreed[]" value="{{ $wording->purpose }}">
        <p>{{ $wording->text }}</p>
    @else
        <label><input type="checkbox" name="agreed[]" value="{{ $wording->purpose }}"> {{ $wording->text }}</label>
    @endif
@endforeach
use Illuminate\Support\Arr;
use Taldres\Waitlist\Exceptions\WaitlistException;
use Taldres\Waitlist\Http\Rules\PurposeChoice;
use Taldres\Waitlist\Support\RequestContext;

$validated = $request->validate([
    'email' => ['required', 'email:filter', 'max:255'],
    'purposes' => ['required', 'array'],
    'purposes.*' => ['required', new PurposeChoice],
    'agreed' => ['required', 'array'],
    'agreed.*' => ['string'],
]);

try {
    Waitlist::for('default')->add($validated['email'], Arr::only($validated['purposes'], $validated['agreed']), context: RequestContext::fromRequest($request));
} catch (WaitlistException $exception) {
    return back()->withErrors(['purposes' => $exception->getMessage()]);
}

return view('waitlist.check-your-inbox'); // the same answer for new and known addresses

Over HTTP: GET /waitlist/purposes?list=default&locale=de, then POST /waitlist with email, list and purposes as {purpose: {version, locale, hash}}. Post back the locale the response served, which may be a fallback. Answers: 202 for every address, 422 names the field.

Calling from your own server instead (Next.js Server Actions, a BFF with a token per project): keep the token server-side and limit each visitor there, since the API sees only your server. With wordingFromCallers() on the project the server may post {version, locale?, text} with the exact text the form showed instead of rendering it from the API.

More than the address goes under metadata, and only for fields the project or list defines ($project->fields(...), ->fields(...) on a list); anything else is a 422. In your own controllers validate with Waitlist::for('default')->fields().

3. Confirm page

The mail link opens the page; the page posts the token back.

use Taldres\Waitlist\Exceptions\ExpiredTokenException;
use Taldres\Waitlist\Exceptions\InvalidTokenException;

try {
    Waitlist::confirm($token);
} catch (ExpiredTokenException) {
    return view('waitlist.expired');
} catch (InvalidTokenException) {
    abort(404);
}

Over HTTP: POST /waitlist/confirm/{token} with Accept: application/json answers {"data": {..., "status": "confirmed"}}, 404 unknown, 410 expired; the package's own refusals name an error, e.g. invalid_token, and a 404 without one means a wrong URL, routes that are off or a useWaitlist gate that keeps the caller out.

4. Unsubscribe page

// POST from the page; a link for one purpose carries ?purpose=
$purpose = $request->query('purpose');

is_string($purpose)
    ? Waitlist::withdrawConsent($token, $purpose)   // only that purpose
    : Waitlist::unsubscribe($token);                // leaves the list

Waitlist::requestManageLink($token);                // "manage preferences instead": mails a manage link

Both throw InvalidTokenException for an unknown token.

Over HTTP: POST /waitlist/unsubscribe/{token}[?purpose=] and POST /waitlist/manage-link with {"token": ...}.

5. Preference page

Behind the manage token from the mailed link (expires after waitlist.manage.token_ttl minutes, 60 by default, at least 1, never unlimited): GET /waitlist/manage/{token} (status, purposes in force), PUT .../purposes with the complete wanted set (the primary purpose must stay), POST .../data (JSON copy), POST .../unsubscribe, POST .../erase with {"confirm": true}. On 410, offer to send a new link. Ask for an explicit confirmation before erasing. In PHP: Waitlist::findByManageToken($token), Waitlist::grantConsent($token, $purpose, $version, $locale).

Shape the purposes of that PUT like the signup's. With WAITLIST_REQUIRE_WORDING_HASH=true, only a purpose being added needs its hash; keeping or dropping one does not.

6. Protect the public endpoints

  • Bot check on the signup and manage links by address, e.g. Cloudflare Turnstile:

    Waitlist::verifySpamUsing(fn (Request $request): bool => Http::asForm()
        ->post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [
            'secret' => config('services.turnstile.secret'),
            'response' => (string) $request->input('turnstile_token'),
            'remoteip' => $request->ip(),
        ])->json('success') === true);
    

    It guards the package routes only; in own controllers, check it yourself.

  • Frontend on another origin: add 'waitlist', 'waitlist/*' to paths in config/cors.php.

  • Behind a proxy or CDN: configure trusted proxies, or all visitors share one rate limit bucket.

  • Routes that are off (the default) answer 404 before any rate limit or session starts. Laravel still answers 405 for a method a route does not take and OPTIONS with the allowed methods before any middleware runs; neither acts. An empty WAITLIST_ROUTES_ENABLED= is a config error; false turns the routes off. The package puts its check, CheckRouteConfig, at the head of Laravel's middleware priority. An app that replaces that list later (setMiddlewarePriority() in a provider booting after the package, or in booted()) must list Taldres\Waitlist\Http\Middleware\CheckRouteConfig first; priority() in bootstrap/app.php needs nothing.

Rules, References, and Templates

Examples

  • "Add a waitlist form to the Blade landing page": steps 2 to 4 in controllers.
  • "Our Nuxt site needs a waitlist": WAITLIST_ROUTES_ENABLED=true, CORS, then the HTTP variants of steps 2 to 5.
  • "Add Turnstile to the waitlist form": step 6, and send turnstile_token with the form.

Anti-patterns

  • Confirming or unsubscribing on GET; mail scanners follow every link.
  • Typing the consent text into the template, or posting text instead of versions.
  • Telling apart new and known addresses in the response.
  • Putting authentication into waitlist.routes.middleware: it also guards the token links and one-click unsubscribes. Use a ProjectResolver or own controllers for closed signups, and waitlist.routes.group_middleware.signup for checks on the forms only, such as CSRF or an origin check.
  • A pre-ticked checkbox for an optional purpose, or one checkbox for two purposes.

Individual skills in this repo

This repo contains 13 individual skills — each has its own dedicated page.

Taldres/laravel-waitlist

Use this skill when reviewing Laravel package compatibility across composer constraints, PHP versions, Laravel versions, Testbench versions, dependency stability lanes, Windows CI, or matrix-sensitive code and workflow changes.

Taldres/laravel-waitlist

Use this skill when creating or updating the bundled Laravel Boost skill under resources/boost/skills from the package implementation and package documentation. Trigger after public APIs, commands, config, routes, views, publish tags, README content, or examples change.

Taldres/laravel-waitlist

Use this skill when preparing Laravel package releases: CHANGELOG.md updates, generated release notes, GitHub release workflows, version checks, tags, release validation, or release automation changes. Never publish autonomously.

Taldres/laravel-waitlist

Use this skill when adding Laravel package capabilities or wiring them through the service provider: commands, migrations, routes, config merges, views, translations, assets, middleware, publish tags, workbench files, or console-only behavior.

Taldres/laravel-waitlist

Use this skill when writing, editing, fixing, or reviewing package tests with Pest 4/5 and Orchestra Testbench, including TDD, feature tests, unit tests, type coverage, arch tests, workbench behavior, commands, routes, config, migrations, and publishable resources.

Taldres/laravel-waitlist

Integrate taldres/laravel-waitlist in a Laravel application: define purposes, lists and fields, build the signup form, send the confirmation mail from events, add a preference page, and keep consent, retention and encryption intact.

Taldres/laravel-waitlist

Turn a taldres/laravel-waitlist waitlist into a launch: invite people in batches in signup order, let invited addresses register, announce the launch to everyone who agreed, and erase the list once its purpose is fulfilled.

Taldres/laravel-waitlist

Send the mails of a taldres/laravel-waitlist integration: the double opt-in confirmation, the preference page link, a welcome mail, launch mails and newsletters to the people who agreed, with the right unsubscribe links, one-click headers and a sender per project.

Taldres/laravel-waitlist

Operate a taldres/laravel-waitlist installation: answer access and erasure requests, withdraw a purpose on request, apply retention, erase a list once its purpose is fulfilled, rotate APP_KEY with waitlist:rekey, export a list, and check the setup before going live.

Taldres/laravel-waitlist

Run the waitlists of several products in one Laravel app with taldres/laravel-waitlist projects: define projects, scope facade calls with project(), resolve the project of HTTP signups, send mail per project, handle requests across all projects, or keep projects in a database for a central waitlist API.

Taldres/laravel-waitlist

Keep a newsletter tool such as Brevo, Mailchimp or Mailcoach in step with a taldres/laravel-waitlist waitlist: add confirmed contacts per purpose, remove them on withdrawal and erasure, and carry unsubscribes made at the provider back into the waitlist via a webhook.

Taldres/laravel-waitlist

Build waitlist statistics with taldres/laravel-waitlist: signups and confirmations per day for charts, totals and confirmation rates for a period, the confirmed count on any past day, and how many people are on a list right now, for dashboards, admin pages and API endpoints.

Taldres/laravel-waitlist

Write feature tests for an application's taldres/laravel-waitlist integration: get the plain tokens from events, assert the confirmation and other mails, test the signup, confirm and unsubscribe flows in PHP and over the package's HTTP routes, and bypass rate limits and bot checks in tests.

Habilidades Relacionadas