Skip to content

Contacts

Contacts are the people in your mailing list. Each contact has a status, optional tags, and an auto-generated unsubscribe token. The ContactService handles the full lifecycle.

Get the service via CI4's service locator:

<?php
$contactService = service('contactService');

Subscribing a contact

<?php
$contact = $contactService->subscribe([
    'email'      => 'ada@example.com',
    'first_name' => 'Ada',
    'last_name'  => 'Lovelace',
]);

subscribe() returns a ContactDTO. It either creates a new contact or, if the email already exists and the contact was previously unsubscribed, re-subscribes them.

Adding tags at subscribe time

Pass a list of tag slugs as the second argument:

<?php
$contact = $contactService->subscribe(
    ['email' => 'ada@example.com'],
    tags: ['newsletter', 'vip']
);

Tags are created automatically if they don't exist yet.

Enrolling in a drip on subscribe

Pass a dripCampaignId to automatically enroll the contact in a drip sequence after subscribing:

<?php
$contact = $contactService->subscribe(
    ['email' => 'ada@example.com'],
    dripCampaignId: 3
);

Contact statuses

Status Meaning
subscribed Active — will receive emails
unsubscribed Opted out — can be re-subscribed
bounced Hard bounce — cannot be re-subscribed
complained Marked as spam — cannot be re-subscribed

Bounced and complained contacts

Calling subscribe() for a contact with bounced or complained status throws a CourierValidationException. These statuses require manual review before re-engagement.

Unsubscribing

Courier handles unsubscribes automatically via the tracking controller when a contact clicks their unsubscribe link. You can also unsubscribe programmatically by token:

<?php
$success = $contactService->unsubscribeByToken($token);

Unsubscribing also cancels all active drip enrollments for that contact.

Managing tags

Applying tags

<?php
$contactService->applyTags($contact->id, ['beta-tester', 'onboarding']);

Tags that don't exist are created on the fly. Tags already applied are silently skipped — it's safe to call this multiple times.

Removing tags

<?php
$contactService->removeTags($contact->id, ['onboarding']);

Tags that aren't applied are silently ignored.

Looking up a contact

<?php
$contact = $contactService->getContact('ada@example.com');
// Returns ContactDTO or null

Custom fields

Contacts support a custom_fields JSON column for arbitrary data. Pass these in the data array when subscribing:

<?php
$contactService->subscribe([
    'email'         => 'ada@example.com',
    'custom_fields' => json_encode(['plan' => 'pro', 'signup_source' => 'homepage']),
]);

Custom fields can also be used in segment rules.