GoSmarter Logo
Versioning & Changes

API Changelog

Customer-facing log of changes to the GoSmarter API. This tracks changes that affect API consumers — request/response shapes, new endpoints, deprecations, and removals. For internal engineering history (refactors, infrastructure, non-API-surface changes), see the repository's CHANGELOG.md instead.

See the API Versioning Policy for how breaking changes are classified and the deprecation notice period they follow.

Entries are grouped by release date and categorized as Added, Changed, Deprecated, or Removed.

Changes to Preview endpoints are listed separately under Preview in each release, prefixed Added, Changed, Removed or Graduated. A Preview endpoint may change or be removed without a v2/ route or a notice period; a Graduated entry announces that it is now stable, and from then on it follows the versioning policy like any other endpoint.


Unreleased

Added

  • GET /api/company/{companyId}/inventory-reference/statuses — lists the fixed inventory lifecycle statuses (InventoryStatusId, Label: Available, Reserved, Consumed, Scrapped).
  • DELETE /api/v2/company/{companyId}/inventory-slim — idempotent version of the inventory delete. Items that belong to your company but are already deleted now count as deleted: the request succeeds (200) and lists them in a new AlreadyDeletedIds field, instead of failing with 404. A 404 now only means an id doesn't exist or belongs to another company, and nothing is deleted. An empty id array is rejected with 400. DELETE /api/company/{companyId}/inventory-slim is unchanged.
  • GET/PUT /api/company/{companyId}/gs1-settings — read or set the company's GS1 Company Prefix.
  • GET /api/company/{companyId}/production-plans/{planId} — each bar has a new optional ReservedInventoryId field: the inventory row its approval reservation holds, or null.
  • GET /api/company/{companyId}/production-plans and GET /api/company/{companyId}/production-plans/{planId} — each plan has a new optional ApprovedByName field: the name of the user who approved it, or null when the plan isn't approved or that user has no name on record. In the single-plan response each bar also has new optional StartedByName and CompletedByName fields, with the same meaning for the user who started or completed the bar.
  • GET /api/company/{companyId}/subscription and GET /api/goadmin/companies/{companyId}/subscription — new optional billing-state fields: SubscriptionStatus (active, trialing, past_due, unpaid, paused, incomplete, incomplete_expired, canceled), PastDueSince (when a payment failure started the dunning state, or null) and DunningGraceEndsAt (when the grace window ends and paid features lapse, or null). past_due, unpaid and incomplete keep access until DunningGraceEndsAt.

Preview

  • Added: GET /api/company/{companyId}/order-line-item-tag-requirements, GET/PUT /api/company/{companyId}/order-line-items/{orderLineItemId}/tag-requirements — read or set the inventory tags an order line item requires (hard: the optimisation solver only cuts the line from stock carrying every required tag) or prefers (soft: preferred stock scores higher). PUT replaces the whole set; send both requiredTagIds and preferredTagIds (empty lists clear it). A tag cannot be in both lists and must be an active inventory tag of the company (otherwise 400); an unknown line item returns 404. Responses are { orderLineItemId, tagId, requirement: "Required" | "Preferred" }.
  • Changed: optimisation problem/result — demand lines can now be reported as unmatched with reason NoStockWithRequiredTags when no stock in their group carries every required tag, and solverSettings.priorities.tagPreference (optional, finite and >= 0, default off) weights the preferred-tag objective; a negative or non-finite value returns 400.
  • Added: GET /api/company/{companyId}/onboarding/progress — returns the trial onboarding checklist progress as six booleans derived from the company's real data: millCertUploaded, inventoryCreated, orderWithLineItemCreated, stockReserved, certificatePackGenerated and hexUsed. Soft-deleted records do not count. Also returns companyCreatedAt, firstPackAt and timeToFirstPackSeconds (time to first certificate pack; null until a pack exists).
  • Added: GET/PUT /api/company/{companyId}/settings — read or set company-level settings kept outside the company profile; currently KerfMm, the saw-blade width lost per cut (mm) that optimisation runs apply. PUT is company admins only, requires KerfMm between 0 and 20 (rounded to 2 decimal places, otherwise 400), and changes no other company field. PUT /api/company/{companyId} never reads or writes KerfMm.
  • Added: POST /api/company/{companyId}/inventory/{inventoryId}/physical-identifiers — issues a governed physical identifier for an inventory item, or returns the item's existing one (idempotent reprint); reissue: true replaces it. With no identifierType, the item's current identifier type is reused; an item with no identifier gets the default type: a serialised GTIN (SGTIN, returned as GTIN) when the company has a GS1 Company Prefix — which needs a gtin, otherwise 400 — or an internal code when it has none. For GTIN identifiers, gtin may be omitted once the item has one, and a supplied gtin must match it (409). Returns 404 for a deleted item.
  • Added: GET /api/company/{companyId}/inventory/{inventoryId}/physical-identifiers and GET /api/company/{companyId}/physical-identifiers/lookup?payload=… — list an item's identifiers, or resolve a scanned payload. isCurrent reflects whether the identifier has been replaced; inventoryActive is false when the identified item has been deleted.
  • Added: POST /api/company/{companyId}/production-plans/{planId}/unapprove — returns an Approved production plan to Draft and releases the stock its approval reserved. Only allowed before any bar has started (400 otherwise). Returns PlanId, PlanStatus and ReleasedLinkCount.
  • Added: POST /api/company/{companyId}/optimisation/runs and POST /api/company/{companyId}/optimisation/runs/preview — compose an optimisation problem from filters (or dispatch a previously-previewed snapshot via PreviewId, within 30 minutes) and start a solver run; preview returns a per-group summary without dispatching anything. GET /api/company/{companyId}/optimisation/runs, GET .../runs/{runId} and POST .../runs/{runId}/cancel list, poll and cancel runs.
  • Changed: POST /api/company/{companyId}/optimisation/runs and POST /api/company/{companyId}/optimisation/runs/preview — solverSettings.algorithm must now be omitted, blank or ga (the solver only runs its genetic algorithm); any other value returns 400 instead of being silently ignored. A saved filter profile's stored algorithm is replaced by ga when a run starts, and the run's result solverInfo.algorithm always reports ga. solverSettings.priorities.stockConsumption and wasteVsSimplicity are still accepted but have no effect on the solver.

