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

# Presence

> Online/offline status and presence subscription operations

The `Presence` struct provides methods for managing your online/offline status and subscribing to contact presence updates.

## Access

Access presence operations through the client:

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

## Methods

### set

Set your presence status (online or offline).

```rust theme={null}
pub async fn set(&self, status: PresenceStatus) -> Result<(), PresenceError>
```

**Parameters:**

* `status: PresenceStatus` - Either `Available` (online) or `Unavailable` (offline)

**Requirements:**

* Push name must be set before sending presence
* Returns error if push name is empty

**Example:**

```rust theme={null}
use whatsapp_rust::features::presence::PresenceStatus;

// Set status to online
client.presence().set(PresenceStatus::Available).await?;

// Set status to offline
client.presence().set(PresenceStatus::Unavailable).await?;
```

### set\_available

Convenience method to set status to available (online).

```rust theme={null}
pub async fn set_available(&self) -> Result<(), PresenceError>
```

**Example:**

```rust theme={null}
client.presence().set_available().await?;
println!("Now online");
```

### set\_unavailable

Convenience method to set status to unavailable (offline).

```rust theme={null}
pub async fn set_unavailable(&self) -> Result<(), PresenceError>
```

**Example:**

```rust theme={null}
client.presence().set_unavailable().await?;
println!("Now offline");
```

### subscribe

Subscribe to a contact's presence updates.

```rust theme={null}
pub async fn subscribe(&self, jid: &Jid) -> Result<(), PresenceError>
```

**Parameters:**

* `jid` - Contact JID to subscribe to

**Behavior:**

* Sends a `<presence type="subscribe">` stanza
* Automatically includes TC token if available for the contact
* Tracks the subscription internally so it can be restored on reconnect
* Used to receive notifications when the contact goes online/offline

**Example:**

```rust theme={null}
let contact_jid: Jid = "15551234567@s.whatsapp.net".parse()?;
client.presence().subscribe(&contact_jid).await?;
println!("Subscribed to {}'s presence", contact_jid);
```

### unsubscribe

Unsubscribe from a contact's presence updates.

```rust theme={null}
pub async fn unsubscribe(&self, jid: &Jid) -> Result<(), PresenceError>
```

**Parameters:**

* `jid` - Contact JID to unsubscribe from

**Behavior:**

* Sends a `<presence type="unsubscribe">` stanza
* Removes the contact from the internal subscription tracker
* You will no longer receive presence updates for this contact

**Example:**

```rust theme={null}
let contact_jid: Jid = "15551234567@s.whatsapp.net".parse()?;
client.presence().unsubscribe(&contact_jid).await?;
println!("Unsubscribed from {}'s presence", contact_jid);
```

## PresenceStatus Enum

```rust theme={null}
#[non_exhaustive]
pub enum PresenceStatus {
    Available,    // Online
    Unavailable,  // Offline
}
```

`PresenceStatus` is `#[non_exhaustive]`, so match statements should include a wildcard arm to handle future variants.

**Methods:**

* `as_str()` - Returns `"available"` or `"unavailable"`

**Conversion:**

```rust theme={null}
let status = PresenceStatus::Available;
assert_eq!(status.as_str(), "available");
```

## Push name requirement

WhatsApp requires a push name (display name) to be set before sending presence updates. This matches WhatsApp Web behavior.

**Error example:**

```rust theme={null}
use whatsapp_rust::features::presence::PresenceError;

// If push name not set
match client.presence().set_available().await {
    Ok(_) => println!("Presence set"),
    Err(PresenceError::PushNameEmpty) => {
        eprintln!("Cannot send presence without a push name set");
    }
    Err(e) => eprintln!("Other error: {}", e),
}
```

The push name is typically set during the pairing/connection process from app state sync.

## Wire Format

### Setting Presence

```xml theme={null}
<!-- Available (online) -->
<presence type="available" name="YourPushName"/>

<!-- Unavailable (offline) -->
<presence type="unavailable" name="YourPushName"/>
```

### Subscribing to Presence

