CLI Commands
Courier ships three Spark commands. They're designed to run via cron — each one is stateless, processes a bounded batch, and logs its results.
courier:send-campaign
Sends all scheduled blast campaigns whose scheduled_at time has passed.
To send a specific campaign by ID (useful for testing or manual sends):
What it does:
- Finds all campaigns with status
scheduledandscheduled_at <= now(or just the one you specified) - Sets each campaign to
sending - Resolves the audience (all subscribers, or filtered by segment/tags)
- Sends emails in batches of
$batchSize - Marks the campaign
sentwhen done, orpausedif an exception occurs
A paused campaign can be resumed via CampaignService::resume() or by fixing the issue and calling courier:send-campaign <id> again.
Recommended cron schedule:
# Check for scheduled campaigns every 5 minutes
*/5 * * * * php /path/to/app/spark courier:send-campaign
courier:process-drips
Sends due drip sequence steps to enrolled contacts.
What it does:
- Finds active enrollments where
next_send_at <= now(up to$batchSize) - Sends the current step email to each contact
- Advances each enrollment to the next step (updating
next_send_atbased on the next step'sdelay_hours) - Marks the enrollment
completedwhen the contact finishes all steps - Cancels the enrollment if the contact is no longer subscribed
Recommended cron schedule:
Run this frequently so drip steps go out close to their scheduled time:
If your batch size is smaller than your active enrollment count, the queue drains across successive runs. That's by design — it prevents overwhelming your email provider in a single burst.
courier:track-events
A stub command for processing bounce webhooks or SMTP feedback loops.
Out of the box this command does nothing but log a warning. It's a placeholder — extend it when you're ready to handle bounce webhooks from your email provider:
<?php
// app/Commands/ProcessBounces.php
namespace App\Commands;
use Myth\Courier\Commands\TrackEvents;
use Myth\Courier\Models\ContactModel;
use Myth\Courier\Enums\ContactStatus;
class ProcessBounces extends TrackEvents
{
protected $name = 'courier:track-events'; // override the base command
protected function processBounce(string $email): void
{
$contact = model(ContactModel::class)->where('email', $email)->first();
if ($contact === null) { return; }
model(ContactModel::class)->update($contact->id, [
'status' => ContactStatus::Bounced->value,
]);
}
}
Logging
All three commands write to CI4's log system using the [Courier] prefix. Check your writable/logs/ directory if something isn't sending as expected.