# Changelog

Changes inside v1 are additive. Anything breaking ships under a new version path, announced ahead of time.

## Flashing lines documented end to end, a created id on 409, and pricing settings

**17 Sep 2026** · Added

Every field on a flashing line and its drawing now says what goes in it: the cut list in subitems (millimetre numbers), totalLength in metres, totalGirth and bends with the rules Factory uses to count them, how the four money fields relate, the drawing's points, edges, angles, finishes, flip and arrow conventions, which side a drawing defaults to, and the size limits. The drawing is required. A new guide, Build a flashing line, works a hand-drawn flashing through end to end. A repeated tempId is now rejected instead of silently reusing the first drawing. The error envelope gains resourceId: on a 409 whose original request completed, it carries the id that request created, and the four 409 codes are listed. GetCompany returns pricingSettings — the minimum lengths charged on lineal-metre, lineal-feet and flashing lines, and the crush-fold bend count. Corrected wording: creating an order or quote with companyName instead of customerId returns 501; the customer name filter is a contains-match; a fee must be a fixed amount and a markup changes no total; the add-line response carries only the id and totals; amounts are never converted between currencies; unrecognised request fields are ignored. Removing a line whose cost and markup no longer agree with its price no longer fails.

## v1 is stable

**16 Sep 2026** · Released

The v1 contract is frozen: 59 operations across orders, quotes, customers, catalogue, inventory, suppliers, users, labels, order statuses, company settings, webhooks and search. From here every published operation, field and value keeps its meaning and wire form for the life of the version. Changes inside v1 are additive; anything breaking ships under a new version path or, for a removal, after a deprecation announced here.

## Order numbers are read-only

**14 Sep 2026** · Changed

purchaseOrder was never your purchase order number. It is Factory’s own sequential document number — the “Order #” in the app, on invoices and delivery documents, and the document reference in the accounting integrations — and the server has always assigned it. It is now orderNumber on Order and Quote: read-only, assigned when the document is created, and unchanged when a quote becomes an order. It is gone from CreateOrder and CreateQuote; a request that still sends purchaseOrder has it ignored, as with any field the API does not recognise. Your own purchase order number belongs in reference, which the app shows as “PO #” and the documents as “PO”; reference itself is unchanged.

## API versioning policy, and search marked preview

**14 Sep 2026** · Added

The API conventions now carry the full v1 API versioning policy: which changes are additive and can ship at any time, which are breaking and never ship inside v1, how deprecation works — announced here and in the description, with at least 12 months before removal — and a preview tier for items published early, which can change or be withdrawn with 30 days’ notice here. Catalogue search is the first preview item. It works today, with two known limits: a page-by-page walk of a large result set can drop or repeat a row, so fetch one large page when completeness matters, and a flashing hit’s id cannot yet be passed to GetFlashing.

## Idempotency keys are enforced

**10 Sep 2026** · Changed

requestId is now a real at-most-once guarantee on every write that takes it — CreateOrder, CreateQuote, CreateCustomer, the add-line-item calls, PostMessage and UploadAttachment. Requests carrying the same key are executed at most once; any repeat is rejected with 409 rather than replayed, and when the original request completed the error body carries the id of the resource it created. If the outcome of the original attempt is unknown — in flight, failed, or cut off — verify with a read, then retry with a fresh key: a key is consumed by its first attempt, success or failure. Keys are scoped to the API key that sends them and retained for at least 24 hours. CreateCustomer, previously documented as validated-but-not-deduplicated, now carries the same guarantee and gains its 409 response. When the idempotency store is unavailable, requests carrying a key are rejected with 503; requests without one are unaffected.

## Labour costs

**4 Sep 2026** · Added

Every user record now carries two default hourly rates: hourlyRateCharged, what an hour of their work sells for, and hourlyRateCost, what it costs your company — both zero until a rate is set in the Factory app. Orders and quotes gain labourTotal, a read-only rollup the server derives on every write: the sum of the labour line totals, including labour components inside product kits. It is the labour charge subtotal — what labour sells for, not what it costs; the cost side stays per-line, on the pricing cost of each labour line. Create responses do not include labourTotal; it is there on any subsequent read.

## Messages and attachments

**2 Sep 2026** · Added

Order and quote conversations — the thread the Factory app shows in its Collaborate tab — are now on the API, with the same operations on both services. ListMessages returns the thread oldest first, text and files together; ListAttachments flattens it to just the files, with isGenerated separating documents the app generated (quote and invoice PDFs) from uploads. Every attachment carries a presigned download URL that expires — re-read, or call GetAttachment, for a fresh one rather than storing it. UploadAttachment sends one file up to 20 MB, base64-encoded over REST, with an optional caption that becomes the message text; HEIC and HEIF images convert to JPEG on upload, and a requestId idempotency key makes retries safe. DeleteAttachment removes a file you uploaded — generated documents are protected, and a Factory app administrator can reverse the removal. PostMessage adds a plain-text message on its own; the text is stored as an HTML fragment — escaped and wrapped in a paragraph tag — and reads back in that form, so mention markup renders as literal text and notifies no one. To send a file with text, use the caption on UploadAttachment.

## Webhooks

**1 Sep 2026** · Added

