Event catalog
Every webhook event the integration emits, what it means, and the shape of data.object. The envelope wrapping each payload is documented on the Webhooks page.
Subscribing to events
Subscriptions are managed from Custom Integration → Settings → Event subscriptions. The default is the wildcard *: your endpoint receives every event.
- Tick individual events to allowlist them.
- Wildcard a namespace with e.g.
listing.*(subscribe to all listing events). integration.test_eventignores subscriptions; it's always delivered when fired manually.
Echo-suppression
Don't expect to receive your own writes back
POST /v1/orders/{id}/cancel or upserting a customer sends nothing to your URL, and a watch that sells through an order you reported does not come back as listing.sold. This is what stops trivial event-loops between your custom site and WatchTraderHub.Events triggered elsewhere (other integrations like Shopify or eBay, actions taken in the dashboard) still fire to your URL as normal.
Events
Listing events only carry the id
listing.* event, data.object is just the listing id and object: "listing". To get the watch (brand, model, price, photos, caliber, complications, water resistance, power reserve), fetch GET /v1/listings/{id} when the event arrives. A permanent deletion sends listing.unpublished with deleted: true. Do not fetch that listing because it has already been removed and the API will return 404. Order events are the exception: they include the full order inline, exactly as GET /v1/orders/{id} returns it.Spare parts are a separate namespace
part.*, and a subscription to listing.* does not receive them. Fetch GET /v1/parts/{id} when one arrives, except for part.unpublished, where the part is already off the channel and the API will return 404.Listing events
listing.published
Fires when the dealer toggles an inventory item ON for the custom channel.
data.object
listing.updated
Fires when a published listing's data changes (price, photos, watch attributes, ...).
data.object
data.previous_attributes: Reserved: currently always an empty object. Fetch the listing to read its current values.
listing.unpublished
Fires when the dealer toggles a listing OFF for the custom channel.
data.object
listing.sold
Fires when a listing transitions to sold (either manually or via order matching).
data.object
listing.in_stock
Fires when a previously-sold listing returns to active stock (e.g. sale voided).
data.object
Spare part events
part.published
Fires when the dealer puts a spare part ON the custom channel. Fetch GET /v1/parts/{id} for the part.
data.object
part.updated
Fires when a part already on the channel changes (price, quantity, photos, compatibility, ...).
data.object
part.unpublished
Fires when the dealer takes a spare part OFF the custom channel. The part stops being readable once this arrives, so do not fetch it.
data.object
part.sold_out
Fires when a part reaches zero units, whether it was sold in WatchTraderHub or through a reported order. The part stays readable and its status reads sold_out.
data.object
part.in_stock
Fires when a live part's stock count changes and it still has units: a restock, a return, a cancelled sale, a fit, or a partial sale. The new count is in quantity.
data.object
Order events
order.fulfilled
Fires when an order is matched to a listing and the dealer marks it fulfilled.
data.object
order.cancelled
Fires when the dealer dismisses one of your orders in WatchTraderHub. Not sent for a cancel you made yourself through `POST /v1/orders/{id}/cancel`: the response already tells you.
data.object
data.previous_attributes: Reserved: currently always an empty object. The order payload carries the new status.
Integration events
integration.test_event
Fires from `POST /v1/test/webhook_ping`. Always delivered regardless of `subscribed_events` filtering.
data.object
