NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Campaigns for Laravel

The Campaigns facade

The Campaigns facade over RoundlyConsulting\Campaigns\CampaignManager is the package’s public API and the recommended way in. It creates and sends campaigns, looks them up, and hands you a handle scoped to one campaign:

use RoundlyConsulting\Campaigns\Facades\Campaigns;

$campaign = Campaigns::create('Spring sale', '<p>50% off</p>')->to($subscribers)->prepare();

Campaigns::start($campaign->uuid);        // only a prepared (Pending) campaign starts
Campaigns::find($campaign->uuid);         // ?Campaign — live progress while it sends
Campaigns::findOrFail($uuid);             // Campaign, or CampaignNotFound
Campaigns::all(offset: 0, limit: 25);     // Collection<int, Campaign>, in creation order
Campaigns::cancel($uuid);                 // Campaign — one that already ended is left as it is

Campaigns::campaign($uuid)->progress()->percentage();   // one campaign, through its handle
Campaigns::settings()->fromAddress();                   // the effective send defaults

Every method

MethodReturnsPurpose
create(string $subject, string $content)PendingCampaignFluent builder — from, to, onlyVerified, viaContactType, uuid, subject, content, prepare, dispatch.
prepare(Campaign $campaign, iterable $recipients = [])CampaignStore a campaign built from the value object with its recipients, and leave it Pending. A uuid that is taken throws CampaignAlreadyExists.
start(Campaign|string $campaign)CampaignStart sending a Pending campaign; anything else throws InvalidCampaignTransition.
cancel(Campaign|string $campaign)CampaignCancel a campaign and its batch; one that already ended comes back unchanged.
find(string $uuid)?CampaignLook a campaign up, with live progress while it sends; null when unknown.
findOrFail(string $uuid)CampaignLook a campaign up or throw CampaignNotFound.
all(int $offset = 0, int $limit = 10)Collection<int, Campaign>A page of campaigns in creation order.
campaign(Campaign|string $campaign)CampaignHandleOne campaign — see the handle below.
settings()CampaignSettingsThe effective send defaults: fromName(), fromAddress(), notificationChannel(), sendingQueue(), onlyVerifiedRecipients(), defaultRecipientContactType().
fake()CampaignsFakeSwap in the recording fake — see Testing.

One campaign: the handle

Campaigns::campaign() takes a Campaign or a uuid and returns a CampaignHandle scoped to that campaign — an unknown uuid throws CampaignNotFound:

use RoundlyConsulting\Campaigns\Facades\Campaigns;

$campaign = Campaigns::campaign($uuid);   // throws CampaignNotFound for an unknown uuid

$campaign->progress()->percentage();      // share delivered so far: 42.5
$campaign->progress()->failed;            // deliveries that failed
$campaign->progress()->remaining();       // recipients with no outcome yet
$campaign->recipients();                  // Collection<CampaignRecipient>, in the order added
$campaign->recipients(offset: 100, limit: 50);
$campaign->recipient($recipientUuid);     // one recipient of THIS campaign
$campaign->batch();                       // the Illuminate\Bus\Batch, or null
$campaign->start();
$campaign->cancel();                      // a campaign that already ended is left as it is
MethodReturnsPurpose
uuid()stringThe campaign’s uuid.
get()CampaignThe campaign as stored now, with live progress.
progress()CampaignProgressLive progress — read from the recipients and the batch while Pending or Processing.
recipients(int $offset = 0, ?int $limit = null)Collection<int, CampaignRecipient>Recipients in the order they were added; a null limit returns them all.
recipient(CampaignRecipient|string $recipient)CampaignRecipientOne recipient of this campaign; RecipientNotFound otherwise.
batch()?BatchThe Laravel job batch — null before prepare() and under the fake.
start()CampaignSame as Campaigns::start().
cancel()CampaignSame as Campaigns::cancel().
markProcessed(CampaignRecipient $recipient)CampaignRecipientRecord a delivery and fire RecipientProcessed.
markFailed(CampaignRecipient $recipient, string $error)CampaignRecipientRecord a failure with its message and fire RecipientFailed.

The handle refuses recipients of other campaigns: recipient(), markProcessed() and markFailed() throw RecipientNotFound. markProcessed() and markFailed() are what delivery jobs call — see Custom delivery jobs.

Want the same API without static calls? See DI and actions.

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.