> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lettr.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Audience

> Manage contact lists, contacts, subscription topics, custom properties, and dynamic segments from Laravel with the Lettr audience API

The audience API manages everything campaigns send to: contact **lists**, the **contacts** themselves, subscription **topics**, custom **properties**, and dynamic **segments**. Everything is reached through `Lettr::audience()`:

```php theme={null}
use Lettr\Laravel\Facades\Lettr;

Lettr::audience()->contacts();    // individual contacts
Lettr::audience()->lists();       // contact lists
Lettr::audience()->segments();    // dynamic segments
Lettr::audience()->topics();      // subscription topics
Lettr::audience()->properties();  // custom contact properties
```

All `list()` endpoints are paginated and return a response object exposing the data collection, a `pagination` object, and a `hasMore()` helper.

## Contacts

A contact is an email address with optional custom properties, optionally attached to a list on creation.

```php theme={null}
use Lettr\Dto\Audience\CreateAudienceContactData;
use Lettr\Dto\Audience\UpdateAudienceContactData;
use Lettr\Dto\Audience\ListAudienceContactsFilter;
use Lettr\Enums\AudienceContactStatus;

// Create (optionally attach to a list and set custom properties)
$contact = Lettr::audience()->contacts()->create(new CreateAudienceContactData(
    email: 'jane@example.com',
    listId: 'list-uuid',
    properties: ['first_name' => 'Jane', 'plan' => 'pro'],
));

$contact->id;     // "contact-uuid"
$contact->email;  // "jane@example.com"
$contact->status; // AudienceContactStatus::Subscribed

// List with filtering and pagination
$response = Lettr::audience()->contacts()->list(
    ListAudienceContactsFilter::create()
        ->page(1)
        ->perPage(50)
        ->search('jane')
        ->status(AudienceContactStatus::Subscribed)
        ->listId('list-uuid')
);

foreach ($response->contacts as $contact) {
    // ...
}
$response->pagination->total;
$response->hasMore();

// Get, update, delete
$contact = Lettr::audience()->contacts()->get('contact-uuid');

Lettr::audience()->contacts()->update('contact-uuid', new UpdateAudienceContactData(
    status: AudienceContactStatus::Unsubscribed,
    properties: ['plan' => 'enterprise'],
));

Lettr::audience()->contacts()->delete('contact-uuid');
```

Contact `status` is one of `Subscribed`, `Unsubscribed`, `Bounced`, `Complained`, or `Unverified`.

<Card title="API Reference" icon="book" href="/api-reference/audience/list-audience-contacts">
  GET /audience/contacts
</Card>

### Double opt-in

Pass a `DoubleOptInConfig` to create the contact as `unverified` and trigger a confirmation email — the contact becomes `subscribed` once they click the link.

```php theme={null}
use Lettr\Dto\Audience\CreateAudienceContactData;
use Lettr\Dto\Audience\DoubleOptInConfig;

Lettr::audience()->contacts()->create(new CreateAudienceContactData(
    email: 'jane@example.com',
    doubleOptIn: new DoubleOptInConfig(
        from: 'hello@example.com',
        subject: 'Confirm your subscription',
        templateSlug: 'email-confirmation',
        redirectUrl: 'https://example.com/confirmed',
        fromName: 'Example',          // optional
    ),
));
```

### Bulk operations & membership

```php theme={null}
use Lettr\Dto\Audience\BulkCreateAudienceContactsData;
use Lettr\Dto\Audience\BulkAttachContactsToListsData;
use Lettr\Dto\Audience\BulkDetachContactsFromListsData;

// Bulk create (up to 1000 emails)
$result = Lettr::audience()->contacts()->bulkCreate(new BulkCreateAudienceContactsData(
    emails: ['a@example.com', 'b@example.com'],
    listId: 'list-uuid',
));
$result->created;        // int
$result->alreadyExisted; // int

// Attach / detach a single contact to a list (true if newly attached)
$attached = Lettr::audience()->contacts()->attachList('contact-uuid', 'list-uuid');
Lettr::audience()->contacts()->detachList('contact-uuid', 'list-uuid');

// Bulk attach / detach (up to 1000 contacts × 50 lists)
$result = Lettr::audience()->contacts()->bulkAttachLists(new BulkAttachContactsToListsData(
    contactIds: ['contact-1', 'contact-2'],
    listIds: ['list-1', 'list-2'],
));
$result->attached;        // int
$result->alreadyAttached; // int
$result->totalPairs;      // int

Lettr::audience()->contacts()->bulkDetachLists(new BulkDetachContactsFromListsData(
    contactIds: ['contact-1'],
    listIds: ['list-1'],
));

// Subscribe / unsubscribe a contact to a topic (true if newly subscribed)
Lettr::audience()->contacts()->subscribeTopic('contact-uuid', 'topic-uuid');
Lettr::audience()->contacts()->unsubscribeTopic('contact-uuid', 'topic-uuid');
```

## Lists

A list is a static collection of contacts.

```php theme={null}
use Lettr\Dto\Audience\CreateAudienceListData;
use Lettr\Dto\Audience\UpdateAudienceListData;
use Lettr\Dto\Audience\BulkDeleteAudienceListsData;
use Lettr\Dto\Audience\ListAudienceListsFilter;

$list = Lettr::audience()->lists()->create(new CreateAudienceListData(name: 'Newsletter'));
$list->id;
$list->name;
$list->contactsCount;

$response = Lettr::audience()->lists()->list(
    ListAudienceListsFilter::create()->page(1)->perPage(20)
);

$list = Lettr::audience()->lists()->get('list-uuid');

Lettr::audience()->lists()->update('list-uuid', new UpdateAudienceListData(name: 'Weekly digest'));

Lettr::audience()->lists()->delete('list-uuid');

// Bulk delete (up to 50)
$result = Lettr::audience()->lists()->bulkDelete(new BulkDeleteAudienceListsData(
    listIds: ['list-1', 'list-2'],
));
$result->deleted; // int
```

