Community코딩 & 개발github.com

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.

laravel-waitlist란 무엇인가요?

laravel-waitlist is a Claude Code agent skill that 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.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Taldres/laravel-waitlist/tree/HEAD/resources/boost/skills/laravel-waitlist-mail

Installed? Explore more 코딩 & 개발 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

Laravel Waitlist: mail

Use this skill when an application using taldres/laravel-waitlist needs to send any mail to people on a waitlist, or set up a mail provider for it.

Primary Goal

  • every mail the flow needs goes out from a queued, encrypted listener, carries the link that matches its purpose, and reaches only addresses with consent in force

Workflow

1. Know which mail is due when

The package sends no mail and calls no provider. It fires events; the app's listeners send through any Laravel mailer (Resend, Postmark, SES, SMTP, …).

MailDue onMust carry
Confirmation (double opt-in)EntrySubscribed with $event->requiresConfirmation$event->confirmUrl, $event->unsubscribeUrl
Preference page linkManageLinkRequested$event->manageUrl, sent only to $event->entry->email
Welcome (optional)EntryConfirmed$event->unsubscribeUrl, Waitlist::listUnsubscribeHeaders($event->entry)
Launch or update about one listthe app decidesper-recipient link that leaves the list
Newsletter (optional purpose)the app decidesper-recipient link that withdraws only the newsletter

2. Confirmation listener

namespace App\Listeners;

use App\Mail\ConfirmWaitlistMail;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Mail;
use LogicException;
use Taldres\Waitlist\Events\EntrySubscribed;
use Taldres\Waitlist\Facades\Waitlist;

class SendWaitlistConfirmationMail implements ShouldQueue, ShouldBeEncrypted
{
    public function handle(EntrySubscribed $event): void
    {
        if (! $event->requiresConfirmation) {
            return;
        }

        $confirmUrl = $event->confirmUrl ?? throw new LogicException('Set the confirm page in urls() or enable the package routes.');

        Mail::to($event->entry->email)->send(new ConfirmWaitlistMail(
            $confirmUrl,
            $event->unsubscribeUrl,
            $event->subscription->consents->pluck('text')->all(), // what is being confirmed
        ));

        Waitlist::confirmationMailed($event->subscription, 'confirm-mail@2026-10');
    }
}
  • The same event carries resends ($event->isNewCycle === false). Do not throttle in the listener; the package's cooldown and caps already did.
  • confirmationMailed() records the reference of the mail next to the consent; use a template name and version, and keep each version's text.
  • $event->confirmUrl is null while the project's urls() sets no confirm page and the package routes are off. A link that cannot be built (the routes are on but not registered, as after a stale route cache) throws InvalidConfigurationException and undoes the signup or confirmation: nothing is committed, so no event fires and no mail is due.

3. Preference page link listener

use Taldres\Waitlist\Events\ManageLinkRequested;

class SendManageLinkMail implements ShouldQueue, ShouldBeEncrypted
{
    public function handle(ManageLinkRequested $event): void
    {
        Mail::to($event->entry->email)->send(new ManageLinkMail($event->manageUrl, $event->expiresAt));
    }
}

The link expires after waitlist.manage.token_ttl minutes (60); say so in the mail.

4. Launch mails and newsletters: ask recipients()

use Taldres\Waitlist\Support\Recipient;

// one list, for its primary purpose: links leave that list
Waitlist::for('beta')->recipients()->each(
    fn (Recipient $recipient) => Mail::to($recipient->email)->locale($recipient->locale)->queue(new LaunchMail($recipient)),
);

// an optional purpose, project-wide, once per address: links withdraw only it
Waitlist::recipients('newsletter')->each(
    fn (Recipient $recipient) => Mail::to($recipient->email)->locale($recipient->locale)->queue(new NewsletterMail($recipient)),
);

In the Mailable, use the recipient's links:

use Illuminate\Mail\Mailables\Headers;

public function __construct(public Recipient $recipient) {}

public function headers(): Headers
{
    return new Headers(text: $this->recipient->listUnsubscribeHeaders()); // RFC 8058 one-click
}

// content: $this->recipient->unsubscribeUrl()

Recipient has entry, email, purpose, locale (the consent's language, or null) and leavesList. Queued Mailables carry the address and token links: let them implement ShouldBeEncrypted too. One-click headers need the package routes and an https APP_URL; without routes they are empty.

5. A link outside events

Waitlist::unsubscribeUrl($entry);                // leaves the list
Waitlist::unsubscribeUrl($entry, 'newsletter');  // withdraws only the newsletter
Waitlist::listUnsubscribeHeaders($entry, 'newsletter');

6. Several projects

The project is $event->entry->project (or $recipient->entry->project). Pick sender and mailer from it:

// config/products.php (the app's own): ['rocket' => ['name' => 'Rocket', 'from' => '[email protected]', 'mailer' => 'resend'], ...]
$product = config("products.{$event->entry->project}");

$mail = (new ConfirmWaitlistMail(...))->from($product['from'], $product['name']);
Mail::mailer($product['mailer'])->to($event->entry->email)->send($mail);

Mail links already point at each project's own pages (urls()).

Rules, References, and Templates

  • Every listener that sends mail implements ShouldQueue (identical response times for new and known addresses) and ShouldBeEncrypted (payloads hold tokens; EntryForgotten holds the address).
  • Laravel discovers listeners in app/Listeners by their handle() type hint; registering them again with Event::listen() sends every mail twice.
  • Events dispatch after commit; a dispatched event does not prove the mail went out, so monitor failed jobs.
  • Mail driver setup: https://laravel.com/docs/mail; send from a domain with SPF, DKIM and DMARC.
  • Full guide: https://github.com/Taldres/laravel-waitlist/blob/main/docs/mail.md

Examples

  • "Send the double opt-in mail with Resend": composer require resend/resend-php, MAIL_MAILER=resend, RESEND_KEY=..., then the listener from step 2.
  • "Email everyone on the beta list that we launched": step 4 with Waitlist::for('beta')->recipients().
  • "Send the monthly newsletter": Waitlist::recipients('newsletter'), one-click headers per recipient.

Anti-patterns

  • Mailing WaitlistEntry::all() or Waitlist::for($list)->entries() without a consent check; use recipients() or whereConsentedTo($purpose).
  • Putting Waitlist::manageLink($entry) or a manage URL into ordinary mails; it opens the person's data and mails get forwarded.
  • Sending the manage link anywhere but $event->entry->email, or answering the request with it.
  • A newsletter link that unsubscribes from the whole list: use unsubscribeUrl($entry, 'newsletter') or the Recipient's own link.
  • Synchronous mail listeners, or queued ones without ShouldBeEncrypted.

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

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.

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

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.

관련 스킬