> ## 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 contacts, lists, subscription topics, custom properties, and dynamic segments with the client.audience resource in the Lettr Python SDK.

The `client.audience` resource manages everything campaigns send to. Each kind is a sub-resource:

```python theme={null}
import lettr

client = lettr.Lettr("lttr_your_api_key")

client.audience.contacts    # individual contacts
client.audience.lists       # contact lists
client.audience.topics      # subscription topics
client.audience.properties  # custom contact properties
client.audience.segments    # dynamic segments
```

## Contacts

```python theme={null}
# Create (optionally attach to a list and set properties)
contact = client.audience.contacts.create(
    email="jane@example.com",
    list_id="list-uuid",
    properties={"first_name": "Jane", "plan": "pro"},
)
print(contact.id, contact.email, contact.status)

# List with filters
page = client.audience.contacts.list(
    page=1,
    per_page=50,
    search="jane",
    status="subscribed",
    list_id="list-uuid",
)
for c in page.contacts:
    print(c.email, c.status)

# Get, update, delete
client.audience.contacts.get("contact-uuid")
client.audience.contacts.update(
    "contact-uuid",
    status="unsubscribed",
    properties={"plan": "enterprise"},
)
client.audience.contacts.delete("contact-uuid")
```

Contact `status` is `"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 `double_opt_in` config to create the contact as `unverified` and send a confirmation email — they become `subscribed` after clicking the link:

```python theme={null}
client.audience.contacts.create(
    email="jane@example.com",
    double_opt_in={
        "from": "hello@example.com",
        "subject": "Confirm your subscription",
        "template_slug": "email-confirmation",
        "redirect_url": "https://example.com/confirmed",
    },
)
```

### Bulk operations & membership

```python theme={null}
# Bulk create
client.audience.contacts.bulk_create(
    emails=["a@example.com", "b@example.com"],
    list_id="list-uuid",
)

# Single list / topic membership
client.audience.contacts.add_to_list(contact_id="contact-uuid", list_id="list-uuid")
client.audience.contacts.remove_from_list(contact_id="contact-uuid", list_id="list-uuid")
client.audience.contacts.subscribe_to_topic(contact_id="contact-uuid", topic_id="topic-uuid")
client.audience.contacts.unsubscribe_from_topic(contact_id="contact-uuid", topic_id="topic-uuid")

# Bulk list membership
client.audience.contacts.bulk_attach_lists(
    contact_ids=["c1", "c2"],
    list_ids=["l1", "l2"],
)
client.audience.contacts.bulk_detach_lists(contact_ids=["c1"], list_ids=["l1"])
```

## Lists

```python theme={null}
audience_list = client.audience.lists.create(name="Newsletter")
print(audience_list.id, audience_list.name, audience_list.contacts_count)

client.audience.lists.list(page=1, per_page=20)
client.audience.lists.get("list-uuid")
client.audience.lists.update("list-uuid", name="Weekly digest")
client.audience.lists.delete("list-uuid")

# Bulk delete
client.audience.lists.bulk_delete(list_ids=["l1", "l2"])
```

<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**; conditions within a group by **AND**. Conditions are a plain dict matching the API schema:

```python theme={null}
client.audience.segments.create(
    name="Pro users at example.com",
    list_id="list-uuid",  # optional: restrict to one list
    conditions={
        "groups": [
            {
                "conditions": [
                    {"field": "email", "operator": "ends_with", "value": "@example.com"},
                    {"field": "plan", "operator": "equals", "value": "pro"},
                ],
            },
        ],
    },
)

client.audience.segments.list(list_id="list-uuid")
client.audience.segments.get("segment-uuid")
client.audience.segments.delete("segment-uuid")
```

Operators include `equals`, `not_equals`, `contains`, `starts_with`, `ends_with`, `is_true`, `is_false`, and others.

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

## Topics

```python theme={null}
client.audience.topics.create(
    name="Product updates",
    description="Occasional product news",
    default_subscription="opt_in",  # "opt_in" | "opt_out" — immutable after creation
    visibility="public",            # "public" | "private"
)

client.audience.topics.list(per_page=20)
client.audience.topics.get("topic-uuid")
client.audience.topics.update("topic-uuid", name="Product news", visibility="private")
client.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` and `type`; only the fallback can be updated.

```python theme={null}
client.audience.properties.create(
    name="plan",
    type="string",          # "string" | "number" | "boolean" | "date" | "json"
    fallback_value="free",
)

client.audience.properties.list(page=1)
client.audience.properties.get("property-uuid")
client.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/python/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>