Changed

  • PUT /api/company/{companyId}/inventory/{inventoryId}/millcert-links — a request that lists the same (MatchKeyType, MatchedValue) key more than once (compared case-insensitively, after trimming, whichever heat each entry names) now returns 400 naming the duplicate positions, instead of 500. If a concurrent request changes the same item's link for the same key, it returns 409 Conflict and saves nothing, instead of 500; retry after reloading. Previously both cases were server errors, so no working client is affected.
  • Audit history endpoints (.../history, and the audit log) — OldValue/NewValue for date-time fields (for example OrderDate, DeliveryDate, ArchivedAt) are now ISO 8601 UTC strings such as 2026-10-09T13:43:21.000Z, instead of US-format 10/09/2026 13:43:21, which UK readers misread. The fields remain open-ended strings; numbers, booleans and text are unchanged. Parse the new format as UTC.
  • GET /api/company/{companyId}/millcertification/{millCertificationId} — now fills in fields the response already declared but always returned empty: the linked supplier and mill organisations (SupplierOrganisationId, SupplierOrganisationName, MillOrganisationId, MillOrganisationName) and the archive state (IsArchived, ArchivedAt). A link to an organisation that has since been deleted is reported as unlinked (null id and name). Saving the whole certificate back no longer clears an existing organisation link.
  • POST /api/company/{companyId}/production-plans/{planId}/discard — now also releases the stock reservations the plan's approval created (they previously stayed reserved after discard). Reservations made before approval are kept. Discarding is now refused with 400 once any bar has started, and also when the calling user can't be identified (discard is now attributed to a user).
  • DELETE /api/company/{companyId}/inventory-slim/{inventoryId}/reserve and DELETE /api/company/{companyId}/orders/{orderId}/line-items/{lineItemId}/reserve/{inventoryId} — return 409 Conflict, and change nothing, when a live production plan with no started bar holds the reservation. If the plan created the reservation, discard or un-approve the plan to release it. If the plan reused a reservation you made by hand, discard or un-approve the plan first and then unreserve again: your reservation is kept. The 409 message says which case applies.

Applied in place (production plan reservations). The Changed entries above (discard releasing the stock its plan reserved and being refused once a bar has started, and the two unreserve 409s) correct reservation behaviour that did not hold the platform's stock-reservation invariant: stock held by an Approved plan could be unreserved, or stay reserved after discard. Approving a plan now also rejects an inactive Draft plan. Because these fix incorrect behaviour rather than add a new contract, they are applied to the existing routes instead of new v2/ routes.

Deprecated

  • GET/POST /api/company/{companyId}/email-domains, DELETE /api/company/{companyId}/email-domains/{domainId} and POST /api/company/{companyId}/email-domains/{domainId}/restore — retired with no replacement, because joining a company by email domain has been withdrawn. The three write routes already return 410 Gone; the GET still lists existing registrations and now sends Deprecation and Sunset headers. All four now carry a sunset date in the OpenAPI document. Sunset date: 2027-01-31. See the migration guide.

{/* Template for a new entry — copy this block above the previous entry when a change ships:

YYYY-MM-DD

Added

  • POST /api/v2/company/{companyId}/example — short description of the new capability.

Changed

  • GET /api/company/{companyId}/example — short description of a backward-compatible change (e.g. a new optional field).

Deprecated

  • GET /api/company/{companyId}/example — deprecated in favour of GET /api/v2/company/{companyId}/example. Sunset date: YYYY-MM-DD. See the migration guide.

Removed

  • GET /api/company/{companyId}/old-example — removed after its notice period ended on YYYY-MM-DD.

Preview

  • Added: POST /api/company/{companyId}/example — a new Preview endpoint; may change before it is stable.
  • Changed: GET /api/company/{companyId}/example — what changed, including any change that would be breaking for a stable endpoint.
  • Removed: GET /api/company/{companyId}/old-preview-example — Preview endpoint withdrawn.
  • Graduated: GET /api/company/{companyId}/example — now stable; from this release it follows the versioning policy (frozen v1). */}
Last modified on