ARCNM

Recipes

Recipe — teach an environment from your actuals

Close the calibration loop over the API. POST your realised costs, recalibrate an environment, and read the tightened prediction interval.

Adaptive Calibration is what turns a generic benchmark into a cortex for your shop. The dashboard drives it interactively; this recipe drives the same thing over the API, so you can wire it into your ERP close or a nightly job.

The loop:

  1. Collect realised costs — work-order actuals from your ERP / MES.
  2. POST them to a costing environment.
  3. ARCNM re-quotes each part, fits the delta, and tightens the interval.
  4. Every subsequent quote on that environment uses the calibrated fit.

Calibration writes need parts:write; the status read needs parts:read.


1. The one-call path — actual unit costs

If you already have per-part actual unit costs, POST them to auto-calibrate. You don't supply a predicted value — the service pairs each part with its most recent quote in this environment and fits the difference:

import os, requests

BASE = "https://api.arcnm.io/api/v1"
H = {"X-API-Key": os.environ["ARCNM_API_KEY"]}   # scope: parts:write

r = requests.post(
    f"{BASE}/parts/calibration/environments/{env_id}/auto-calibrate",
    headers=H,
    json={"actuals": [
        {"part_revision_id": "1f…", "actual_unit_cost": 12.40, "lot_size": 50},
        {"part_revision_id": "2a…", "actual_unit_cost": 8.10,  "lot_size": 100},
    ]},
)
r.raise_for_status()
report = r.json()
# { matched, unmatched_part_ids, n_train, n_holdout, holdout_mape, holdout_coverage, … }

Pass lot_size whenever you have it (the work-order quantity) — without it the fit can't separate one-time setup from per-unit cost. Parts with no prior quote come back in unmatched_part_ids; quote them once, then retry.


2. The ERP-native path — raw work orders

Most ERPs export work orders, not unit costs. auto-calibrate-from-erp takes the raw columns (SAP AUFK/AFKO, Oracle WIP_DISCRETE_JOBS) and does part-number resolution + unit-cost derivation for you:

r = requests.post(
    f"{BASE}/parts/calibration/environments/{env_id}/auto-calibrate-from-erp",
    headers=H,
    json={"work_orders": [
        {"part_number": "BRACKET-001", "work_order_id": "WO-5512",
         "quantity": 50, "total_cost": 620.00},
    ]},
)

work_order_id is the idempotency key — re-POSTing the same work order is a no-op, so you can stream your last 12 months once and replay safely. Unknown part numbers come back in unknown_part_numbers.


3. Check calibration health

GET .../environments/{env_id}/status is the safe, read-only endpoint to call first (and after) — it reports whether the env is calibrated, when it last ran, how many observations are queued, and any drift alerts:

{
  "env_id": "env_2a…",
  "active_posterior_count": 36,
  "last_fit_at": "2026-05-28T14:00:00Z",
  "observations": [{ "oracle": "erp", "target_metric": "unit_cost", "count": 142 }],
  "conformal": [{ "target_metric": "unit_cost", "observed_coverage": 0.91 }],
  "drift_alerts": []
}

active_posterior_count: 0 means the env is still on platform benchmarks — submit a batch to calibrate it.


4. Read the tightened interval

Once calibrated, every quote on that environment carries a narrower quote-level interval (see Audit & provenance). Use it to set a margin buffer, or to trigger a re-quote when it's too wide for the customer:

{ "unit_cost": 12.84, "currency": "EUR", "interval": [12.20, 13.50] }

Pick the right entry point

Endpoint Use it when
auto-calibrate You have per-part actual unit costs.
auto-calibrate-from-erp You have raw work orders (quantity + total cost).
teach You have explicit (predicted, actual) pairs.
learning-curve-fit You have lot-progression actuals for one part.
conformal-recalibrate You only want to refresh the prediction interval.
status Always — read calibration health before and after a run.

See also