<Card title="API Reference" icon="book" href="/api-reference/audience/list-audience-lists">
  GET /audience/lists
</Card>

## Segments

A segment is a dynamic group defined by conditions. Groups are joined by **OR**, and conditions within a group are joined by **AND**.

```php theme={null}
use Lettr\Dto\Audience\CreateAudienceSegmentData;
use Lettr\Dto\Audience\UpdateAudienceSegmentData;
use Lettr\Dto\Audience\ListAudienceSegmentsFilter;
use Lettr\Dto\Audience\SegmentConditionsInput;
use Lettr\Dto\Audience\SegmentConditionGroup;
use Lettr\Dto\Audience\SegmentCondition;
use Lettr\Enums\SegmentOperator;

$conditions = new SegmentConditionsInput(groups: [
    new SegmentConditionGroup(conditions: [
        new SegmentCondition('email', SegmentOperator::EndsWith, '@example.com'),
        new SegmentCondition('plan', SegmentOperator::Equals, 'pro'),
    ]),
]);

$segment = Lettr::audience()->segments()->create(new CreateAudienceSegmentData(
    name: 'Pro users at example.com',
    conditions: $conditions,
    listId: 'list-uuid', // optional: restrict to one list (null = all lists)
));

$response = Lettr::audience()->segments()->list(
    ListAudienceSegmentsFilter::create()->listId('list-uuid')
);

$segment = Lettr::audience()->segments()->get('segment-uuid');

// The update DTO uses a builder — only the touched fields are sent
Lettr::audience()->segments()->update('segment-uuid', UpdateAudienceSegmentData::empty()
    ->withName('Renamed segment')
    ->withConditions($conditions));

Lettr::audience()->segments()->delete('segment-uuid');
```

Operators that don't take a value (`SegmentOperator::IsTrue`, `SegmentOperator::IsFalse`) report this via `requiresValue()`.

<Card title="API Reference" icon="book" href="/api-reference/audience/list-audience-segments">
  GET /audience/segments
</Card>

## Topics

Topics are subscription categories contacts can opt in or out of.

```php theme={null}
use Lettr\Dto\Audience\CreateAudienceTopicData;
use Lettr\Dto\Audience\UpdateAudienceTopicData;
use Lettr\Dto\Audience\ListAudienceTopicsFilter;
use Lettr\Enums\AudienceTopicDefaultSubscription;
use Lettr\Enums\AudienceTopicVisibility;

$topic = Lettr::audience()->topics()->create(new CreateAudienceTopicData(
    name: 'Product updates',
    description: 'Occasional product news',
    defaultSubscription: AudienceTopicDefaultSubscription::OptIn, // immutable after creation
    visibility: AudienceTopicVisibility::PublicVisibility,
));

$response = Lettr::audience()->topics()->list(
    ListAudienceTopicsFilter::create()->perPage(20)
);

$topic = Lettr::audience()->topics()->get('topic-uuid');

// default_subscription cannot be changed after creation
Lettr::audience()->topics()->update('topic-uuid', new UpdateAudienceTopicData(
    name: 'Product news',
    visibility: AudienceTopicVisibility::PrivateVisibility,
));

Lettr::audience()->topics()->delete('topic-uuid');
```

<Card title="API Reference" icon="book" href="/api-reference/audience/list-audience-topics">
  GET /audience/topics
</Card>

## Properties

Custom contact properties have an immutable `name` (must match `^[a-z][a-z0-9_]*$`) and an immutable `type`. Only the fallback value can be updated.

```php theme={null}
use Lettr\Dto\Audience\CreateAudiencePropertyData;
use Lettr\Dto\Audience\UpdateAudiencePropertyData;
use Lettr\Dto\Audience\ListAudiencePropertiesFilter;
use Lettr\Enums\AudiencePropertyType;

$property = Lettr::audience()->properties()->create(new CreateAudiencePropertyData(
    name: 'plan',
    type: AudiencePropertyType::StringType,  // StringType | NumberType | BooleanType | DateType | JsonType
    fallbackValue: 'free',
));

$response = Lettr::audience()->properties()->list(
    ListAudiencePropertiesFilter::create()->page(1)
);

$property = Lettr::audience()->properties()->get('property-uuid');

// Only the fallback can change — use the named constructors
Lettr::audience()->properties()->update('property-uuid', UpdateAudiencePropertyData::withFallback('basic'));
Lettr::audience()->properties()->update('property-uuid', UpdateAudiencePropertyData::clearFallback());

Lettr::audience()->properties()->delete('property-uuid');
```

<Card title="API Reference" icon="book" href="/api-reference/audience/list-audience-properties">
  GET /audience/properties
</Card>

## What's Next

<CardGroup cols={2}>
  <Card title="Campaigns" icon="paper-plane" href="/quickstart/laravel/campaigns">
    Send to your audience
  </Card>

  <Card title="API Reference" icon="book" href="/api-reference/audience/list-audience-lists">
    Full audience API reference
  </Card>
</CardGroup>
