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:
let newsletter = client.newsletter();
See Newsletter API reference for the full API.
Creating a newsletter
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 for details.
Listing subscribed newsletters
let newsletters = client.newsletter().list_subscribed().await?;
for nl in &newsletters {
println!("{}: {} ({} subscribers)",
nl.jid, nl.name, nl.subscriber_count);
}
See Newsletter API reference for details.
By JID
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
let metadata = client.newsletter()
.get_metadata_by_invite("invite-code-here")
.await?;
println!("Found: {} ({})", metadata.name, metadata.jid);
See Newsletter API reference for all metadata fields.
Joining and leaving
Join a newsletter
let joined = client.newsletter().join(&newsletter_jid).await?;
println!("Joined '{}' as {:?}", joined.name, joined.role);
Leave a newsletter
client.newsletter().leave(&newsletter_jid).await?;
See Newsletter API reference for details.
Updating a newsletter
You can update the name and/or description of a newsletter you own:
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 for details.
Sending messages
Newsletter messages are plaintext — they bypass Signal encryption entirely.
Text messages
use waproto::whatsapp as wa;
let message = wa::Message {
conversation: Some("Hello subscribers!".to_string()),
..Default::default()
};
let msg_id = client.newsletter()
.send_message(&newsletter_jid, &message)
.await?;
println!("Sent message: {}", msg_id);
For media messages (images, videos, etc.), you must upload the media separately using the newsletter-specific upload endpoint before sending. Text messages work directly.
Reactions
Send a reaction to a specific newsletter message using its server_id:
// 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 Newsletter API reference for details.
Fetching message history
Retrieve past messages with pagination support:
// 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);
}
}
Use the server_id from a previous response to paginate backwards:
// 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 for details.
Live updates
Subscribe to real-time updates for a newsletter to receive reaction count changes:
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:
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<_>>()
);
}
}
The subscription duration (typically 300 seconds) is returned by the server. You need to re-subscribe periodically to continue receiving live updates.
See Events reference for the full event type.
Error handling
Newsletter operations return MexError for metadata/management operations and anyhow::Error for message operations:
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