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

# MEX (GraphQL)

> Execute typed GraphQL queries and mutations via Meta Exchange persisted operations

The `Mex` feature wraps WhatsApp's Meta Exchange (MEX) GraphQL API. Operations are persisted: you reference each query or mutation by a `(name, id)` pair pulled from a generated operation module, and pass its typed `Variables`. WhatsApp Web bundle updates rotate the numeric `id`, so the human-readable `name` keeps diagnostics stable across releases.

## Access

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

## Building a request

Each persisted operation lives in its own module under [`wacore::iq::mex_operations`](/api/wacore). Every module exposes:

* `NAME` — the operation name (e.g. `"WAWebJoinNewsletterMutation"`).
* `DOC_ID` — the current persisted document ID.
* `OPERATION_KIND` — `"query"` or `"mutation"`.
* `Variables` — a typed struct matching the operation's input shape.
* `Response` — a typed struct matching the operation's output shape.

Construct a [`MexRequest`](#mexrequest) with [`MexRequest::new`](#new) using those constants and a `Variables` value:

```rust theme={null}
use wacore::iq::mex_operations::fetch_newsletter;
use whatsapp_rust::features::mex::MexRequest;

let request = MexRequest::new(
    fetch_newsletter::NAME,
    fetch_newsletter::DOC_ID,
    fetch_newsletter::Variables {
        input: Some(fetch_newsletter::Input {
            key: Some(jid.to_string()),
            r#type: Some("JID".into()),
            view_role: Some("GUEST".into()),
        }),
        fetch_viewer_metadata: Some(true),
        fetch_full_image: Some(true),
        fetch_creation_time: Some(true),
    },
);

let response = client.mex().query(request).await?;
```

<Tip>
  For operations whose `Variables` schema is too permissive to model exactly, pass any `serde::Serialize` value (for example a `serde_json::json!({...})`) as the variables — `query` and `mutate` are generic over `V: Serialize`.
</Tip>

## Methods

### query

Execute a GraphQL query.

```rust theme={null}
pub async fn query<V: Serialize>(
    &self,
    request: MexRequest<V>,
) -> Result<MexResponse, MexError>
```

### mutate

Execute a GraphQL mutation.

```rust theme={null}
pub async fn mutate<V: Serialize>(
    &self,
    request: MexRequest<V>,
) -> Result<MexResponse, MexError>
```

**Example — mutation with typed variables:**

```rust theme={null}
use wacore::iq::mex_operations::join_newsletter;
use whatsapp_rust::features::mex::MexRequest;

let request = MexRequest::new(
    join_newsletter::NAME,
    join_newsletter::DOC_ID,
    join_newsletter::Variables {
        newsletter_id: Some(jid.to_string()),
    },
);

let response = client.mex().mutate(request).await?;
```

**Example — loosely-typed variables with `serde_json`:**

```rust theme={null}
use serde_json::json;
use wacore::iq::mex::MexDoc;
use whatsapp_rust::features::mex::MexRequest;

let request = MexRequest {
    doc: MexDoc {
        name: "WAWebUpdateGroupPropertyMutation",
        id: "8014515568645029",
    },
    variables: json!({
        "group_id": group_jid.to_string(),
        "property": "announcement",
        "value": true,
    }),
};

let response = client.mex().mutate(request).await?;
```

## Types

### MexDoc

A persisted-query descriptor. Re-exported from `wacore::iq::mex`.

```rust theme={null}
pub struct MexDoc {
    /// Human-readable operation name (stable across bundle releases).
    pub name: &'static str,
    /// Numeric persisted-query ID (rotates with bundle releases).
    pub id: &'static str,
}
```

### MexRequest

A persisted-query descriptor plus its typed variables. `V` is the variables type — usually the generated `Variables` struct for an operation, but any `Serialize` value works.

```rust theme={null}
pub struct MexRequest<V> {
    pub doc: MexDoc,
    pub variables: V,
}
```

#### new

Pair an operation's `NAME` and `DOC_ID` with its variables.

```rust theme={null}
pub fn new(
    name: &'static str,
    id: &'static str,
    variables: V,
) -> MexRequest<V>
```

### MexResponse

Response from a GraphQL operation.

```rust theme={null}
pub struct MexResponse {
    /// Response data (if successful).
    pub data: Option<serde_json::Value>,
    /// List of errors (if any).
    pub errors: Option<Vec<MexGraphQLError>>,
}
```

**Methods:**

* `has_data()` — `true` if the response contains data.
* `has_errors()` — `true` if the response contains errors.
* `fatal_error()` — the first fatal error (any error with an `error_code`), if any.

### MexGraphQLError

```rust theme={null}
pub struct MexGraphQLError {
    pub message: String,
    pub extensions: Option<MexErrorExtensions>,
}
```

**Methods:**

* `error_code()` — the numeric error code, if present.
* `is_summary()` — `true` when the error is marked as a summary.
* `has_error_code()` — `true` when an error code is set.

<Note>
  `MexGraphQLError` is re-exported from the crate root as `whatsapp_rust::MexGraphQLError`, alongside `MexError`, `MexErrorExtensions`, `MexRequest`, and `MexResponse`.
</Note>

### MexErrorExtensions

```rust theme={null}
pub struct MexErrorExtensions {
    pub error_code: Option<i32>,
    pub is_summary: Option<bool>,
    pub is_retryable: Option<bool>,
    pub severity: Option<String>,
}
```

## Error handling

```rust theme={null}
pub enum MexError {
    /// Payload missing or malformed with only a descriptive message.
    PayloadParsing(String),
    /// Payload contained an invalid JID.
    InvalidJid(JidError),
    /// GraphQL error with a numeric code surfaced as a typed error.
    ExtensionError { code: i32, message: String },
    /// Underlying IQ request failed.
    Request(IqError),
    /// JSON serialization or deserialization error.
    Json(serde_json::Error),
}
```

`query` and `mutate` automatically convert any fatal GraphQL error (any error with an `error_code`) into `MexError::ExtensionError` so you can match on it without inspecting `response.errors` yourself.

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

match client.mex().query(request).await {
    Ok(response) => {
        if response.has_data() {
            println!("Success: {:?}", response.data);
        }
    }
    Err(MexError::ExtensionError { code, message }) => {
        eprintln!("MEX error {}: {}", code, message);
    }
    Err(e) => eprintln!("Request failed: {}", e),
}
```

## Response handling

### Check for data

```rust theme={null}
let response = client.mex().query(request).await?;

if response.has_data() {
    let data = response.data.unwrap();
    // Process data
}
```

### Handle non-fatal errors

Errors without an `error_code` are non-fatal and surface in `response.errors` alongside any partial data.

```rust theme={null}
let response = client.mex().query(request).await?;

if response.has_errors() {
    for error in response.errors.as_ref().unwrap() {
        if !error.is_summary() && !error.has_error_code() {
            eprintln!("Warning: {}", error.message);
        }
    }
}
```

### Decode the typed response

Generated operation modules also expose a `Response` struct that mirrors the GraphQL schema. Decode it from `response.data` with `serde_json`:

```rust theme={null}
use wacore::iq::mex_operations::fetch_newsletter;

let response = client.mex().query(request).await?;

if let Some(data) = response.data {
    let decoded: fetch_newsletter::Response = serde_json::from_value(data)?;
    if let Some(newsletter) = decoded.xwa2_newsletter {
        println!("Newsletter: {:?}", newsletter);
    }
}
```

<Note>
  The persisted `DOC_ID` for each operation is regenerated from the latest WhatsApp Web bundle. Pin to a specific crate version if you need a stable wire format.
</Note>
