Record Production
POST/api/manufacturing/manufacturing-orders/:id/record-production
Record actual component consumption, output produced, and operation actuals for an in-progress MO. Posts inventory movements (consumption + receipts) and creates FIFO layers for outputs.
manufacturing:writeGrant this scope to your token under Settings → Developer → Personal Access Tokens.
Lot tracking validation (B23/B24):
outputs[].batch_numberis required when the output product isis_lot_tracked = true.outputs[].expiry_dateis required when the output product haslot_tracking_method = fefo.- Validation errors are returned per-output with dot notation, e.g.
outputs.0.batch_number.
The MO Show endpoint (GET /api/manufacturing/manufacturing-orders/{id}) exposes output_lines[].product and component_lines[].product with is_lot_tracked, lot_tracking_method, shelf_life_days, near_expiry_threshold_days, and min_remaining_shelf_life_days so clients can drive UI gating before submitting.
Component lot consumption override (FEFO suggest-with-override):
- Each
components[]entry may carry an optionallot_allocation[]of{fifo_layer_id, quantity}to choose exactly which lots of a lot-tracked component are consumed. Omit it to consume by automatic FEFO/FIFO. - The allocation must reconcile to that component's total draw this run (
actual_consumed_quantity + actual_scrap_quantity), reference active FIFO layers of that component product + the MO's source warehouse, and respect the tenant's lot-override policy (underenforce, consuming expired stock or a later-expiry lot for a FEFO product is hard-blocked). - The legacy
components[].fifo_layer_ids[](restrict-eligibility, FEFO-ordered) is still accepted;lot_allocation[]takes precedence when both are present. - Validation errors are returned per row with dot notation, e.g.
components.0.lot_allocation.1.fifo_layer_id.
Over-consumption guard (BQ8): if components[].actual_consumed_quantity exceeds the quantity reserved (Allocated) for that component line at Start, the call returns 422 with code = over_consumption and an offending[] array (per-component requested_quantity vs allocated_quantity) instead of a 500. The whole call rolls back — MO status and consumption are unchanged.
Destination bin: each output may optionally set warehouse_location_id — the bin the produced quantity is received into. When omitted, the assembly's preferred bin (or the warehouse's default storage location) is used.
Request components (present, may be empty): the components array must always be present, but it no longer needs at least one entry. Only forward_flush component lines need entries — omit backflush lines entirely (they are consumed automatically, prorated to the recorded output), and submit "components": [] when every component line on the order is backflush. Including an entry for a backflush line acts as a manual override of the automatic quantity for that run.
Backflush components: component lines with consumption_method = backflush are consumed automatically — for each backflush line without an explicit components[] entry, the recorded output is converted to that line's BOM-implied usage (per-unit usage multiplied by cumulative primary output produced — deliberately uncapped, so over-yield beyond the planned quantity consumes proportionally more — minus what earlier production records already consumed) and issued through the same FIFO/lot machinery as operator entries. Consumption beyond the quantity reserved at confirmation automatically tops up the line's allocation from free (unreserved) stock; when free stock cannot cover the excess the request fails with the same structured 422s as a manual over-issue (code = insufficient_inventory when the stock physically does not exist, code = over_consumption when it exists but is reserved by other documents) and the whole call rolls back. Submitting an explicit components[] entry for a backflush line overrides the automatic quantity for that run. forward_flush lines are only consumed when an explicit entry is submitted.
Response cost_breakdown: the returned manufacturing order carries a cumulative cost_breakdown object: materials (FIFO value consumed so far), labor (time-based operation labour), overhead (labour multiplied by the work-center overhead percent), fixed (flat per-run operation costs — never marked up by overhead), service_fee and landed_cost (outsourced orders only), total, allocation_method, recorded_at, and per-output allocation rows in outputs[].
Operation costing without a timer: each operations[] row in the response reports fixed_cost, actual_cost, and cost_source — none (no cost recorded), actual (from logged time or a flat fixed-cost operation), estimated (fell back to the planned duration because no time was logged), or manual (an explicitly supplied cost that is never auto-recalculated — only another explicit cost, or an explicitly logged positive time, replaces it).
Insufficient stock guard: if a component's FIFO layers in the MO's source warehouse cannot cover the quantity to consume (forward-flush or backflush), the call returns 422 with code = insufficient_inventory and an offending[] array (product_id, product_sku, product_name, warehouse_id, requested_quantity, available_quantity). The whole call rolls back — no movements, FIFO layers, or production iterations are written.
Explicit lot allocation shortages: shortage failures inside an explicit lot_allocation[] return the same 422 code = insufficient_inventory envelope (previously an internal error). A named lot that is no longer an active layer for the component at consumption time, or a lot that cannot cover its requested quantity, reports the usual product_id, product_sku, product_name, warehouse_id, requested_quantity, available_quantity row. A lot-level overdraw detected mid-consumption (e.g. duplicate entries naming the same lot whose combined quantity exceeds its availability) identifies the lot with fifo_layer_id and reports the layer's real state at the failed draw — layer_original_quantity, layer_fulfilled_quantity (includes quantities drawn earlier in the same rolled-back call), and layer_remaining_quantity — rather than a fabricated requested/available pair. The whole call rolls back.
Downward cost revision guard: operation cost revisions between production records are recognized as SIGNED deltas on the next iteration's journal — negative buckets post as balanced flipped legs (e.g. a debit to Direct Labor Applied) and a net-zero reclassification still posts its offsetting legs. When a negative conversion delta exceeds the value this run's output FIFO layers can absorb (a layer's cost would drop below zero), the call returns 422 with code = cost_revision_exceeds_layer_value, the signed conversion_delta, and an offending[] array (output_line_id, fifo_layer_id, product_id, product_sku, product_name, layer_cost, revision_share, resulting_cost). The whole call rolls back — reverse the over-costed earlier iteration and re-record it with the corrected operation costs.
Request
Responses
- 200
- 401
- 403
- 404
- 422
- 429
OK
Response Headers
Unauthenticated — the bearer token is missing, revoked, expired, or malformed. Never retry automatically; fix the credential. See the Errors guide.
Forbidden — the token lacks a required scope, the endpoint is not available to API tokens, or the user behind the token lacks the permission. A human must adjust the token scopes or user permissions; do not retry.
Not found — no record with the given identifier (or the route does not exist). Verify the ID before retrying.
Unprocessable Entity
Response Headers
Rate limited — platform limit is 1,000 requests/min; individual tokens may carry lower limits. Honor the Retry-After header before retrying. See the Rate Limits guide.