> ## 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.

# Campaigns

> List, inspect, send, schedule, and unschedule email campaigns with the Lettr Rust SDK, including engagement stats and events.

The `client.campaigns` service gives you read access to campaigns plus lifecycle actions — send now, schedule, and unschedule. Campaigns are **authored in the Lettr app**; the API does not expose create, update, or delete.

Reads require an API key with the `campaigns:read` scope; actions require `campaigns:write`.

```rust theme={null}
use lettr::Lettr;
use lettr::campaigns::{ListCampaignsOptions, ListCampaignEventsOptions, ScheduleCampaignOptions, CampaignStatus, CampaignEventType};

let client = Lettr::new("your-api-key");
```

## List Campaigns

```rust theme={null}
let response = client.campaigns.list(
    ListCampaignsOptions::new().status(CampaignStatus::Sent)
).await?;

for campaign in &response.campaigns {
    println!("{}: {:?} ({} sent)", campaign.name, campaign.status, campaign.sent_count);
}
println!("Page {} of {}", response.pagination.current_page, response.pagination.last_page);
```

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

## Get a Campaign

`get` returns a `CampaignDetail` with the rendered `html_content` and engagement `stats`:

```rust theme={null}
let campaign = client.campaigns.get("campaign-id").await?;
println!("Subject: {:?}", campaign.subject);
println!("Opens: {}, Clicks: {}", campaign.stats.unique_opens, campaign.stats.clicks);

if let Some(html) = &campaign.html_content {
    println!("HTML length: {}", html.len());
}
```

<Card title="API Reference" icon="book" href="/api-reference/campaigns/show-a-campaign">
  GET /campaigns/{campaignId}
</Card>

## List Campaign Events

Engagement events use cursor-based pagination. Keep requesting with the returned cursor until it is `None`:

```rust theme={null}
let mut options = ListCampaignEventsOptions::new().event_type(CampaignEventType::Click);
loop {
    let page = client.campaigns.list_events("campaign-id", options.clone()).await?;
    for event in &page.events {
        println!("{} {:?} {}", event.timestamp, event.event_type, event.email);
    }
    match page.next_cursor {
        Some(cursor) => options = options.cursor(cursor),
        None => break,
    }
}
```

<Card title="API Reference" icon="book" href="/api-reference/campaigns/list-campaign-engagement-events">
  GET /campaigns/{campaignId}/events
</Card>

## Send / Schedule / Unschedule

```rust theme={null}
// Send a draft campaign now (asynchronous; transitions to "preparing")
client.campaigns.send("campaign-id").await?;

// Schedule for future delivery — ISO 8601 with a timezone offset.
client.campaigns.schedule(
    "campaign-id",
    ScheduleCampaignOptions::new("2026-06-01T09:00:00+00:00"),
).await?;

// Cancel a scheduled send, returning the campaign to draft
client.campaigns.unschedule("campaign-id").await?;
```

The action methods return `Option<Campaign>` — the SDK returns `Some` with the updated campaign, or `None` if the API omits the payload.

<CardGroup cols={3}>
  <Card title="Send" icon="paper-plane" href="/api-reference/campaigns/send-a-campaign-now">
    POST /campaigns/{id}/send
  </Card>

  <Card title="Schedule" icon="clock" href="/api-reference/campaigns/schedule-a-campaign">
    POST /campaigns/{id}/schedule
  </Card>

  <Card title="Unschedule" icon="ban" href="/api-reference/campaigns/unschedule-a-campaign">
    POST /campaigns/{id}/unschedule
  </Card>
</CardGroup>

## What's Next

<CardGroup cols={2}>
  <Card title="Audience" icon="users" href="/quickstart/rust/audience">
    Manage the contacts campaigns send to
  </Card>

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