Production Routing API migration guide

What API consumers must change before upgrading to the production routing release.

All changes below land together in one release.

Overview

This release introduces production routing: a part can now have multiple work orders (production runs), each carrying its own routing — the ordered sequence of operations it follows.

  • Phase 1 — required before you upgrade: hard breaks. Requests fail, return wrong results, or silently drop data until you migrate.
  • Phase 2 — review before you upgrade: same shapes, changed meaning. Your calls keep working but may not do what they used to.
  • Phase 3 — migrate at your convenience: renamed fields where the old keys are still emitted. They will be removed in a future release.

A test environment with the new API is available before the release — validate Phases 1 and 2 there before upgrading (checklist at the end).

Phase 1 — required before you upgrade

1.1 workOrders replaces workflow on requisition payloads

Anywhere you read workflow: [{statusId, sequence}] on a requisition (GET /requisition, /{id}, order, backlog, part-specification/{specificationId}, production-step/{stepId}, operation/{operationId}), you'll now find:

workOrders: [
  {
    id,                  // UUID — the work-order identity (you'll need it for move-batches)
    name,                // optional
    quantity,
    routingTemplateId,   // nullable — null = routing was derived automatically
    routing: [{operationId, sequence}],
    createdAt
  }
]

Migrate: read a part's steps from workOrders[].routing[]. Handle multiple entries per requisition (multi-run is the point of the release) and treat routingTemplateId: null as "automatic". The old workflow key is gone — no fallback.

1.2 Looking up parts by operation — endpoint replaced

GET /api/manufacturer/v1/requisition/production-status/{id} returns 404. Use GET /api/manufacturer/v1/requisition/operation/{id} — same response shape.

1.3 Moving batches — identity changed from requisition to work order

PATCH /api/manufacturer/v1/production-step/move-batches: each batches[] entry must now send workOrderId (UUID, from requisition.workOrders[].id) instead of requisitionId (number). The destination key is toOperationId (the old toStatusId is still accepted).

1.4 Renamed query parameters — old names no longer work

EndpointNew paramOld param (no longer accepted)
GET .../operation/batch-balances/countoperationIdsstatusIds
GET .../production-step/batch-balances/status-idsoperationIdsproductionStatusIds

The new parameter is required — a request still sending only the old name fails with 400 (missing operationIds).

1.5 Renamed request fields — old keys are silently dropped on writes

  • Creating/updating an operation ("production status"): send name instead of statusName.
  • Process prices and post-processing create/update: send operationIds instead of productionStatusIds. Process-prices responses also now return operationIds only — the old key is not echoed back.

1.6 Empty routing now rejected

Creating a work order whose routing would have zero steps returns 400 (error code EMPTY_ROUTING). If you create work orders programmatically, handle the 400.

Phase 2 — review before you upgrade (behavior changes)

2.1 Bulk scheduling — pick your path deliberately

                     do you re-call bulk scheduling on parts
                     that may already be scheduled?

          ┌────── yes ──────────────┴────────────── no ──────┐
          ▼                                                  ▼
STAY on the legacy paths                          either path works; the new
POST .../requisition/batch-status                 canonical path is
POST .../requisition/create-batches               POST .../requisition/work-orders
          │                                                  │
unchanged semantics, kept routable                ALWAYS creates a new work
indefinitely: a part with an existing             order per part — NOT idempotent;
work order and no routing fields is               re-calling mints duplicates
reused/skipped (all-skipped → 400);
explicit routingTemplateId/operationIds
creates a new work order

To add quantity to an existing work order, use POST /work-orders/{id}/parts — unchanged, and bulk scheduling never tops up on either path.

2.2 Webhooks order.created / order.updated

  • Each part now carries a workOrders array: [{id, name, quantity, productionSteps: [{name, sequence}]}] — one entry per production run.
  • The part-level productionSteps field still exists but now reflects the first work order's routing and is empty until a work order exists. If you read it, switch to workOrders[].productionSteps.

2.3 Approval gate — only if you enable the setting

Only relevant if Require approved routing templates for production is switched ON in operator settings (it's OFF by default — leave it off and nothing here applies):

  • Scheduling a part whose resolved routing isn't an approved template returns 409 with error code NO_APPROVED_ROUTING_TEMPLATE and the affected spec ids.
  • PATCH /work-orders/{id}/routing is gated the same way.
  • The order-status automation that schedules parts skips gated parts (with a log warning) and schedules the rest — it no longer fails the whole order. If your flow assumes all-or-nothing scheduling, account for partial results.

2.4 routingTemplateId is nullable

On every work-order payload, routingTemplateId: null means the routing was derived automatically from the process configuration. Don't assume the field is present and non-null.

Phase 3 — migrate at your convenience (old keys still emitted, removed in a future release)

Responses use "operation" naming and additionally emit the old keys, so existing readers keep working for now:

ResourceNew keysOld keys (still present, deprecated)
Batch balancesoperationId/Name, new workOrderIdbatchStatusId/Name
Batch transitionsfromOperationId/Name, toOperationId/Name, new workOrderIdfromStatusId/Name, toStatusId/Name
Production stepsoperationId/NameproductionStatusId/Name
OperationsnamestatusName
Operations endpoint/api/manufacturer/v1/operation/production-status path still served

Plan to move off the old keys before they're removed; don't build anything new on them.

Unchanged — safe to rely on

  • POST /api/manufacturer/v1/work-orders/{id}/parts — adding quantity to an existing work order works exactly as before.
  • Traveller documents — untouched.
  • Process-catalog workflow (process configuration) — unchanged; only the requisition-level workflow was replaced (Phase 1.1).

New, optional — available if useful

  • Routing templates on a part specification: GET/POST /part-specifications/{specId}/routing-templates, PATCH /routing-templates/{id}, POST /routing-templates/{id}/approve | /default | /deprecate, DELETE. Templates freeze once a work order uses them (edits return 409); isDefault marks the template used for future work orders.
  • Work orders: POST .../requisition/{requisitionId}/work-order (additional run; optionally pin routingTemplateId or supply operationIds), PATCH /work-orders/{id}/routing.
  • GET /part-specification/{id}/derived-routing — preview the routing the system would derive.
  • PATCH /order/{id}/kanban-column gains optional skipAutomationTypes: string[] (e.g. "SCHEDULE_PARTS_FOR_PRODUCTION") to skip named column automations for one request. Omit it and behavior is unchanged.

Pre-release test checklist

Run against the test environment before the release:

  • Requisition reads parse workOrders[].routing[] and tolerate multiple work orders + routingTemplateId: null (1.1)
  • No remaining calls to /requisition/production-status/{id} (1.2)
  • move-batches sends workOrderId UUIDs (1.3)
  • Batch-balance queries use operationIds and return expected (filtered) counts (1.4)
  • Operation/process-price/post-processing writes use name / operationIds and the values persist (1.5)
  • Work-order creation handles 400 EMPTY_ROUTING (1.6)
  • Re-running your scheduling automation does not create duplicate work orders (2.1)
  • Webhook consumers read workOrders[].productionSteps (2.2)

Questions or anything unclear — happy to jump on a call before the release.

Last updated on