Skip to main content
The business API lets you read a WhatsApp Business account’s profile, browse its product catalog and collections, look up an order’s line items, and manage the authenticated account’s own business profile and cover photo.

Access

Reading a business’s profile stays a direct client method. Everything else — catalog, collections, orders, and profile writes — goes through client.business():
Catalog, collection, and order lookups go over MEX (persisted GraphQL) — WhatsApp Web has no IQ fallback for these. Business profile writes and the cover photo go over IQ (w:biz). See the MEX API for the underlying GraphQL transport.

Methods

get_business_profile

Fetch the business profile for a WhatsApp Business account.
Parameters:
  • jid - JID of the account to query
Returns:
  • Some(BusinessProfile) if the account is a business with a profile
  • None if the account is not a business or has no profile
Example:

get_catalog

Fetch one page of a business’s product catalog, over MEX.
Parameters:
  • jid - JID of the business account
  • options - paging and thumbnail size; see CatalogOptions
Example:

get_collections

Fetch one page of a business’s product collections, each with its products inline, over MEX.
Parameters:
  • jid - JID of the business account
  • options - paging and thumbnail size; see CollectionOptions
Example:
Collection::products is a possibly-truncated prefix — the collections query returns no per-collection cursor, so products.len() == item_limit is the only signal that more products may exist in that collection. Equality doesn’t prove it: a collection with exactly item_limit products and no more looks identical.

get_order

Look up an order’s line items and totals, over MEX.
Parameters:
  • jid - the business the order was placed with
  • order_id / token - taken from the order message itself (OrderMessage.order_id / OrderMessage.token on the Rust struct — orderId/token on the wire); the token is a per-order capability, so an order cannot be read without the message that announced it
Example:

update_profile

Apply a delta to the authenticated account’s own business profile, over IQ (w:biz).
Parameters: The update is validated client-side before anything is sent — an invalid delta returns BusinessError::InvalidUpdate without touching the wire. See Validation below. Example:

set_cover_photo

Point the business profile at an already-uploaded cover photo, over IQ (w:biz).
Parameters:
  • upload - the fbid/meta_hmac/ts receipt from a biz-cover-photo media upload; see CoverPhotoUpload for why this crate does not perform that upload itself
Example:

remove_cover_photo

Remove the business profile’s cover photo, over IQ (w:biz).
Parameters:
  • id - the fbid the cover photo was set with
Example:

Types

BusinessProfile

BusinessProfile is #[non_exhaustive]: struct-literal construction and exhaustive struct destructuring from outside the crate are both disallowed. Field reads are unaffected; add .. to any exhaustive destructuring patterns.

BusinessCategory

BusinessCategory is #[non_exhaustive]: struct-literal construction and exhaustive struct destructuring from outside the crate are both disallowed. Field reads are unaffected; add .. to any exhaustive destructuring patterns.

BusinessHours

BusinessHours is #[non_exhaustive]: struct-literal construction and exhaustive struct destructuring from outside the crate are both disallowed. Field reads are unaffected; add .. to any exhaustive destructuring patterns.

BusinessHoursConfig

The read side reports opening hours with this type; the write side (BusinessHoursUpdate::config) reuses it too.
BusinessHoursConfig is #[non_exhaustive]: struct-literal construction and exhaustive struct destructuring from outside the crate are both disallowed. Field reads are unaffected; add .. to any exhaustive destructuring patterns. Constructors:
  • BusinessHoursConfig::new(day_of_week, mode) — a day whose mode carries no explicit range (Open24H or AppointmentOnly)
  • BusinessHoursConfig::with_hours(day_of_week, mode, open_time, close_time) — a single opening range, in minutes past local midnight. A day with two ranges is two entries sharing a day_of_week.
There’s no “closed” mode or constructor. config holds one entry per day that has hours to report; a day closed every week is represented by leaving it out of config entirely, not by constructing an entry for it.

DayOfWeek

BusinessHourMode

BusinessHoursUpdate

Opening hours carried by a profile mutation. Distinct from the read-side BusinessHours because the mutation also carries a free-text note, which the profile query does not return.

BusinessProfileUpdate

A delta mutation of the business profile, passed to update_profile. This is a delta, not a replacement: every field defaults to None, and a None field is left untouched on the server. To clear a field instead of leaving it alone, set it to Some of an empty value — Some(String::new()) for text fields, Some(Vec::new()) for websites. None and an empty Some are not interchangeable: only the latter clears anything.

BUSINESS_PROFILE_MAX_WEBSITES

WhatsApp Web emits at most two <website/> nodes per profile mutation. A websites list longer than this is rejected client-side (BusinessProfileUpdateError::TooManyWebsites) rather than silently truncated, since the real client would drop the extra entries without telling you.

CoverPhotoUpload

The upload receipt a cover photo mutation has to quote back.
These three values come from a biz-cover-photo media upload, whose response carries fbid/meta_hmac/ts instead of the url/direct_path pair every other media type returns. This crate does not yet perform that upload — the upload pipeline (src/upload.rs) parses url/direct_path out of every response, which the cover-photo endpoint doesn’t return. Obtain the receipt yourself and pass it in.

CatalogOptions

