Campaigns
A campaign is an email (or series of emails) sent to a set of contacts. Courier has two campaign types:
- Blast — a single email sent to an audience at a scheduled time
- Drip sequence — a series of emails sent to individually-enrolled contacts on a delay schedule
This page covers blast campaigns. For drip sequences, see Drip Sequences.
Creating a campaign
<?php
$campaignService = service('campaignService');
$campaign = $campaignService->create([
'name' => 'May Newsletter',
'subject' => 'What\'s new this month',
'from_name' => 'Acme Team',
'from_email' => 'hello@acme.com',
'view' => 'App\Views\emails\may_newsletter',
'type' => 'blast',
]);
Required fields: name, subject, from_name, from_email. The type defaults to blast if omitted. The campaign is created in draft status.
Targeting your audience
By default, a campaign sends to all subscribed contacts. You can narrow the audience three ways:
By segment
Assign a saved segment by ID:
By tags
Pass a JSON-encoded array of tag slugs — only contacts with all listed tags will receive the email:
Combining segment + tags
Set both segment_id and tag_filter to intersect them — contacts must be in the segment and have all the specified tags.
Segments
Segments are saved rule sets that filter the contact list. A segment can match contacts by any contact column, tag, or custom field.
Rule structure
Each rule is an object with field, op, and value:
{
"rules": [
{ "field": "status", "op": "eq", "value": "subscribed" },
{ "field": "subscribed_at","op": "gte", "value": "2025-01-01" }
],
"match_mode": "all"
}
Supported fields:
| Field | Description |
|---|---|
email |
Contact's email address |
first_name / last_name |
Name fields |
status |
Contact status (subscribed, unsubscribed, etc.) |
source |
How the contact was acquired |
subscribed_at / unsubscribed_at |
Subscription timestamps |
tag |
Has (or doesn't have) a specific tag slug |
custom:<key> |
A key inside custom_fields JSON (MySQL/SQLite only) |
Operators: eq, neq, gt, gte, lt, lte
match_mode: all (AND logic) or any (OR logic)
Previewing segment size
<?php
$segmentService = service('segmentService');
$count = $segmentService->previewCount($segmentId);
Scheduling a campaign
Once your campaign is in draft and has a view set, schedule it for delivery:
<?php
$sendAt = new DateTime('2025-06-01 09:00:00');
$campaignService->schedule($campaign->id, $sendAt);
The campaign moves to scheduled status. The courier:send-campaign command picks it up when the scheduled time arrives.
Campaign lifecycle
A paused campaign can be resumed:
This moves it back to scheduled so the next command run picks it up.
Checking campaign stats
<?php
$stats = $campaignService->getCampaignStats($campaign->id);
// Returns:
// [
// 'total' => 1500,
// 'sent' => 1487,
// 'failed' => 13,
// 'opened' => 423,
// 'clicked' => 89,
// ]
Open and click counts depend on the tracking controller being set up.