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

# Newsletters (channels)

> Learn how to create, manage, and interact with WhatsApp newsletter channels in whatsapp-rust

## Overview

Newsletters (also called channels) are broadcast-style messaging in WhatsApp. Unlike groups, newsletter messages are **plaintext** (no Signal E2E encryption) and flow one-way from admins to subscribers.

This guide covers creating newsletters, managing subscriptions, sending messages, and handling live updates.

## Accessing the Newsletter API

All newsletter operations are accessed through the `newsletter()` method:

```rust theme={null}
let newsletter = client.newsletter();
```

See [Newsletter API reference](/api/newsletter) for the full API.

## Creating a newsletter

```rust theme={null}
let created = client.newsletter()
    .create("My Channel", Some("Channel description"))
    .await?;

println!("Created: {} ({})", created.name, created.jid);
println!("Invite code: {:?}", created.invite_code);
```

The returned `NewsletterMetadata` contains the channel's JID (with `@newsletter` server), name, subscriber count, and invite code.

See [Newsletter API reference](/api/newsletter#create) for details.

## Listing subscribed newsletters

```rust theme={null}
let newsletters = client.newsletter().list_subscribed().await?;

for nl in &newsletters {
    println!("{}: {} ({} subscribers)",
        nl.jid, nl.name, nl.subscriber_count);
}
```

See [Newsletter API reference](/api/newsletter#list_subscribed) for details.

## Fetching metadata

### By JID

```rust theme={null}
let metadata = client.newsletter().get_metadata(&newsletter_jid).await?;

println!("Name: {}", metadata.name);
println!("Description: {:?}", metadata.description);
println!("Subscribers: {}", metadata.subscriber_count);
println!("Verification: {:?}", metadata.verification);
println!("State: {:?}", metadata.state);
```

### By invite code

```rust theme={null}
let metadata = client.newsletter()
    .get_metadata_by_invite("invite-code-here")
    .await?;

println!("Found: {} ({})", metadata.name, metadata.jid);
```

See [Newsletter API reference](/api/newsletter#get_metadata) for all metadata fields.

## Joining and leaving

### Join a newsletter

```rust theme={null}
let joined = client.newsletter().join(&newsletter_jid).await?;

println!("Joined '{}' as {:?}", joined.name, joined.role);
```

### Leave a newsletter

```rust theme={null}
client.newsletter().leave(&newsletter_jid).await?;
```

See [Newsletter API reference](/api/newsletter#join) for details.

## Updating a newsletter

You can update the name and/or description of a newsletter you own:

```rust theme={null}
let updated = client.newsletter()
    .update(
        &newsletter_jid,
        Some("New Channel Name"),
        Some("Updated description"),
    )
    .await?;

println!("Updated: {}", updated.name);
```

Pass `None` for fields you don't want to change.

See [Newsletter API reference](/api/newsletter#update) for details.

## Sending messages

Newsletter messages are plaintext — they bypass Signal encryption entirely. You send newsletter messages through the unified `client.send_message()` method, just like regular and group messages.

### Text messages

```rust theme={null}
use waproto::whatsapp as wa;

let message = wa::Message {
    conversation: Some("Hello subscribers!".to_string()),
    ..Default::default()
};

let msg_id = client.send_message(newsletter_jid, message).await?;

println!("Sent message: {}", msg_id);
```

The library automatically detects that the recipient is a newsletter JID and sends the message as plaintext (no Signal encryption). It also infers the correct `type` attribute (text, media, reaction, poll) and `mediatype` attribute from the message content. Stanza-level `<meta>` nodes (for polls, events, etc.) are also injected automatically, matching WhatsApp Web behavior.

<Note>
  For media messages (images, videos, etc.), you must upload the media separately using the newsletter-specific upload endpoint before sending. Text messages work directly.
</Note>

### Reactions

Send a reaction to a specific newsletter message using its `server_id`:

```rust theme={null}
// React with thumbs up
client.newsletter()
    .send_reaction(&newsletter_jid, server_id, "👍")
    .await?;

// Remove reaction (empty string)
client.newsletter()
    .send_reaction(&newsletter_jid, server_id, "")
    .await?;
```

See [Send API reference](/api/send#send_message) for details.

### Editing and revoking messages

Channels use a plaintext edit/revoke flow that's separate from DM and group messages. Call the helpers on `client.newsletter()` and pass the original message's `message_id` (the wire stanza id returned by `send_message`, **not** the `server_id` used for reactions):

```rust theme={null}
use waproto::whatsapp as wa;

// Edit a text message
let new_body = wa::Message {
    conversation: Some("Updated announcement".to_string()),
    ..Default::default()
};

client.newsletter()
    .edit_message(&newsletter_jid, message_id.clone(), new_body)
    .await?;

// Revoke (delete) it
client.newsletter()
    .revoke_message(&newsletter_jid, message_id)
    .await?;
```

<Warning>
  Don't call `Client::edit_message` or `Client::revoke_message` with a newsletter JID. The E2E send path now rejects channel JIDs outright (including from `pin_message`) so you get a clear error rather than a malformed encrypted stanza. Always go through `client.newsletter()` for channel edits and revokes.
</Warning>

## Fetching message history

Retrieve past messages with pagination support:

```rust theme={null}
// Fetch the latest 50 messages
let messages = client.newsletter()
    .get_messages(&newsletter_jid, 50, None)
    .await?;

for msg in &messages {
    println!("ID: {}, time: {}, type: {}",
        msg.server_id, msg.timestamp, msg.message_type);
    
    if let Some(decoded) = &msg.message {
        if let Some(text) = &decoded.conversation {
            println!("  Text: {}", text);
        }
    }
    
    for reaction in &msg.reactions {
        println!("  {} x{}", reaction.code, reaction.count);
    }
}
```

### Pagination

Use the `server_id` from a previous response to paginate backwards:

```rust theme={null}
// Get messages before a specific server_id
let older = client.newsletter()
    .get_messages(&newsletter_jid, 20, Some(last_server_id))
    .await?;
```

See [Newsletter API reference](/api/newsletter#get_messages) for details.

## Live updates

Subscribe to real-time updates for a newsletter to receive reaction count changes:

```rust theme={null}
let duration = client.newsletter()
    .subscribe_live_updates(&newsletter_jid)
    .await?;

println!("Subscribed for {}s", duration);
```

The server sends `NewsletterLiveUpdate` events with updated reaction counts. Handle them in your event handler:

```rust theme={null}
use wacore::types::events::Event;

Event::NewsletterLiveUpdate(update) => {
    println!("Newsletter {} updated:", update.newsletter_jid);
    for msg in &update.messages {
        println!("  Message {}: {:?}", msg.server_id,
            msg.reactions.iter()
                .map(|r| format!("{} x{}", r.code, r.count))
                .collect::<Vec<_>>()
        );
    }
}
```

<Note>
  The subscription duration (typically 300 seconds) is returned by the server. You need to re-subscribe periodically to continue receiving live updates.
</Note>

See [Events reference](/concepts/events#newsletterliveupdate) for the full event type.

## Error handling

Newsletter operations return `MexError` for metadata/management operations and `anyhow::Error` for message operations:

```rust theme={null}
use whatsapp_rust::features::mex::MexError;

match client.newsletter().get_metadata(&jid).await {
    Ok(metadata) => println!("Found: {}", metadata.name),
    Err(MexError::PayloadParsing(msg)) => {
        eprintln!("Parse error: {}", msg);
    }
    Err(e) => eprintln!("Error: {}", e),
}
```

## Key differences from groups

| Feature     | Newsletters               | Groups      |
| ----------- | ------------------------- | ----------- |
| Encryption  | Plaintext                 | Signal E2E  |
| Messaging   | One-way (admins only)     | All members |
| JID server  | `@newsletter`             | `@g.us`     |
| Reactions   | Emoji counts (aggregated) | Per-message |
| API backend | MEX (GraphQL)             | IQ stanzas  |

## Next steps

* [Newsletter API reference](/api/newsletter) — Full API details and types
* [Events](/concepts/events) — Handle newsletter live update events
* [Sending messages](/guides/sending-messages) — Send messages to groups and contacts
* [MEX API](/api/mex) — Understand the GraphQL layer used by newsletters
