Skip to main content
The Newsletter feature provides methods for managing WhatsApp newsletter channels, including creation, subscription management, reactions, and live updates. Newsletter operations use MEX (GraphQL) for metadata/management and IQ stanzas for message operations. Newsletter message sending is handled by the unified client.send_message() method — see the Send API. Reactions remain on the Newsletter struct because they use a different stanza format.
Newsletter messages are plaintext — they are not encrypted with the Signal protocol.

Access

Access newsletter operations through the client:
let newsletter = client.newsletter();

Methods

list_subscribed

List all newsletters the user is subscribed to.
pub async fn list_subscribed(&self) -> Result<Vec<NewsletterMetadata>, MexError>
Returns:
  • Vec<NewsletterMetadata> — List of subscribed newsletters
Example:
let newsletters = client.newsletter().list_subscribed().await?;

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

get_metadata

Fetch metadata for a newsletter by its JID.
pub async fn get_metadata(&self, jid: &Jid) -> Result<NewsletterMetadata, MexError>
Parameters:
  • jid — Newsletter JID (server must be newsletter)
Returns:
  • NewsletterMetadata — Full newsletter metadata
Example:
let metadata = client.newsletter().get_metadata(&newsletter_jid).await?;

println!("Name: {}", metadata.name);
println!("Subscribers: {}", metadata.subscriber_count);
println!("Verified: {:?}", metadata.verification);

get_metadata_by_invite

Fetch metadata for a newsletter by its invite code.
pub async fn get_metadata_by_invite(
    &self,
    invite_code: &str,
) -> Result<NewsletterMetadata, MexError>
Parameters:
  • invite_code — Newsletter invite code string
Returns:
  • NewsletterMetadata — Full newsletter metadata
Example:
let metadata = client.newsletter()
    .get_metadata_by_invite("ABC123")
    .await?;

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

create

Create a new newsletter.
pub async fn create(
    &self,
    name: &str,
    description: Option<&str>,
) -> Result<NewsletterMetadata, MexError>
Parameters:
  • name — Newsletter name
  • description — Optional description
Returns:
  • NewsletterMetadata — Metadata of the newly created newsletter
Example:
let created = client.newsletter()
    .create("My Channel", Some("A description"))
    .await?;

println!("Created: {} ({})", created.name, created.jid);

join

Join (subscribe to) a newsletter.
pub async fn join(&self, jid: &Jid) -> Result<NewsletterMetadata, MexError>
Parameters:
  • jid — Newsletter JID to join
Returns:
  • NewsletterMetadata — Metadata with the viewer’s role set to Subscriber
Example:
let joined = client.newsletter().join(&newsletter_jid).await?;

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

leave

Leave (unsubscribe from) a newsletter.
pub async fn leave(&self, jid: &Jid) -> Result<(), MexError>
Parameters:
  • jid — Newsletter JID to leave
Example:
client.newsletter().leave(&newsletter_jid).await?;

update

Update a newsletter’s name and/or description.
pub async fn update(
    &self,
    jid: &Jid,
    name: Option<&str>,
    description: Option<&str>,
) -> Result<NewsletterMetadata, MexError>
Parameters:
  • jid — Newsletter JID
  • name — New name, or None to keep the current name
  • description — New description, or None to keep the current description
Returns:
  • NewsletterMetadata — Updated metadata
Example:
let updated = client.newsletter()
    .update(&newsletter_jid, Some("New Name"), None)
    .await?;

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

Sending messages

Newsletter message sending is handled by the unified client.send_message() method. See the Send API reference for full details.
use waproto::whatsapp as wa;

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

// Pass a newsletter JID directly to send_message
let msg_id = client.send_message(newsletter_jid, message).await?;
The library detects newsletter recipients automatically and sends messages as plaintext (no Signal encryption), with the correct type and mediatype stanza attributes inferred from the message content. Stanza-level <meta> nodes (for polls, events, etc.) are also included automatically, matching WhatsApp Web behavior.
Newsletter::send_message() was removed. Use client.send_message() instead — it accepts newsletter, group, and direct message JIDs.

send_reaction

Send a reaction to a newsletter message.
pub async fn send_reaction(
    &self,
    jid: &Jid,
    server_id: u64,
    reaction: &str,
) -> Result<(), anyhow::Error>
Parameters:
  • jid — Newsletter JID
  • server_id — Server-assigned ID of the message to react to
  • reaction — Emoji code (e.g., "👍", "❤️"), or empty string to remove