```xml theme={null}
<!-- Without TC token -->
<presence type="subscribe" to="15551234567@s.whatsapp.net"/>

<!-- With TC token (privacy gating) -->
<presence type="subscribe" to="15551234567@s.whatsapp.net">
  <tctoken><!-- raw token bytes --></tctoken>
</presence>
```

### Unsubscribing from Presence

```xml theme={null}
<presence type="unsubscribe" to="15551234567@s.whatsapp.net"/>
```

## TC token handling

When subscribing to presence, the library automatically:

* Looks up TC token for the target JID
* Includes token as child node if available
* Skips token if not found (non-error)

This matches WhatsApp Web's privacy gating behavior.

## Subscription Tracking

The library automatically tracks which contacts you have subscribed to. This enables automatic re-subscription after a reconnect, so you don't lose presence updates when the connection drops.

### How it works

* Calling `subscribe(jid)` adds the contact to an internal tracked set
* Calling `unsubscribe(jid)` removes the contact from the tracked set
* Duplicate subscriptions are deduplicated automatically
* On reconnect, the library re-subscribes to all tracked contacts

### Automatic re-subscription on reconnect

When the client reconnects after a connection drop, it automatically calls `resubscribe_presence_subscriptions()` to restore all tracked presence subscriptions. This happens transparently — you don't need to manually re-subscribe after a reconnect.

The re-subscription process includes safety checks:

* Bails out early if the connection generation changes (a new reconnect occurred)
* Skips re-subscription if the client is no longer connected

<Note>
  This matches WhatsApp Web behavior, which re-subscribes to all active presence subscriptions after reconnecting.
</Note>

## Behavior Notes

### Available (Online)

When setting status to `Available`, the library automatically:

1. Validates push name is set
2. Sends unified session (internal protocol requirement)
3. Broadcasts presence stanza with push name

### Unavailable (Offline)

When setting status to `Unavailable`:

1. Validates push name is set
2. Broadcasts unavailable presence

Note: This marks you as offline but doesn't disconnect the client.

## PresenceError

The `set`, `set_available`, and `set_unavailable` methods return `Result<(), PresenceError>`:

```rust theme={null}
#[non_exhaustive]
#[derive(Debug, Error)]
pub enum PresenceError {
    #[error("cannot send presence without a push name set")]
    PushNameEmpty,
    #[error("{0}")]
    Client(#[from] ClientError),
    #[error("{0}")]
    Other(#[from] anyhow::Error),
}
```

**Variants:**

* `PushNameEmpty` - Push name must be set before sending presence
* `Client` — wraps a `ClientError` (connection errors from `set`, `set_available`, `set_unavailable`, `subscribe`, and `unsubscribe`)
* `Other` - Wraps any other error (network, connection, etc.)

## Error handling

```rust theme={null}
use whatsapp_rust::features::presence::PresenceError;

match client.presence().set_available().await {
    Ok(_) => println!("Successfully set to online"),
    Err(PresenceError::PushNameEmpty) => {
        eprintln!("Need to set push name first");
    }
    Err(e) => eprintln!("Unexpected error: {}", e),
}
```

## Complete Example

```rust theme={null}
use whatsapp_rust::features::presence::PresenceStatus;

// Set yourself online
client.presence().set_available().await?;

// Subscribe to contacts' presence
let contacts: Vec<Jid> = vec![
    "15551111111@s.whatsapp.net".parse()?,
    "15552222222@s.whatsapp.net".parse()?,
];

for contact in &contacts {
    client.presence().subscribe(contact).await?;
    println!("Subscribed to {}", contact);
}

// Do work while online...
// If the connection drops, tracked subscriptions are
// automatically re-subscribed on reconnect.

// Unsubscribe from a specific contact
client.presence().unsubscribe(&contacts[0]).await?;
println!("Unsubscribed from {}", contacts[0]);

// Set yourself offline when done
client.presence().set_unavailable().await?;
```

## Receiving presence updates

After subscribing to a contact's presence, you'll receive presence events through the event handler. See the [Events](/concepts/events) documentation for details on handling incoming presence updates.