Paging and thumbnail options for get_catalog.
Default: limit: 10, after: None, image_width: 100, image_height: 100, allow_shop_source: true. These defaults are this client’s choice — WhatsApp Web computes them per-surface at the call site, so there’s no bundle constant to match. Override them per request.

CollectionOptions

Options for get_collections.
Default: collection_limit: 51, item_limit: 51, after: None, image_width: 100, image_height: 100.

Catalog

One page of a business catalog, returned by get_catalog.

Collections

One page of a business’s collections, returned by get_collections.

Collection

A named group of products within a catalog.
Collection::products can be truncated, and says so only by its length: the collections query returns a prefix with no per-collection cursor, so products.len() == item_limit is the only signal more products may exist — it isn’t conclusive, since a collection with exactly that many products and no more reads the same way. Reading a collection to the end would need a different, not-yet-wired-up operation.

Product

A catalog product. Only id is guaranteed — every other field is optional because the server omits rather than blanks a missing value (an absent name is None, never ""; an absent is_hidden is None, never false).
Catalog and collection products never carry variant data (variant_info is opt-in on the wire and this crate doesn’t request it), so there’s no variant_properties field here. Order line items are the one place variant data is surfaced — see OrderProduct.

Price

A price, in thousandths of the currency’s main unit — not hundredths.
WhatsApp Web’s protobuf field is priceAmount1000: int64, formatted through formatAmount1000. Keeping the raw integer avoids rounding — dividing by 1000 into a float is a display-time decision for the caller to make.

SalePrice

ProductImage

ProductVideo

ImporterAddress

A postal address, sent for a product’s importer of record. Every part is optional — the server omits what it does not hold rather than sending an empty string.

ProductAvailability

Whether a product can currently be bought. The wire values are the GraphQL enum names.

Order

Returned by get_order.

OrderProduct

A line item on an order — a snapshot rather than a live product: the price is what was quoted when the order was placed.

VariantProperty

One dimension of a chosen product variant, e.g. name: "Size", value: "Large".

OrderPriceDetails

Validation

update_profile validates a BusinessProfileUpdate before sending anything, because the server rejects a business_profile delta on any single bad field — losing the other, valid fields in the same update. All of the following surface as BusinessError::InvalidUpdate(BusinessProfileUpdateError::...):
  • Empty delta (Empty) — every field is None, so the update would be a no-op.
  • Too many websites (TooManyWebsites { count }) — more than BUSINESS_PROFILE_MAX_WEBSITES (2) entries in websites.
  • Invalid coordinate (InvalidCoordinate { axis, value, limit }) — latitude/longitude outside ±90°/±180°, or non-finite (NaN/inf).
  • Invalid business-hour time (InvalidBusinessHourTime { day, field, value }) — open_time/close_time must be less than 1440 (minutes in a day). Ranges that cross midnight (open_time > close_time) are legal — WhatsApp Web’s picker UI discourages them, but the wire format doesn’t forbid them.
  • Incomplete business-hour range (IncompleteBusinessHourRange { day }) — open_time and close_time must both be set or both be absent.
  • Mismatched business-hour mode (MismatchedBusinessHourMode { day, mode, expectation }) — SpecificHours requires a range; Open24H and AppointmentOnly must not carry one. An unrecognized Other(...) mode is never checked this way.

Error handling

get_catalog, get_collections, and get_order return BusinessError::Mex on a failed MEX call and BusinessError::MalformedResponse on a structurally-unexpected (but successfully-fetched) response. update_profile, set_cover_photo, and remove_cover_photo return BusinessError::Request on a failed IQ, and update_profile additionally returns BusinessError::InvalidUpdate when client-side validation rejects the delta (see Validation).

Not yet supported

  • Product create/edit/delete. Only reads exist (get_catalog, get_collections, get_order) — there is no product-mutation surface in this crate. WhatsApp Web itself has no such operation to mirror; only third-party clients expose one against the legacy IQ transport.
  • Uploading a new cover photo. set_cover_photo requires a pre-uploaded CoverPhotoUpload receipt because the biz-cover-photo media type isn’t wired into this crate’s upload pipeline yet.
  • Reading a full (untruncated) collection. get_collections returns a possibly-truncated product prefix per collection with no way to page further within one collection.
  • Catalog/collection variant data. Only orders carry variant_properties; requesting variant info for catalog/collection products isn’t implemented.

Business events

Business account changes are reported through the event system. Subscribe to BusinessStatusUpdate events to track changes:
See Events for the full BusinessStatusUpdate type.

Checking if a contact is a business

You can check if a contact is a business account using the contacts API:
See Contacts API for details.

Automatic business stanza detection

When sending interactive business messages (native-flow buttons for payments, CTAs, catalogs, etc.), the library automatically injects a <biz> stanza child node on the outgoing message. This means you can send InteractiveMessage with NativeFlowMessage content through send_message or send_message_with_options without manually constructing business protocol nodes. See Send API - Automatic business node detection for the full list of supported button-to-flow mappings.

See also

  • Client API - Client-level business methods
  • MEX API - GraphQL transport used by catalog, collections, and order lookup
  • Contacts API - Check is_business flag on contacts
  • Events - Business status update events
  • Send API - Auto-detected <biz> stanza nodes