A new WebhookService pushes order and quote events to an HTTPS endpoint you subscribe. Eight event types cover creation, updates, status changes, archival and quote conversion. A delivery names the document that changed and never carries it: the JSON body is id, type, occurred_at and data — data.order.id or data.quote.id (an order for quote.converted_to_order), plus data.previous on transitions (status_id, quote, or archived) — so read the document back with GetOrder or GetQuote; an archived order needs includeArchived. Every delivery is signed: the whsec_ secret from the create response, shown exactly once, is the HMAC-SHA256 key for the X-Factory-Signature header, and each request also carries X-Factory-Event (the type), Date (the same instant as the signed t, as an HTTP-date) and User-Agent: Factory-API/v1. Failed deliveries retry after 1 min, 5 min, 30 min, 2 h and 8 h — six attempts over about ten hours — before they exhaust, and a subscription with nothing delivered successfully for seven days is disabled until you re-enable it. Delivery records are queryable, so you can see what was sent, when, and what your endpoint said back — and a test operation sends a synthetic event on demand.

## Measurements get a real schema

**20 Aug 2026** · Changed

The loose measurement arrays on lines and kit components are now typed. measurements (was measurementItems) prices the line or component; asBuiltMeasurements (was actualMeasurementItems) records what production actually cut and drives cost, margin and stock — never the price. Both are arrays of MeasurementEntry: length, width and amount as decimal strings in the strategy’s native unit. Kit components drop initialMeasurementItems and sumLengths. asBuiltMeasurements is optional and defaults to measurements on create and update alike, so payloads that copied one into the other can simply stop.

## Kit totals now have to add up

**20 Aug 2026** · Changed

The server now checks the maths inside standard kits instead of trusting it. Component totals have to match their own quantity, price and discount (or their measurements), a parent or sub-kit total has to equal what its pieces add up to, and every priced component needs its own totalPrice even when you priced the parent yourself. Custom-priced kits always send a total now — leaving it out used to store $0.00 silently. One field to watch: customPrice overrides a component’s whole total, not its unit price, so if you send it alongside totalPrice the two have to agree. When something doesn’t line up you get a 400 naming the component with both numbers, so you can fix the payload instead of hunting through a stored document.

## Line totals can be derived server-side

**18 Aug 2026** · Changed

Rolling out account by account: with derivation enabled, omit a line’s totalPrice and the server computes it — and rejects a supplied total that does not match, naming the line, instead of storing it. Catalogue lines can also omit unitPrice and send cost plus markup. Flashing, custom-formula and custom-priced-kit totals are never derived — always send those. Until derivation is enabled for your account nothing changes. Field descriptions across LinePricing now spell out every rule.

## Stock entries identify their variant

**12 Aug 2026** · Added

Every stock entry now carries thickness and attributes — the same identifying name/value pairs the catalogue product row holds — so entries can be told apart without a catalogue lookup. Applies to ListInventory, GetProductInventory and the SetStockLevels response alike. Entries were always per variant row per colour, never product totals; the endpoint descriptions now say so explicitly.

## Inventory and supplier APIs

**10 Aug 2026** · Added

New InventoryService with three operations: list stock, get stock for one product, and set stock levels. New SupplierService with two read-only operations: list suppliers and get one supplier. Products now carry an isInventoryTracked flag indicating whether stock is tracked.

## More filters on the order and quote lists

**7 Aug 2026** · Added

ListOrders and QueryOrders accept statusName, receivedStatus, fulfilmentMethod, and a requiredAfter/requiredBefore window; ListOrders also gains statusId and paymentStatus, which QueryOrders already had. ListQuotes and QueryQuotes accept fulfilmentMethod, requiredAfter and requiredBefore. The two order endpoints now filter on nearly the same set.

## Field filtering on responses

**7 Aug 2026** · Added

Every endpoint can trim its response with fields and excludeFields — a comma-separated list of camelCase names, with dot notation for nested fields. Names that do not exist on the resource are ignored rather than rejected, and fields takes precedence when both are supplied. Reads take them as query parameters; creates, line adds, label writes and drawing uploads take them as request body fields instead.

## One error envelope for every failure

**6 Aug 2026** · Changed

Every error response now shares the same envelope with a stable type field — validation_failure, not_found, conflict, rate_limited. Creates that include flashing lines return a drawings mapping pairing each tempId with its assigned drawingId, so SVG uploads no longer need a re-read. The change feeds accept createdAfter and createdBefore. Adding a flashing line to an existing order is explicitly unimplemented and returns 501.

## Labels on create, and filtering by label

**5 Aug 2026** · Added

CreateOrder and CreateQuote accept labelIds, so a document can be labelled as it is created. ListOrders and ListQuotes accept labelIds to return only documents carrying at least one of the given labels.

## Editable order lines, labels and archiving

**5 Aug 2026** · Added

Order line items can now be added, replaced and removed after creation, matching quotes. Labels can be read with ListLabels and attached to orders and quotes. Archived documents are reachable through new archived and includeArchived flags, and the change feeds accept an updatedBefore upper bound.

## v1 draft-005 published

**29 Jul 2026** · Draft

Quote line item add, update and remove; catalogue search across products, kits and flashings; custom field definitions on the company endpoint.

## Change feeds for orders and quotes

**11 Jun 2026** · Added

QueryOrders and QueryQuotes join the list endpoints with a stable oldest-first ordering and an updatedSince window, so a poller can checkpoint safely.

## Flashing drawing uploads

**02 May 2026** · Added

Attach client-rendered SVGs to an order or quote after creation. Each drawing reports its own outcome.

---

Source: https://developer.factory.app/changelog · Factory Sales API v1
