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
| Endpoint | New param | Old param (no longer accepted) |
|---|---|---|
GET .../operation/batch-balances/count | operationIds | statusIds |
GET .../production-step/batch-balances/status-ids | operationIds | productionStatusIds |
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
nameinstead ofstatusName. - Process prices and post-processing create/update: send
operationIdsinstead ofproductionStatusIds. Process-prices responses also now returnoperationIdsonly — 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 orderTo 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
workOrdersarray:[{id, name, quantity, productionSteps: [{name, sequence}]}]— one entry per production run. - The part-level
productionStepsfield still exists but now reflects the first work order's routing and is empty until a work order exists. If you read it, switch toworkOrders[].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_TEMPLATEand the affected spec ids. PATCH /work-orders/{id}/routingis 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:
| Resource | New keys | Old keys (still present, deprecated) |
|---|---|---|
| Batch balances | operationId/Name, new workOrderId | batchStatusId/Name |
| Batch transitions | fromOperationId/Name, toOperationId/Name, new workOrderId | fromStatusId/Name, toStatusId/Name |
| Production steps | operationId/Name | productionStatusId/Name |
| Operations | name | statusName |
| 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-levelworkflowwas 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 return409);isDefaultmarks the template used for future work orders. - Work orders:
POST .../requisition/{requisitionId}/work-order(additional run; optionally pinroutingTemplateIdor supplyoperationIds),PATCH /work-orders/{id}/routing. GET /part-specification/{id}/derived-routing— preview the routing the system would derive.PATCH /order/{id}/kanban-columngains optionalskipAutomationTypes: 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-batchessendsworkOrderIdUUIDs (1.3) - Batch-balance queries use
operationIdsand return expected (filtered) counts (1.4) - Operation/process-price/post-processing writes use
name/operationIdsand 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