Skip to content
API Docs

Changelog & versioning

The API is versioned by date. Your integration is pinned to a single date-stamped version. We won't change anything about that version's behaviour without you opting in.

How versioning works

The version pinned to your integration is visible at Settings → API version and on every response as the X-API-Version header. It's also embedded in every webhook event:

{
  "id": "evt_…",
  "object": "event",
  "type": "listing.published",
  "api_version": "2026-05-07",
  …
}

Newly-created integrations always pin to the latest dated version at creation time. To roll an existing integration forward (to opt into breaking changes from a newer version), email support@watchtraderhub.com. We don't auto-roll because we don't want to break your production integration on our schedule.

X-API-Version is response-only

You do not need to send X-API-Version on outbound requests; we do not read it. Version pinning happens at integration creation time and is enforced server-side. The header on every response and on every webhook envelope's api_version field is the canonical value, set entirely by us. Send the header defensively if you like (some integrators do for forward-compat breadcrumbs), but it has no effect.

What counts as a breaking change

Breaking changes (mint a new dated version):

  • Removing an endpoint, field, or event type.
  • Renaming an endpoint, field, or event type.
  • Tightening a validation rule (a payload that used to be accepted is now rejected).
  • Changing the meaning of a field, e.g. a status enum value being repurposed.
  • Changing the algorithm of webhook signing or any header format.
  • Changing the cursor format or pagination semantics.

Non-breaking changes (ship continuously, pinned versions automatically benefit):

  • Adding a new endpoint.
  • Adding a new optional field to a request body.
  • Adding a new field to a response body.
  • Adding a new event type. Existing subscriptions still apply; the wildcard * picks it up.
  • Adding a new error code (existing codes keep their meaning).
  • Improving error message wording.

Match codes, not messages

We only consider error.code values part of the contract. error.message wording can change at any time in the name of better diagnostics. Branch on code.

Versions

2026-05-07

Current

Initial public release of the Custom Integration API. Includes the full REST surface (listings, customers, orders, integration metadata, test utilities) and the outbound webhook surface (10 event types, HMAC-SHA256 signing, retry + replay + dead-letter).

Additions, September 2026 (non-breaking)

All additive on 2026-05-07: nothing was removed or renamed, and existing integrations keep working unchanged.

  • Listings: title, quantity, labels (English display text for every enum field) and description_paragraphs; under watch: case_thickness_mm, lug_width_mm, bezel_material, crystal_type, dial_numerals, bracelet_material, bracelet_color, bracelet_links, clasp_type, clasp_material, additional_details, handling_time and tax_treatment.
  • Parts: part.tax_treatment, labels and description_paragraphs.
  • Photos (listings and parts): width, height and a square thumbnail_url.
  • Promo prices (listings and parts): while the dealer runs a promo, price is the promo price and the new promotion object names the regular price it replaces (regular_amount, regular_amount_formatted) and when it ends (ends_at, or null). promotion is null otherwise. Starting, changing or ending a promo sends listing.updated (part.updated for a part).
  • description no longer carries the " || " paragraph separator some imported descriptions used: it becomes a blank line. Line breaks the dealer typed are unchanged.
  • Orders: optional shipping_amount, discount_amount and tax_amount on the order and discount_amount on each line. Shipping and coupons are recorded as shipping and discounts instead of fees; orders read back the amounts you reported. Orders without them behave exactly as before.
  • GET /v1/orders now honours updated_after, as documented.
  • listing.updated and part.updated now also fire when the dealer changes photos or stock quantity, your integration's markup, rounding or image format, the business address or default tax treatment, a business unit or location name, or assigns stock numbers. Before, only an edit of the item itself sent one.
  • order.cancelled now fires when the dealer dismisses one of your orders in WatchTraderHub, with the full order. order.received and customer.matched are no longer listed: they could only ever describe your own API call, were never delivered to you, and a subscription that names them keeps working.
  • A failed database read now answers 500 query_failed instead of serving a listing priced in a fallback currency. Retry it.
  • Corrected reference: POST /v1/customers/bulk returns bulk_result and is atomic; POST /v1/test/webhook_ping returns delivery_id and enqueued; the signature header is X-WTH-Signature; customer metadata is accepted and not stored.