Example:
// Add a reaction
client.newsletter()
    .send_reaction(&newsletter_jid, server_id, "👍")
    .await?;

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

get_messages

Fetch message history from a newsletter.
pub async fn get_messages(
    &self,
    jid: &Jid,
    count: u32,
    before: Option<u64>,
) -> Result<Vec<NewsletterMessage>, anyhow::Error>
Parameters:
  • jid — Newsletter JID
  • count — Maximum number of messages to return
  • before — If set, return messages before this server_id (for pagination)
Returns:
  • Vec<NewsletterMessage> — List of newsletter messages
Example:
// Fetch latest 50 messages
let messages = client.newsletter()
    .get_messages(&newsletter_jid, 50, None)
    .await?;

// Paginate backwards
if let Some(oldest) = messages.last() {
    let older = client.newsletter()
        .get_messages(&newsletter_jid, 50, Some(oldest.server_id))
        .await?;
}

subscribe_live_updates

Subscribe to live updates for a newsletter (reaction counts, message changes).
pub async fn subscribe_live_updates(
    &self,
    jid: &Jid,
) -> Result<u64, anyhow::Error>
Parameters:
  • jid — Newsletter JID
Returns:
  • u64 — Subscription duration in seconds (typically 300)
The server sends Event::NewsletterLiveUpdate events with updated reaction counts. You need to re-subscribe periodically when the duration expires. Example:
let duration = client.newsletter()
    .subscribe_live_updates(&newsletter_jid)
    .await?;

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

Types

NewsletterMetadata

Metadata for a newsletter channel. Implements PartialEq and Eq for direct comparison.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NewsletterMetadata {
    pub jid: Jid,
    pub name: String,
    pub description: Option<String>,
    pub subscriber_count: u64,
    pub verification: NewsletterVerification,
    pub state: NewsletterState,
    pub picture_url: Option<String>,
    pub preview_url: Option<String>,
    pub invite_code: Option<String>,
    pub role: Option<NewsletterRole>,
    pub creation_time: Option<u64>,
}

NewsletterVerification

#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NewsletterVerification {
    Verified,
    Unverified,
}

NewsletterState

#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NewsletterState {
    Active,
    Suspended,
    Geosuspended,
}

NewsletterRole

The viewer’s role in a newsletter.
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NewsletterRole {
    Owner,
    Admin,
    Subscriber,
    Guest,
}
All newsletter enums are #[non_exhaustive], so match statements should include a wildcard arm to handle future variants.

NewsletterMessage

A message from a newsletter’s history.
pub struct NewsletterMessage {
    /// Server-assigned message ID (monotonic, used for pagination cursors).
    pub server_id: u64,
    /// Message timestamp (Unix seconds).
    pub timestamp: u64,
    /// Message type (Text, Media, Reaction, etc.).
    pub message_type: NewsletterMessageType,
    /// Whether the viewer is the sender.
    pub is_sender: bool,
    /// Decoded protobuf message (from plaintext bytes).
    pub message: Option<wa::Message>,
    /// Reaction counts on this message.
    pub reactions: Vec<NewsletterReactionCount>,
}

NewsletterMessageType

The type of a newsletter message. Uses a StringEnum for type-safe wire-protocol mapping. Implements PartialEq and Eq.
#[non_exhaustive]
pub enum NewsletterMessageType {
    Text,          // "text"
    Media,         // "media"
    Reaction,      // "reaction"
    Revoke,        // "revoke"
    PollCreation,  // "poll_creation"
    PollVote,      // "poll_vote"
    Edit,          // "edit"
    Other(String), // Unknown/future types
}
Methods:
  • as_str() - Returns the wire-protocol string representation
  • From<&str> - Parse from wire string, unknown values become Other(String)

NewsletterReactionCount

A reaction count on a newsletter message.
pub struct NewsletterReactionCount {
    pub code: String,
    pub count: u64,
}

Error handling

Metadata and management methods return MexError:
pub enum MexError {
    PayloadParsing(String),
    // ... other variants
}
Message operations (send_reaction, get_messages, subscribe_live_updates) return anyhow::Error. Newsletter message sending uses client.send_message() which also returns anyhow::Error.
match client.newsletter().create("Test", None).await {
    Ok(metadata) => println!("Created: {}", metadata.name),
    Err(e) => eprintln!("Failed: {}", e),
}