Premium Apparel API — Changelog
Every change to the API surface, newest first, with the date it reached production.
API reference · Integration FAQ · Error reference
Versioning policy
What counts as breaking
- Removing an endpoint, or removing a field from a response.
- Renaming a field, or changing its type or meaning.
- Making a previously optional request field required, or tightening validation so a payload that used to be accepted is refused.
- Returning a new HTTP status from an existing operation.
- Introducing a rate limit, or lowering an existing one.
What we commit to
- Breaking changes are announced here at least 90 days before they take effect.
- Breaking changes ship as a new version path (
/v2/...)./v1/...does not change underneath you. - When a version is retired, the retirement date is announced here and the endpoints
carry RFC 8594
DeprecationandLinkheaders for the whole notice period, so your monitoring can see it without anyone reading this page.
What is not breaking, and ships without notice
- A new endpoint.
- A new optional field on a request.
- A new field on a response. Parse responses tolerantly — ignore keys you do not recognise rather than rejecting the payload.
- A new value in an open vocabulary (for example an
actionstring on a line-sync result), or a newdetailsshape under an existing error code. - Wording changes to an error
message. Branch on theerrorcode, never on the message text. The code set is fixed and documented at /docs/errors.
Additive changes are still recorded here, so this page is worth watching even if you never expect to be broken by it.
2026-11-14
Added — product images on GET /v1/feeds/products: image_url (the colour's main image),
image_urls (all of the colour's images) and hero_urls (the style's lead images), as full public HTTPS
URLs from the website catalogue. Products not on the website have image_url: null and empty lists.
2026-11-13
Added — more product fields on POST and PUT /v1/products (privileged):
sales_account, cog_account, asset_account, stocked_type, article (text),
pieces_per_unit (whole number), exclude_from_api, we_stock_this, we_buy_this
(booleans). A product created without duplicate_id can now be given the values the
template product does not carry.
Added — GET /v1/feeds/purchase-orders (privileged). A paged sync feed of purchase orders
with their lines, for planning systems. Backfill returns every open PO plus POs invoiced in the
last 24 months (from to change it); since returns every PO whose header or any line changed.
Cursor paging (limit up to 200, next_cursor), an as_of to feed back as the next since, and
removed for PO numbers that stopped being purchase orders. Lines carry the original, received and
outstanding quantity, cost, purchase_line_pk and the line's received date. Needs
purchase_orders.read.
Added — GET /v1/feeds/products and GET /v1/feeds/suppliers (privileged), same paging
contract. Products: supply data (supplier and code, buy price and currency, NZD average and last
cost, lead time, minimum buy quantity, carton quantity, flags) and per-SKU stock on hand, allocated
and available; since includes stock movements. Suppliers: the supplier master (no bank details).
Products need products.read; suppliers need purchase_orders.read.
Added — GET /v1/feeds/sales-orders (privileged), same paging contract. Orders and
invoices by order date (default 24 months back), with per-SKU lines: quantity, shipped, list and
net price ex GST, discount, line total. Credits are negative-quantity invoices with the original
invoice where known. since catches header and line changes. Needs orders.see_all.
2026-11-12
Added — more product attributes on POST /v1/products and PUT /v1/products
(privileged). All optional, and on create each overrides the value copied from
duplicate_id:
category_1,category_3,category_4— Category 1, 3 and 4.categorystill sets Category 2.custom4— free text.currency— the buy currency, upper-cased on write.label— the brand.lead_time_days— whole days.active— boolean. A created product is still active by default; sendactive: falseto create it inactive, andPUTwithactive: trueto switch it on.
Added — size_group_name on POST /v1/products. The exact name of an existing
GarmentWorks size group, as an alternative to size_group. It never creates a group and
must match exactly one, otherwise that product fails with unknown_size_group or
ambiguous_size_group. Sending both fails with size_group_conflict.
Documented — what duplicate_id copies: every field of the source product, before
the request's own values are applied. See POST /v1/products in the partner reference.
Added — product website data: GET, POST, PUT and DELETE /v1/product-attributes
(privileged). Read, create, change and delete the per-style and per-colour records the
website uses (Title, Body, Tags, Hero, Images, metafields, Price A-D and so on). Up to 25
items per write, best-effort with per-item results. Records kept by the Shopify sync are
read-only. GET needs products.read, POST and PUT need products.write, and DELETE
needs the new scope products.delete_attributes.
Changed — PMS is held per colour on /v1/product-attributes: it needs colour_code, and
the Shopify push writes it to each variant of that colour as custom.pms. No PMS records
existed before the change.
Added — POST /v1/colour-codes (privileged). Adds Dye colour codes. Never overwrites
an existing code. Needs products.write.
2026-11-10
Added — paid on order reads (GET /v1/orders/{our_ref}, GET /v1/orders?order_no=):
true when the order is marked as paid. Staff can change it in GW, so it reflects the order as
it stands now.
Added — paid on POST /v1/orders (privileged). Send paid: true to create the order
already marked as paid, for orders taken after payment. true needs a separate permission on
your token and is refused with 403 forbidden without it; false changes nothing. Ask your
account contact if you need it.
Added — invoice_to on POST /v1/orders (privileged). Sets the debtor name and postal
address printed on the order's packing slip and invoice, for an order placed under one customer
on behalf of another. company is required; leave the address fields out to keep the customer's
own postal address. The order's customer, pricing and tax are unchanged, and no customer record
is changed. It needs a separate permission on your token and is refused with 403 forbidden
without it.
2026-11-04
Added — POST /v1/orders/{our_ref}/line-items (privileged) adds lines to an existing
order and leaves every line already on it untouched, so it also works on an order that
holds the same product on more than one line. Line items take the POST /v1/orders shape,
description included, and are grouped the same way. It needs the same line-amendment
permission as the PUT. It is not idempotent: sending the same request twice adds the
lines twice. Added — add_order_lines, the same operation as an MCP tool.
2026-10-08
Added — discount lines with a negative quantity on POST /v1/orders. A line for the
discount product RETDIS may carry a whole negative quantity at a positive price
(RETDIS-Uni, quantity -1, price 4.50 takes 4.50 off the order). Every other line
still needs a quantity of at least 1, and a negative line without a price is refused.
With taxes_included: true the discount price is read GST-inclusive, like every other
price on that order.
Changed — how line items become order lines on POST /v1/orders. Line items for the
same product that send the same description and the same price (or neither) form one
order line, and a size sent twice is now added up. Previously the later quantity
replaced the earlier one. Line items for the same product with a different
description or price now become separate order lines, so one SKU can appear on
several lines. Previously different descriptions were refused with 422, and different
prices fell back to the price book.
Added — on_hold on the POST /v1/orders response (order reads have it since
2026-10-07). A token can
be set up so that every order it creates is placed on Hold, to be released by staff
before it can be picked. Ask your account contact.
Added — catalogue fields on /v1/products: per size barcode (string),
units_per_carton and rrp (GST-inclusive); per style packing_units. Each is null
where not held. Because every style's content changed, the first ?since= poll after
this shows every style as changed, once.
Changed — PUT /v1/orders/{our_ref}/line-items (privileged) needs the
line-amendment permission on your token, and refuses with 409 conflict
(details.reason: "ambiguous_lines") an order that holds more than one line for the
same product, since it cannot tell those lines apart. Added — set_order_lines, the
same operation as an MCP tool.
2026-10-07
Added — dispatch details on order reads (GET /v1/orders/{our_ref},
GET /v1/orders?order_no=): shipments (one { carrier, tracking_number } per courier
consignment), courier_ticket_no, ship_via, date_shipped, on_hold, and
quantity_shipped on each line. Each line's price is now filled in when the line holds
one; the field was already in the published schema but came back empty. See
Dispatch information on order reads.
2026-10-06
Fixed — POST /v1/orders no longer reports a created order as a failure. A large
order could take longer than the 5-second upstream timeout. The API then answered
503 upstream_unavailable, while the order was still created. Order creation now
has 30 seconds. If it still runs out, the API looks the order up by order_no
before answering: you get the normal 201 if it exists, otherwise a 503 with
details.reason: "outcome_unknown", which means "look it up before retrying". See
/docs/errors.
Changed — PUT /v1/orders/{our_ref}/line-items (privileged) only amends an order
your token can read, under the same visibility rules as GET /v1/orders/{id}. Any
other order returns 404. A moved_to order your token cannot read is refused with
the same 422 as a moved_to that does not exist.
2026-10-05
Added — product attributes on POST /v1/products and PUT /v1/products (privileged).
All optional:
rrp— the recommended retail price, GST-inclusive, stored as sent. It is the figure the no-token catalogue publishes.barcodes— one per size slot, positional across the 11 slots likesell_prices. Send strings: GTINs carry leading zeros.comment— free text; an empty string clears it on update.height,width,depth(metres) — setting them recomputesvolume_m3. Sendvolume_m3as well and your explicit figure is kept.units_per_carton,min_buy_qty,normal_order_qty— whole numbers.
Changed — a product created from duplicate_id no longer inherits the template's
comment or barcodes. Both start empty and take only what you send, because a
template's spec text and GTINs describe the template: inherited barcodes made every clone
scan as its source product. Every other attribute is still inherited when omitted.
Added — update_products over MCP. PUT /v1/products is now available to AI
assistants as a tool. As over REST, it needs the products write permission on your
account and is refused with 403 forbidden without it.
2026-10-01
Fixed — line_items[].price on POST /v1/orders is now honoured. The price was
validated and accepted but never reached the order line, so every line was priced from
the price book. A line with a price is now created at that price, with the price book
and customer discount set aside for it; a line without one is priced from the price book
as before. 0 creates a free line.
Added — line_items[].description on POST /v1/orders. Optional. When present and
not blank it replaces the product's own description on that order line, so a generic
code can name the actual job ("Embroidery - Left Chest"). One order line holds one
product, so lines of the same product share a description: the first non-blank one is
used, and two different descriptions on the same product are refused with
422 validation_error before anything is created. Not accepted by
PUT /v1/orders/{our_ref}/line-items.
Additive: a new optional request field, and a documented field that now does what it said.
2026-09-21
Added — PATCH /v1/orders/{our_ref}. Amends the header of an order that already
exists: the Attention contact, the delivery address, the required-by date and the
internal message. Until now nothing in the API could touch an order after creation, so
an order pushed or imported with a detail missing had to be finished by hand.
Only the fields you send are changed; an omitted field is left alone and a field sent as
"" is cleared. Two of them behave differently on purpose, and both are worth reading
before you call it:
comments_appendappends to the internal message on its own line and never overwrites it, because orders created from an emailed CSV already carry the customer's standing notes there. Sending text the message already contains does nothing, so a retry after a timeout cannot double it up.shipping_addressreplaces the whole delivery address. Anything you leave out is cleared, so send every line you want kept —address1andcityare required whenever it is present. Changing the address also drops the delivery-point code held against the old one, so courier labels are not printed against an address that no longer applies.
Changing the required-by date carries it down onto the order's lines, as changing it in GW does.
Only orders still in the Order state can be amended; once converted to an invoice the
call returns 409 conflict with details.reason = order_not_amendable. An order
somebody has open for editing answers 409 conflict too, with details.reason =
conflict — that one is transient and safe to retry. The response names the fields that
actually changed, so a field sent with the value it already held comes back absent rather
than as an error. See the error reference for both.
Additive: a new endpoint, no change to any existing one.
2026-09-15
Added — Integration FAQ. A new documentation page answering the questions integrators ask before they build: rate limits, GST treatment of prices, incremental-sync semantics, recovering removals, telling discontinued apart from out of stock, read-only tokens, and testing without a sandbox.
Added — this changelog, and the versioning policy above. Both are new commitments, not a change to the API surface.
Clarified — prices are exclusive of GST. No behaviour changed; this was always the case and is now stated in the reference and the FAQ. The one exception is the no-token RRP catalogue, whose prices are GST-inclusive. See Are prices exclusive of GST?.
Clarified — rate limits. There is no enforced rate limit and no endpoint returns
429. If one is introduced it will be announced here first and will use 429 with
Retry-After.
Earlier history
Per-change dates were not recorded before this page existed, so the entries below are grouped by month rather than by day. Everything listed is live.
2026-08
POST /mcp— the sales-order and catalogue operations exposed over the Model Context Protocol, with an OAuth authorization-code flow so AI assistant connectors can authenticate. Connection guide at /docs/mcp.GET /v1/stockandGET /v1/prices— SKU-level slices of the catalogue, filtered byproduct_id,styleorsku, with?since=incremental polling andremovedtombstones.?since=onGET /v1/products, returning{ as_of, as_of_epoch_ms, count, products, removed_styles }, plus aLast-Modifiedheader on every catalogue response.POST /v1/products,PUT /v1/products,POST /v1/product-images,GET /v1/products/{code}/exists— product maintenance. Privileged; not shown in the standard reference and not available over MCP.GET /v1/productsbecame the canonical catalogue path. The legacyGET /api/productscontinues to work unchanged and now carriesDeprecationandLinkheaders pointing at the canonical path. No retirement date is set; one would be announced here with 90 days' notice.PUT /v1/orders/{our_ref}/line-items— desired-state sync of an order's lines, for integrators that split an order after pushing it.- Purchase-order endpoints — create, read, add lines, set lines and shipments. Privileged.
2026-06
- Error reference published, documenting every error code, when it occurs and the recommended client action.
2026-04
POST /v1/orders, idempotent onorder_no.GET /v1/orders/{our_ref}andGET /v1/orders?order_no=.- Interactive reference at /docs, with the OpenAPI document at /v1/openapi.json.