Access
Reading a business’s profile stays a direct client method. Everything else — catalog, collections, orders, and profile writes — goes throughclient.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.jid- JID of the account to query
Some(BusinessProfile)if the account is a business with a profileNoneif the account is not a business or has no profile
get_catalog
Fetch one page of a business’s product catalog, over MEX.jid- JID of the business accountoptions- paging and thumbnail size; seeCatalogOptions
get_collections
Fetch one page of a business’s product collections, each with its products inline, over MEX.jid- JID of the business accountoptions- paging and thumbnail size; seeCollectionOptions
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.jid- the business the order was placed withorder_id/token- taken from the order message itself (OrderMessage.order_id/OrderMessage.tokenon the Rust struct —orderId/tokenon the wire); the token is a per-order capability, so an order cannot be read without the message that announced it
update_profile
Apply a delta to the authenticated account’s own business profile, over IQ (w:biz).
update- fields leftNoneare untouched; seeBusinessProfileUpdatefor how to clear one instead
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).
upload- thefbid/meta_hmac/tsreceipt from abiz-cover-photomedia upload; seeCoverPhotoUploadfor why this crate does not perform that upload itself
remove_cover_photo
Remove the business profile’s cover photo, over IQ (w:biz).
id- thefbidthe cover photo was set with
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 (Open24HorAppointmentOnly)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 aday_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-sideBusinessHours 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 toupdate_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
<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.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 forget_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 forget_collections.
Default: collection_limit: 51, item_limit: 51, after: None, image_width: 100, image_height: 100.
Catalog
One page of a business catalog, returned byget_catalog.
Collections
One page of a business’s collections, returned byget_collections.
Collection
A named group of products within a catalog.Product
A catalog product. Onlyid 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.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 byget_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 isNone, so the update would be a no-op. - Too many websites (
TooManyWebsites { count }) — more thanBUSINESS_PROFILE_MAX_WEBSITES(2) entries inwebsites. - Invalid coordinate (
InvalidCoordinate { axis, value, limit }) —latitude/longitudeoutside ±90°/±180°, or non-finite (NaN/inf). - Invalid business-hour time (
InvalidBusinessHourTime { day, field, value }) —open_time/close_timemust 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_timeandclose_timemust both be set or both be absent. - Mismatched business-hour mode (
MismatchedBusinessHourMode { day, mode, expectation }) —SpecificHoursrequires a range;Open24HandAppointmentOnlymust not carry one. An unrecognizedOther(...)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_photorequires a pre-uploadedCoverPhotoUploadreceipt because thebiz-cover-photomedia type isn’t wired into this crate’s upload pipeline yet. - Reading a full (untruncated) collection.
get_collectionsreturns 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 toBusinessStatusUpdate events to track changes:
BusinessStatusUpdate type.
Checking if a contact is a business
You can check if a contact is a business account using the contacts API: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_businessflag on contacts - Events - Business status update events
- Send API - Auto-detected
<biz>stanza nodes