Skip to main content
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

Building a request

Each persisted operation lives in its own module under wacore::iq::mex_operations. Every module exposes:
  • NAME — the operation name (e.g. "WAWebJoinNewsletterMutation").
  • DOC_ID — the current persisted document ID.
  • OPERATION_KIND"query" or "mutation".
  • VARIABLE_KEYS — every variable name the persisted document declares, e.g. &["fetch_viewer_metadata", "fetch_full_image", ...].
  • Variables — a typed struct matching the operation’s input shape.
  • Response — a typed struct matching the operation’s output shape.
Construct a MexRequest with MexRequest::new using those constants and a Variables value:
A generated Variables does not implement Default. The server binds a persisted query’s variables by name and answers a bare 400 Bad Request when it can’t bind one of them, so every variable the operation declares has to be named at the call site — ..Default::default() does not compile, and neither does Variables::default(). Passing None for an Option field is still how you send a variable WhatsApp Web itself omits; nested input objects (for example Updates on update_newsletter) are unaffected and keep Default.
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. Use MexRequest::missing_variables to check such a payload against VARIABLE_KEYS, since the compiler can’t.

Methods

query

Execute a GraphQL query.

mutate

Execute a GraphQL mutation.
Example — mutation with typed variables:
Example — loosely-typed variables with serde_json:

fetch_new_chat_message_capping_info

Fetch the cap on how many new one-on-one conversations you can start in the current cycle.
WhatsApp Web issues this request at app launch. It refreshes the cap on a TTL (wa_individual_new_chat_msg_capping_fetch_ttl_seconds, 1 hour), gated on wa_individual_new_chat_msg_capping_enabled. If the cap doesn’t apply to your account, you still get an answer — the status just reports NONE. Example:

get_username

Read this account’s own username, its state, and its username key.
Returns:
  • None — no username is set on this account. WhatsApp Web reads the same 404 the server sends for this case.
  • Some(OwnUsername) — the account’s username info.
Only reads are exposed. set_username and set_username_key are not wrapped. They change the account’s identity in a way the server does not undo. A wrong call can burn a handle that someone else could otherwise take, and there is no way to test either safely.
Example:

Types

MexDoc

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

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.
  • declared_variables — the variable names the persisted document declares (an operation module’s VARIABLE_KEYS), carried so missing_variables can check a payload against them even when variables is a loosely-typed Serialize value.

new

Pair an operation’s NAME, DOC_ID, and VARIABLE_KEYS with its variables. Prefer this over building MexRequest as a struct literal.

missing_variables

Declared variables this request’s payload does not carry, found by serializing variables and checking which of declared_variables are absent.
A non-empty result isn’t automatically wrong — WhatsApp Web itself omits an optional variable it has no value for, and this reports that the same way rather than judging it. It exists mainly for the loosely-typed Serialize form of a request, where the compiler can’t check a json! payload against the operation’s declared variables the way it checks a Variables struct literal.

MexResponse

Response from a GraphQL operation.
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

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.
MexGraphQLError is re-exported from the crate root as whatsapp_rust::MexGraphQLError, alongside MexError, MexErrorExtensions, MexFatalError, MexRequest, MexResponse, and OwnUsername.

MexErrorExtensions

MexFatalError

The typed cause behind a fatal GraphQL error, from wacore::iq::mex. WhatsApp Web treats a GraphQL error carrying an extension code as fatal to the whole request. This struct is what lets that code survive the trip through IqError::ParseError’s source() chain instead of being flattened into a plain message.
You won’t normally construct or match this directly. query/mutate already downcast it for you and hand back MexError::ExtensionError. It’s public for callers working with wacore::iq::mex::MexQuerySpec directly, below the whatsapp_rust::features::mex::Mex wrapper.

OwnUsername

This account’s own Meta username, as get_username reports it. Every field is optional because the server omits the ones that don’t apply.

NewChatMessageCapping

How many new one-on-one conversations you can still start in the current cycle, and why. Every field is optional — the server omits any that don’t apply to your account’s tier.
  • total_quota / used_quota — new chats allowed / already started this cycle
  • cycle_start_timestamp / cycle_end_timestamp / server_sent_timestamp — Unix seconds. The cap lifts once cycle_end_timestamp passes.
Methods:
  • remaining_quota() -> Option<u64> — saturating subtraction of used_quota from total_quota, when both are present; returns Some(0) when used_quota exceeds total_quota

CappingStatus

Where you stand against the cap.

CappingOteStatus

Whether you’re eligible for the one-time extension that lifts the cap for a cycle.

CappingMvStatus

Your Meta Verified subscription state, which lifts the cap permanently.
NewChatMessageCapping, CappingStatus, CappingOteStatus, and CappingMvStatus are re-exported from the crate root, alongside the other Mex types.

Error handling

query and mutate automatically convert any fatal GraphQL error (any error with an error_code) into MexError::ExtensionError. This lets you match on it without inspecting response.errors yourself. This now happens for every MEX operation via a typed MexFatalError carried inside IqError::ParseError’s source chain. Previously the code could be lost, surfacing only as a message inside MexError::Request(IqError::ParseError(_)).
get_username treats a fatal 404 as “no username set” rather than a failure. It arrives as either MexError::ExtensionError { code: 404, .. } or MexError::Request(IqError::ServerError { code: 404, .. }) — the two shapes WhatsApp Web’s own MEX client raises a 404 from. Either shape returns Ok(None) instead of an error.

Response handling

Check for data

Handle non-fatal errors

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

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