ARCNM

API reference

Calibration

The Calibration API teaches an environment your real costs: submit observations and auto-calibrate its prediction interval, learning curve, and machine policy…

The Calibration API teaches an environment your real costs: submit observations and auto-calibrate its prediction interval, learning curve, and machine policy from your actuals.

Auto-generated from the public OpenAPI spec — this page never drifts from the running API. Base URL https://api.arcnm.io. Authenticate with the X-API-Key header (see Authentication).

One-click environment calibration from (part_revision_id, actual_unit_cost) rows.

POST /api/v1/calibration/environments/{env_id}/auto-calibrate

The headline customer-facing endpoint — turn a list of ERP actual unit costs into a calibrated environment in one call. Each actual is paired with the most recent successful quote for its part at its lot size in this environment, priced by the current version of the costing model, and the calibration is fitted to the difference; nothing is re-quoted. Parts with no such quote — none at all, or only one priced before the costing model last changed — are returned in unmatched_part_ids so you can quote them and retry. Safe for AI-agent use over MCP and idempotent: the same set of actuals yields the same result.

Parameters

Name In Type Required Description
env_id path string yes Identifier of the env.

Request body (application/json)

Field Type Required Description
actuals AutoCalibrateActualRow[] yes Up to 20,000 (part_revision_id, actual_unit_cost) rows. Designed for ERP exports: an AI agent or a CSV uploader can stream a tenant's history in one call.
basis herstellkosten | selbstkosten | angebotspreis no Which rung of the cost sheet these actuals are measured at: 'herstellkosten' (manufacturing cost per unit — what a work order books), 'selbstkosten' (total cost per unit, including administration, selling, freight and duty) or 'angebotspreis' (the quoted price per unit, including the profit mark-up). Each is compared against the model's own figure at the same rung. Omit it only if you genuinely do not know: an unstated basis is read as manufacturing cost and the environment's calibration report flags how many rows were unstated.
holdout_pct number no Fraction of rows held out to size the prediction interval.
target_alpha number no Target miss rate for the prediction interval (default 0.10 → a 90% interval).

Request

curl -X POST https://api.arcnm.io/api/v1/calibration/environments/{env_id}/auto-calibrate \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "actuals": [
      {
        "actual_unit_cost": 0,
        "annual_volume": 500,
        "bin": "string",
        "lot_size": 50,
        "part_revision_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "tooling_lifetime_units": 0
      }
    ]
  }'
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/calibration/environments/{env_id}/auto-calibrate",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "actuals": [
            {
                "actual_unit_cost": 0,
                "annual_volume": 500,
                "bin": "string",
                "lot_size": 50,
                "part_revision_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
                "tooling_lifetime_units": 0
            }
        ]
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/calibration/environments/{env_id}/auto-calibrate", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "actuals": [
      {
        "actual_unit_cost": 0,
        "annual_volume": 500,
        "bin": "string",
        "lot_size": 50,
        "part_revision_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "tooling_lifetime_units": 0
      }
    ]
  }),
})
const data = await resp.json()

Responses

Status Description
200 Successful Response
422 Validation Error

Errors

Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.

Status Code When
401 invalid_api_key Missing, malformed, or revoked API key.
403 insufficient_scope The key is valid but lacks a scope this endpoint requires.
404 not_found A referenced resource doesn't exist or isn't visible to your organisation.
409 conflict A conflicting change, or an Idempotency-Key reused with a different body.
429 rate_limited Per-key or per-org rate limit exceeded — back off with jitter and retry.

Response body 200

Field Type Description
a number Fitted first-unit cost coefficient of the curve.
b number Fitted log-log slope exponent of the learning curve.
env_id string Identifier of the calibrated environment.
fit object Structured object with the fitted parameters and fit diagnostics.
fitted_accuracy number Accuracy (0–1) of the fitted policy on the training rows.
fitted_policy object Structured object describing the fitted selection policy.
grid_size integer Number of policy candidates evaluated during the fit.
history_received integer Historical choice records supplied in the request.
history_rejected object[] Records that could not be used, each with its position in the request and why. Capped at 50 entries; the counts above are never truncated.
history_used integer Records the fit actually used. Lower than history_received means some could not be read; see history_rejected.
holdout_accuracy number Accuracy (0–1) of the fitted policy on held-out rows.
holdout_coverage number Fraction (0–1) of held-out actuals falling inside the interval.
holdout_mape number Mean absolute percentage error of the PRE-calibration predictions on the held-out rows — the miss this run set out to close, not the outcome. Read mape_after for the result.
id string Identifier of the resulting record, when applicable.
ingested integer Number of observations persisted by an ingest call.
learning_rate number Learning rate (0–1): cost retained each time output doubles.
mape_after number Holdout MAPE AFTER the fit — the held-out rows re-priced under the calibrated result. The run's headline outcome; null when no rows were held out.
mape_before number Holdout MAPE before the fit (same figure as holdout_mape, named for the before/after pair).
matched integer Number of supplied rows matched to an existing quote.
message string Human-readable note about the run, when present.
model string Learning-curve model that was fit (crawford_unit or wright_cumulative).
n_holdout integer Number of rows held out to size the prediction interval.
n_iterations integer Iterations the midpoint solver ran before converging.
n_lots integer Number of production lots used in the curve fit.
n_observations integer Number of observations the calibration fit used.
n_train integer Number of rows used to fit the calibration.
part_revision_id string Identifier of the part revision that was fit.
policy_history_id string Identifier of the newly written selection-policy row.
r_squared number Coefficient of determination (0–1) of the curve fit.
residual_sigma_log number Standard deviation of the fit residuals in log space.
run_id string Identifier of the calibration run, when one was created.
samples integer Number of samples considered by the run.
samples_applied integer Number of samples actually applied to the fit.
scope string Scope the run applied to: 'platform' or 'environment'.
seed_accuracy number Accuracy (0–1) of the seed policy before calibration.
status string Outcome status of the calibration run.
target_metric string Metric the calibration targeted (e.g. 'unit_cost').
unknown_part_numbers string[] Part numbers that could not be resolved to a known part.
unmatched_part_ids string[] Part revision IDs with no quote at their lot size priced by the current version of the costing model, so they were skipped.
work_orders_received integer Number of ERP work-order rows received.
work_orders_resolved integer Number of work orders resolved to a known part revision.

Example response

{
  "a": 0,
  "b": 0,
  "env_id": "string",
  "fit": {},
  "fitted_accuracy": 0,
  "fitted_policy": {},
  "grid_size": 0,
  "history_received": 0,
  "history_rejected": [
    {}
  ],
  "history_used": 0,
  "holdout_accuracy": 0,
  "holdout_coverage": 0.9,
  "holdout_mape": 0.05,
  "id": "string",
  "ingested": 0,
  "learning_rate": 0,
  "mape_after": 0.05,
  "mape_before": 0.05,
  "matched": 0,
  "message": "string",
  "model": "string",
  "n_holdout": 0,
  "n_iterations": 0,
  "n_lots": 0,
  "n_observations": 0,
  "n_train": 0,
  "part_revision_id": "string",
  "policy_history_id": "string",
  "r_squared": 0,
  "residual_sigma_log": 0,
  "run_id": "string",
  "samples": 0,
  "samples_applied": 0,
  "scope": "string",
  "seed_accuracy": 0,
  "status": "string",
  "target_metric": "string",
  "unknown_part_numbers": [
    "string"
  ],
  "unmatched_part_ids": [
    "string"
  ],
  "work_orders_received": 0,
  "work_orders_resolved": 0
}

Calibrate an env from raw ERP work-order rows.

POST /api/v1/calibration/environments/{env_id}/auto-calibrate-from-erp

ERP-native variant of auto-calibrate: accepts the raw columns most ERPs export (part_number, work_order_id, quantity, total_cost) and handles part-number resolution + unit-cost derivation server-side. Idempotent on work_order_id.

Parameters

Name In Type Required Description
env_id path string yes Identifier of the env.

Request body (application/json)

Field Type Required Description
holdout_pct number no Fraction of rows held out to size the prediction interval.
target_alpha number no Target miss rate for the prediction interval (0.10 → a 90% interval).
work_orders ErpWorkOrderPayload[] yes Up to 20 000 raw ERP work-order lines. Designed for the AI-agent ERP-import use case: pull tenant's last 12 months of WIP_DISCRETE_JOBS rows, POST them once, and the service resolves part numbers, decomposes setup vs variable cost from the lot-size diversity, and returns a calibrated env.

Request

curl -X POST https://api.arcnm.io/api/v1/calibration/environments/{env_id}/auto-calibrate-from-erp \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "work_orders": [
      {
        "annual_forecast": 0,
        "part_number": "BRACKET-001",
        "quantity": 0,
        "total_cost": 642,
        "work_order_id": "string"
      }
    ]
  }'
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/calibration/environments/{env_id}/auto-calibrate-from-erp",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "work_orders": [
            {
                "annual_forecast": 0,
                "part_number": "BRACKET-001",
                "quantity": 0,
                "total_cost": 642,
                "work_order_id": "string"
            }
        ]
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/calibration/environments/{env_id}/auto-calibrate-from-erp", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "work_orders": [
      {
        "annual_forecast": 0,
        "part_number": "BRACKET-001",
        "quantity": 0,
        "total_cost": 642,
        "work_order_id": "string"
      }
    ]
  }),
})
const data = await resp.json()

Responses

Status Description
200 Successful Response
422 Validation Error

Errors

Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.

Status Code When
401 invalid_api_key Missing, malformed, or revoked API key.
403 insufficient_scope The key is valid but lacks a scope this endpoint requires.
404 not_found A referenced resource doesn't exist or isn't visible to your organisation.
409 conflict A conflicting change, or an Idempotency-Key reused with a different body.
429 rate_limited Per-key or per-org rate limit exceeded — back off with jitter and retry.

Response body 200

Field Type Description
a number Fitted first-unit cost coefficient of the curve.
b number Fitted log-log slope exponent of the learning curve.
env_id string Identifier of the calibrated environment.
fit object Structured object with the fitted parameters and fit diagnostics.
fitted_accuracy number Accuracy (0–1) of the fitted policy on the training rows.
fitted_policy object Structured object describing the fitted selection policy.
grid_size integer Number of policy candidates evaluated during the fit.
history_received integer Historical choice records supplied in the request.
history_rejected object[] Records that could not be used, each with its position in the request and why. Capped at 50 entries; the counts above are never truncated.
history_used integer Records the fit actually used. Lower than history_received means some could not be read; see history_rejected.
holdout_accuracy number Accuracy (0–1) of the fitted policy on held-out rows.
holdout_coverage number Fraction (0–1) of held-out actuals falling inside the interval.
holdout_mape number Mean absolute percentage error of the PRE-calibration predictions on the held-out rows — the miss this run set out to close, not the outcome. Read mape_after for the result.
id string Identifier of the resulting record, when applicable.
ingested integer Number of observations persisted by an ingest call.
learning_rate number Learning rate (0–1): cost retained each time output doubles.
mape_after number Holdout MAPE AFTER the fit — the held-out rows re-priced under the calibrated result. The run's headline outcome; null when no rows were held out.
mape_before number Holdout MAPE before the fit (same figure as holdout_mape, named for the before/after pair).
matched integer Number of supplied rows matched to an existing quote.
message string Human-readable note about the run, when present.
model string Learning-curve model that was fit (crawford_unit or wright_cumulative).
n_holdout integer Number of rows held out to size the prediction interval.
n_iterations integer Iterations the midpoint solver ran before converging.
n_lots integer Number of production lots used in the curve fit.
n_observations integer Number of observations the calibration fit used.
n_train integer Number of rows used to fit the calibration.
part_revision_id string Identifier of the part revision that was fit.
policy_history_id string Identifier of the newly written selection-policy row.
r_squared number Coefficient of determination (0–1) of the curve fit.
residual_sigma_log number Standard deviation of the fit residuals in log space.
run_id string Identifier of the calibration run, when one was created.
samples integer Number of samples considered by the run.
samples_applied integer Number of samples actually applied to the fit.
scope string Scope the run applied to: 'platform' or 'environment'.
seed_accuracy number Accuracy (0–1) of the seed policy before calibration.
status string Outcome status of the calibration run.
target_metric string Metric the calibration targeted (e.g. 'unit_cost').
unknown_part_numbers string[] Part numbers that could not be resolved to a known part.
unmatched_part_ids string[] Part revision IDs with no quote at their lot size priced by the current version of the costing model, so they were skipped.
work_orders_received integer Number of ERP work-order rows received.
work_orders_resolved integer Number of work orders resolved to a known part revision.

Example response

{
  "a": 0,
  "b": 0,
  "env_id": "string",
  "fit": {},
  "fitted_accuracy": 0,
  "fitted_policy": {},
  "grid_size": 0,
  "history_received": 0,
  "history_rejected": [
    {}
  ],
  "history_used": 0,
  "holdout_accuracy": 0,
  "holdout_coverage": 0.9,
  "holdout_mape": 0.05,
  "id": "string",
  "ingested": 0,
  "learning_rate": 0,
  "mape_after": 0.05,
  "mape_before": 0.05,
  "matched": 0,
  "message": "string",
  "model": "string",
  "n_holdout": 0,
  "n_iterations": 0,
  "n_lots": 0,
  "n_observations": 0,
  "n_train": 0,
  "part_revision_id": "string",
  "policy_history_id": "string",
  "r_squared": 0,
  "residual_sigma_log": 0,
  "run_id": "string",
  "samples": 0,
  "samples_applied": 0,
  "scope": "string",
  "seed_accuracy": 0,
  "status": "string",
  "target_metric": "string",
  "unknown_part_numbers": [
    "string"
  ],
  "unmatched_part_ids": [
    "string"
  ],
  "work_orders_received": 0,
  "work_orders_resolved": 0
}

Per-driver calibrated-environment report for one environment.

GET /api/v1/calibration/environments/{env_id}/calibration-report

Calibration status for an environment: whether it has been calibrated to your actuals and, for the first-party app, the per-driver adjustments (machine / labour / overhead) with their confidence intervals. Public integrations receive the status only.

Parameters

Name In Type Required Description
env_id path string yes Identifier of the env.

Request

curl -X GET https://api.arcnm.io/api/v1/calibration/environments/{env_id}/calibration-report \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.get(
    "https://api.arcnm.io/api/v1/calibration/environments/{env_id}/calibration-report",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/calibration/environments/{env_id}/calibration-report", {
  method: "GET",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
  },
})
const data = await resp.json()

Responses

Status Description
200 Successful Response
422 Validation Error

Errors

Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.

Status Code When
401 invalid_api_key Missing, malformed, or revoked API key.
403 insufficient_scope The key is valid but lacks a scope this endpoint requires.
404 not_found A referenced resource doesn't exist or isn't visible to your organisation.
429 rate_limited Per-key or per-org rate limit exceeded — back off with jitter and retry.

Response body 200

Field Type Description
calibrated boolean True when this environment has been calibrated to your actuals.
drivers object[] Per-driver calibrated adjustments (machine / labour / overhead), each a structured object with the adjustment, its confidence interval, and whether it was applied.
env_id string Identifier of the environment this report describes.
material object Pinned (multiplier 1.0) in the per-driver adjustments: no machine, labour or overhead adjustment scales the material line. Where an environment is calibrated with a single overall correction of the unit cost instead, that correction scales material together with every other line. The price per kg is the one you state, or the platform's reference price where you state none; the quantity is the stock the part is cut from; scrap and remnant credits apply only once you state them, at your own scrap price or the platform's reference price where you state none.
note string Present on the public API only: points integrations at the app for the per-driver detail.
provenance object Fit provenance: last_fit_at, corpus_hash, n_effective, run id.
residual object The explicit residual multiplier (tooling/subcontract catch-all).
retired_drivers object[] Adjustments calibration no longer makes, each with the reason (for example material_quantity_modelled: the material quantity is modelled rather than adjusted). A correction stored for one is not applied, and the next calibration run removes it.

Example response

{
  "calibrated": true,
  "drivers": [
    {}
  ],
  "env_id": "string",
  "material": {},
  "note": "string",
  "provenance": {},
  "residual": {},
  "retired_drivers": [
    {}
  ]
}

Turn an environment's calibration off.

POST /api/v1/calibration/environments/{env_id}/deactivate

Retire the environment's active calibration without replacing it: quotes priced after this call use the uncalibrated defaults. The run history is kept, so any earlier run can be restored with the revert endpoint. Fails when no calibration is active.

Parameters

Name In Type Required Description
env_id path string yes Identifier of the env.

Request

curl -X POST https://api.arcnm.io/api/v1/calibration/environments/{env_id}/deactivate \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/calibration/environments/{env_id}/deactivate",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/calibration/environments/{env_id}/deactivate", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
  },
})
const data = await resp.json()

Responses

Status Description
200 Successful Response
422 Validation Error

Errors

Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.

Status Code When
401 invalid_api_key Missing, malformed, or revoked API key.
403 insufficient_scope The key is valid but lacks a scope this endpoint requires.
404 not_found A referenced resource doesn't exist or isn't visible to your organisation.
409 conflict A conflicting change, or an Idempotency-Key reused with a different body.
429 rate_limited Per-key or per-org rate limit exceeded — back off with jitter and retry.

Response body 200

Field Type Description
a number Fitted first-unit cost coefficient of the curve.
b number Fitted log-log slope exponent of the learning curve.
env_id string Identifier of the calibrated environment.
fit object Structured object with the fitted parameters and fit diagnostics.
fitted_accuracy number Accuracy (0–1) of the fitted policy on the training rows.
fitted_policy object Structured object describing the fitted selection policy.
grid_size integer Number of policy candidates evaluated during the fit.
history_received integer Historical choice records supplied in the request.
history_rejected object[] Records that could not be used, each with its position in the request and why. Capped at 50 entries; the counts above are never truncated.
history_used integer Records the fit actually used. Lower than history_received means some could not be read; see history_rejected.
holdout_accuracy number Accuracy (0–1) of the fitted policy on held-out rows.
holdout_coverage number Fraction (0–1) of held-out actuals falling inside the interval.
holdout_mape number Mean absolute percentage error of the PRE-calibration predictions on the held-out rows — the miss this run set out to close, not the outcome. Read mape_after for the result.
id string Identifier of the resulting record, when applicable.
ingested integer Number of observations persisted by an ingest call.
learning_rate number Learning rate (0–1): cost retained each time output doubles.
mape_after number Holdout MAPE AFTER the fit — the held-out rows re-priced under the calibrated result. The run's headline outcome; null when no rows were held out.
mape_before number Holdout MAPE before the fit (same figure as holdout_mape, named for the before/after pair).
matched integer Number of supplied rows matched to an existing quote.
message string Human-readable note about the run, when present.
model string Learning-curve model that was fit (crawford_unit or wright_cumulative).
n_holdout integer Number of rows held out to size the prediction interval.
n_iterations integer Iterations the midpoint solver ran before converging.
n_lots integer Number of production lots used in the curve fit.
n_observations integer Number of observations the calibration fit used.
n_train integer Number of rows used to fit the calibration.
part_revision_id string Identifier of the part revision that was fit.
policy_history_id string Identifier of the newly written selection-policy row.
r_squared number Coefficient of determination (0–1) of the curve fit.
residual_sigma_log number Standard deviation of the fit residuals in log space.
run_id string Identifier of the calibration run, when one was created.
samples integer Number of samples considered by the run.
samples_applied integer Number of samples actually applied to the fit.
scope string Scope the run applied to: 'platform' or 'environment'.
seed_accuracy number Accuracy (0–1) of the seed policy before calibration.
status string Outcome status of the calibration run.
target_metric string Metric the calibration targeted (e.g. 'unit_cost').
unknown_part_numbers string[] Part numbers that could not be resolved to a known part.
unmatched_part_ids string[] Part revision IDs with no quote at their lot size priced by the current version of the costing model, so they were skipped.
work_orders_received integer Number of ERP work-order rows received.
work_orders_resolved integer Number of work orders resolved to a known part revision.

Example response

{
  "a": 0,
  "b": 0,
  "env_id": "string",
  "fit": {},
  "fitted_accuracy": 0,
  "fitted_policy": {},
  "grid_size": 0,
  "history_received": 0,
  "history_rejected": [
    {}
  ],
  "history_used": 0,
  "holdout_accuracy": 0,
  "holdout_coverage": 0.9,
  "holdout_mape": 0.05,
  "id": "string",
  "ingested": 0,
  "learning_rate": 0,
  "mape_after": 0.05,
  "mape_before": 0.05,
  "matched": 0,
  "message": "string",
  "model": "string",
  "n_holdout": 0,
  "n_iterations": 0,
  "n_lots": 0,
  "n_observations": 0,
  "n_train": 0,
  "part_revision_id": "string",
  "policy_history_id": "string",
  "r_squared": 0,
  "residual_sigma_log": 0,
  "run_id": "string",
  "samples": 0,
  "samples_applied": 0,
  "scope": "string",
  "seed_accuracy": 0,
  "status": "string",
  "target_metric": "string",
  "unknown_part_numbers": [
    "string"
  ],
  "unmatched_part_ids": [
    "string"
  ],
  "work_orders_received": 0,
  "work_orders_resolved": 0
}

Recalibrate an environment from its accumulated observations.

POST /api/v1/calibration/environments/{env_id}/finalize

Recalibrate this environment for the selected target metric using every observation submitted for it so far, then activate the result. Use after submitting observations. Only an observation whose predicted value comes from the current version of the costing model is fitted: one submitted before the model last changed, or naming a calculation priced before it, is left out and counted in message, and a run with none left is refused. To start from raw ERP actuals without submitting observations first, use the one-click auto-calibrate endpoint instead.

Parameters

Name In Type Required Description
env_id path string yes Identifier of the env.

Request body (application/json)

Field Type Required Description
notes string no Optional free-text note recorded with the run.
target_alpha number no Target miss rate for the prediction interval (0.10 → a 90% interval).
target_metric cycle_time_s | setup_time_s | programming_time_s | unit_cost | machine_choice no Metric to calibrate the environment for.

Request

curl -X POST https://api.arcnm.io/api/v1/calibration/environments/{env_id}/finalize \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "notes": "string",
    "target_alpha": 0.1,
    "target_metric": "cycle_time_s"
  }'
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/calibration/environments/{env_id}/finalize",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "notes": "string",
        "target_alpha": 0.1,
        "target_metric": "cycle_time_s"
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/calibration/environments/{env_id}/finalize", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "notes": "string",
    "target_alpha": 0.1,
    "target_metric": "cycle_time_s"
  }),
})
const data = await resp.json()

Responses

Status Description
200 Successful Response
422 Validation Error

Errors

Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.

Status Code When
401 invalid_api_key Missing, malformed, or revoked API key.
403 insufficient_scope The key is valid but lacks a scope this endpoint requires.
404 not_found A referenced resource doesn't exist or isn't visible to your organisation.
409 conflict A conflicting change, or an Idempotency-Key reused with a different body.
429 rate_limited Per-key or per-org rate limit exceeded — back off with jitter and retry.

Response body 200

Field Type Description
a number Fitted first-unit cost coefficient of the curve.
b number Fitted log-log slope exponent of the learning curve.
env_id string Identifier of the calibrated environment.
fit object Structured object with the fitted parameters and fit diagnostics.
fitted_accuracy number Accuracy (0–1) of the fitted policy on the training rows.
fitted_policy object Structured object describing the fitted selection policy.
grid_size integer Number of policy candidates evaluated during the fit.
history_received integer Historical choice records supplied in the request.
history_rejected object[] Records that could not be used, each with its position in the request and why. Capped at 50 entries; the counts above are never truncated.
history_used integer Records the fit actually used. Lower than history_received means some could not be read; see history_rejected.
holdout_accuracy number Accuracy (0–1) of the fitted policy on held-out rows.
holdout_coverage number Fraction (0–1) of held-out actuals falling inside the interval.
holdout_mape number Mean absolute percentage error of the PRE-calibration predictions on the held-out rows — the miss this run set out to close, not the outcome. Read mape_after for the result.
id string Identifier of the resulting record, when applicable.
ingested integer Number of observations persisted by an ingest call.
learning_rate number Learning rate (0–1): cost retained each time output doubles.
mape_after number Holdout MAPE AFTER the fit — the held-out rows re-priced under the calibrated result. The run's headline outcome; null when no rows were held out.
mape_before number Holdout MAPE before the fit (same figure as holdout_mape, named for the before/after pair).
matched integer Number of supplied rows matched to an existing quote.
message string Human-readable note about the run, when present.
model string Learning-curve model that was fit (crawford_unit or wright_cumulative).
n_holdout integer Number of rows held out to size the prediction interval.
n_iterations integer Iterations the midpoint solver ran before converging.
n_lots integer Number of production lots used in the curve fit.
n_observations integer Number of observations the calibration fit used.
n_train integer Number of rows used to fit the calibration.
part_revision_id string Identifier of the part revision that was fit.
policy_history_id string Identifier of the newly written selection-policy row.
r_squared number Coefficient of determination (0–1) of the curve fit.
residual_sigma_log number Standard deviation of the fit residuals in log space.
run_id string Identifier of the calibration run, when one was created.
samples integer Number of samples considered by the run.
samples_applied integer Number of samples actually applied to the fit.
scope string Scope the run applied to: 'platform' or 'environment'.
seed_accuracy number Accuracy (0–1) of the seed policy before calibration.
status string Outcome status of the calibration run.
target_metric string Metric the calibration targeted (e.g. 'unit_cost').
unknown_part_numbers string[] Part numbers that could not be resolved to a known part.
unmatched_part_ids string[] Part revision IDs with no quote at their lot size priced by the current version of the costing model, so they were skipped.
work_orders_received integer Number of ERP work-order rows received.
work_orders_resolved integer Number of work orders resolved to a known part revision.

Example response

{
  "a": 0,
  "b": 0,
  "env_id": "string",
  "fit": {},
  "fitted_accuracy": 0,
  "fitted_policy": {},
  "grid_size": 0,
  "history_received": 0,
  "history_rejected": [
    {}
  ],
  "history_used": 0,
  "holdout_accuracy": 0,
  "holdout_coverage": 0.9,
  "holdout_mape": 0.05,
  "id": "string",
  "ingested": 0,
  "learning_rate": 0,
  "mape_after": 0.05,
  "mape_before": 0.05,
  "matched": 0,
  "message": "string",
  "model": "string",
  "n_holdout": 0,
  "n_iterations": 0,
  "n_lots": 0,
  "n_observations": 0,
  "n_train": 0,
  "part_revision_id": "string",
  "policy_history_id": "string",
  "r_squared": 0,
  "residual_sigma_log": 0,
  "run_id": "string",
  "samples": 0,
  "samples_applied": 0,
  "scope": "string",
  "seed_accuracy": 0,
  "status": "string",
  "target_metric": "string",
  "unknown_part_numbers": [
    "string"
  ],
  "unmatched_part_ids": [
    "string"
  ],
  "work_orders_received": 0,
  "work_orders_resolved": 0
}

Calibrate cost-down-with-volume from lot-progression actuals.

POST /api/v1/calibration/environments/{env_id}/learning-curve-fit

Calibrate how this environment's per-unit cost falls as cumulative production grows, using a sequence of production-lot actuals for one part. Choose the cumulative model (the safer default, observed directly) or the per-unit model. Submit at least three lots.

Parameters

Name In Type Required Description
env_id path string yes Identifier of the env.

Request body (application/json)

Field Type Required Description
lots LearningCurveLotPayload[] yes Sequence of production-lot actuals to fit; at least three.
model crawford_unit | wright_cumulative no Learning-curve model to fit (per-unit or cumulative).
part_revision_id string yes Identifier of the part revision the lots belong to.

Request

curl -X POST https://api.arcnm.io/api/v1/calibration/environments/{env_id}/learning-curve-fit \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "part_revision_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "lots": [
      {
        "lot_index": 0,
        "lot_size": 50,
        "lot_total_cost": 0
      }
    ]
  }'
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/calibration/environments/{env_id}/learning-curve-fit",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "part_revision_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "lots": [
            {
                "lot_index": 0,
                "lot_size": 50,
                "lot_total_cost": 0
            }
        ]
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/calibration/environments/{env_id}/learning-curve-fit", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "part_revision_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "lots": [
      {
        "lot_index": 0,
        "lot_size": 50,
        "lot_total_cost": 0
      }
    ]
  }),
})
const data = await resp.json()

Responses

Status Description
200 Successful Response
422 Validation Error

Errors

Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.

Status Code When
401 invalid_api_key Missing, malformed, or revoked API key.
403 insufficient_scope The key is valid but lacks a scope this endpoint requires.
404 not_found A referenced resource doesn't exist or isn't visible to your organisation.
409 conflict A conflicting change, or an Idempotency-Key reused with a different body.
429 rate_limited Per-key or per-org rate limit exceeded — back off with jitter and retry.

Response body 200

Field Type Description
a number Fitted first-unit cost coefficient of the curve.
b number Fitted log-log slope exponent of the learning curve.
env_id string Identifier of the calibrated environment.
fit object Structured object with the fitted parameters and fit diagnostics.
fitted_accuracy number Accuracy (0–1) of the fitted policy on the training rows.
fitted_policy object Structured object describing the fitted selection policy.
grid_size integer Number of policy candidates evaluated during the fit.
history_received integer Historical choice records supplied in the request.
history_rejected object[] Records that could not be used, each with its position in the request and why. Capped at 50 entries; the counts above are never truncated.
history_used integer Records the fit actually used. Lower than history_received means some could not be read; see history_rejected.
holdout_accuracy number Accuracy (0–1) of the fitted policy on held-out rows.
holdout_coverage number Fraction (0–1) of held-out actuals falling inside the interval.
holdout_mape number Mean absolute percentage error of the PRE-calibration predictions on the held-out rows — the miss this run set out to close, not the outcome. Read mape_after for the result.
id string Identifier of the resulting record, when applicable.
ingested integer Number of observations persisted by an ingest call.
learning_rate number Learning rate (0–1): cost retained each time output doubles.
mape_after number Holdout MAPE AFTER the fit — the held-out rows re-priced under the calibrated result. The run's headline outcome; null when no rows were held out.
mape_before number Holdout MAPE before the fit (same figure as holdout_mape, named for the before/after pair).
matched integer Number of supplied rows matched to an existing quote.
message string Human-readable note about the run, when present.
model string Learning-curve model that was fit (crawford_unit or wright_cumulative).
n_holdout integer Number of rows held out to size the prediction interval.
n_iterations integer Iterations the midpoint solver ran before converging.
n_lots integer Number of production lots used in the curve fit.
n_observations integer Number of observations the calibration fit used.
n_train integer Number of rows used to fit the calibration.
part_revision_id string Identifier of the part revision that was fit.
policy_history_id string Identifier of the newly written selection-policy row.
r_squared number Coefficient of determination (0–1) of the curve fit.
residual_sigma_log number Standard deviation of the fit residuals in log space.
run_id string Identifier of the calibration run, when one was created.
samples integer Number of samples considered by the run.
samples_applied integer Number of samples actually applied to the fit.
scope string Scope the run applied to: 'platform' or 'environment'.
seed_accuracy number Accuracy (0–1) of the seed policy before calibration.
status string Outcome status of the calibration run.
target_metric string Metric the calibration targeted (e.g. 'unit_cost').
unknown_part_numbers string[] Part numbers that could not be resolved to a known part.
unmatched_part_ids string[] Part revision IDs with no quote at their lot size priced by the current version of the costing model, so they were skipped.
work_orders_received integer Number of ERP work-order rows received.
work_orders_resolved integer Number of work orders resolved to a known part revision.

Example response

{
  "a": 0,
  "b": 0,
  "env_id": "string",
  "fit": {},
  "fitted_accuracy": 0,
  "fitted_policy": {},
  "grid_size": 0,
  "history_received": 0,
  "history_rejected": [
    {}
  ],
  "history_used": 0,
  "holdout_accuracy": 0,
  "holdout_coverage": 0.9,
  "holdout_mape": 0.05,
  "id": "string",
  "ingested": 0,
  "learning_rate": 0,
  "mape_after": 0.05,
  "mape_before": 0.05,
  "matched": 0,
  "message": "string",
  "model": "string",
  "n_holdout": 0,
  "n_iterations": 0,
  "n_lots": 0,
  "n_observations": 0,
  "n_train": 0,
  "part_revision_id": "string",
  "policy_history_id": "string",
  "r_squared": 0,
  "residual_sigma_log": 0,
  "run_id": "string",
  "samples": 0,
  "samples_applied": 0,
  "scope": "string",
  "seed_accuracy": 0,
  "status": "string",
  "target_metric": "string",
  "unknown_part_numbers": [
    "string"
  ],
  "unmatched_part_ids": [
    "string"
  ],
  "work_orders_received": 0,
  "work_orders_resolved": 0
}

What would improve this environment's calibration most.

GET /api/v1/calibration/environments/{env_id}/next-best-evidence

Ranks the missing evidence for an environment: which parts or lot sizes to submit actuals for next, and where the model is capped and needs a configuration review instead of more data. Safe for AI agent use: read-only.

Parameters

Name In Type Required Description
env_id path string yes Identifier of the env.

Request

curl -X GET https://api.arcnm.io/api/v1/calibration/environments/{env_id}/next-best-evidence \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.get(
    "https://api.arcnm.io/api/v1/calibration/environments/{env_id}/next-best-evidence",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/calibration/environments/{env_id}/next-best-evidence", {
  method: "GET",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
  },
})
const data = await resp.json()

Responses

Status Description
200 Successful Response
422 Validation Error

Errors

Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.

Status Code When
401 invalid_api_key Missing, malformed, or revoked API key.
403 insufficient_scope The key is valid but lacks a scope this endpoint requires.
404 not_found A referenced resource doesn't exist or isn't visible to your organisation.
429 rate_limited Per-key or per-org rate limit exceeded — back off with jitter and retry.

Response body 200

Field Type Description
env_id string The environment this advice is for.
items NextBestEvidenceItem[] Ranked asks; empty when the corpus is already rich.

Example response

{
  "env_id": "string",
  "items": [
    {
      "ask": "string",
      "code": "other",
      "count": 0,
      "process": "string",
      "rank": 0,
      "reason": "string"
    }
  ]
}

Calibrate which machine an environment recommends from past choices.

POST /api/v1/calibration/environments/{env_id}/policy/calibrate

Calibrate how this environment chooses a machine, using a list of historical 'the shop actually picked machine X' decisions. Once calibrated, the environment's machine recommendations better match your shop's real choices.

Parameters

Name In Type Required Description
env_id path string yes Identifier of the env.

Request body (application/json)

Field Type Required Description
history object[] yes Past machine-choice records, each with candidates and the chosen_machine_id.
region string no Region whose seed policy to start from (e.g. DE-BY); null for the default.

Request

curl -X POST https://api.arcnm.io/api/v1/calibration/environments/{env_id}/policy/calibrate \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "history": [
      {}
    ]
  }'
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/calibration/environments/{env_id}/policy/calibrate",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "history": [
            {}
        ]
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/calibration/environments/{env_id}/policy/calibrate", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "history": [
      {}
    ]
  }),
})
const data = await resp.json()

Responses

Status Description
200 Successful Response
422 Validation Error

Errors

Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.

Status Code When
401 invalid_api_key Missing, malformed, or revoked API key.
403 insufficient_scope The key is valid but lacks a scope this endpoint requires.
404 not_found A referenced resource doesn't exist or isn't visible to your organisation.
409 conflict A conflicting change, or an Idempotency-Key reused with a different body.
429 rate_limited Per-key or per-org rate limit exceeded — back off with jitter and retry.

Response body 200

Field Type Description
a number Fitted first-unit cost coefficient of the curve.
b number Fitted log-log slope exponent of the learning curve.
env_id string Identifier of the calibrated environment.
fit object Structured object with the fitted parameters and fit diagnostics.
fitted_accuracy number Accuracy (0–1) of the fitted policy on the training rows.
fitted_policy object Structured object describing the fitted selection policy.
grid_size integer Number of policy candidates evaluated during the fit.
history_received integer Historical choice records supplied in the request.
history_rejected object[] Records that could not be used, each with its position in the request and why. Capped at 50 entries; the counts above are never truncated.
history_used integer Records the fit actually used. Lower than history_received means some could not be read; see history_rejected.
holdout_accuracy number Accuracy (0–1) of the fitted policy on held-out rows.
holdout_coverage number Fraction (0–1) of held-out actuals falling inside the interval.
holdout_mape number Mean absolute percentage error of the PRE-calibration predictions on the held-out rows — the miss this run set out to close, not the outcome. Read mape_after for the result.
id string Identifier of the resulting record, when applicable.
ingested integer Number of observations persisted by an ingest call.
learning_rate number Learning rate (0–1): cost retained each time output doubles.
mape_after number Holdout MAPE AFTER the fit — the held-out rows re-priced under the calibrated result. The run's headline outcome; null when no rows were held out.
mape_before number Holdout MAPE before the fit (same figure as holdout_mape, named for the before/after pair).
matched integer Number of supplied rows matched to an existing quote.
message string Human-readable note about the run, when present.
model string Learning-curve model that was fit (crawford_unit or wright_cumulative).
n_holdout integer Number of rows held out to size the prediction interval.
n_iterations integer Iterations the midpoint solver ran before converging.
n_lots integer Number of production lots used in the curve fit.
n_observations integer Number of observations the calibration fit used.
n_train integer Number of rows used to fit the calibration.
part_revision_id string Identifier of the part revision that was fit.
policy_history_id string Identifier of the newly written selection-policy row.
r_squared number Coefficient of determination (0–1) of the curve fit.
residual_sigma_log number Standard deviation of the fit residuals in log space.
run_id string Identifier of the calibration run, when one was created.
samples integer Number of samples considered by the run.
samples_applied integer Number of samples actually applied to the fit.
scope string Scope the run applied to: 'platform' or 'environment'.
seed_accuracy number Accuracy (0–1) of the seed policy before calibration.
status string Outcome status of the calibration run.
target_metric string Metric the calibration targeted (e.g. 'unit_cost').
unknown_part_numbers string[] Part numbers that could not be resolved to a known part.
unmatched_part_ids string[] Part revision IDs with no quote at their lot size priced by the current version of the costing model, so they were skipped.
work_orders_received integer Number of ERP work-order rows received.
work_orders_resolved integer Number of work orders resolved to a known part revision.

Example response

{
  "a": 0,
  "b": 0,
  "env_id": "string",
  "fit": {},
  "fitted_accuracy": 0,
  "fitted_policy": {},
  "grid_size": 0,
  "history_received": 0,
  "history_rejected": [
    {}
  ],
  "history_used": 0,
  "holdout_accuracy": 0,
  "holdout_coverage": 0.9,
  "holdout_mape": 0.05,
  "id": "string",
  "ingested": 0,
  "learning_rate": 0,
  "mape_after": 0.05,
  "mape_before": 0.05,
  "matched": 0,
  "message": "string",
  "model": "string",
  "n_holdout": 0,
  "n_iterations": 0,
  "n_lots": 0,
  "n_observations": 0,
  "n_train": 0,
  "part_revision_id": "string",
  "policy_history_id": "string",
  "r_squared": 0,
  "residual_sigma_log": 0,
  "run_id": "string",
  "samples": 0,
  "samples_applied": 0,
  "scope": "string",
  "seed_accuracy": 0,
  "status": "string",
  "target_metric": "string",
  "unknown_part_numbers": [
    "string"
  ],
  "unmatched_part_ids": [
    "string"
  ],
  "work_orders_received": 0,
  "work_orders_resolved": 0
}

Calibration run history for one environment.

GET /api/v1/calibration/environments/{env_id}/runs

Reverse-chronological list of this environment's calibration runs: when each ran, how many observations it used, and whether its result is the currently active calibration. Use it to pick the run to revert to. Read-only; safe for AI agent use.

Paginated. Pass limit and offset to page through results.

Parameters

Name In Type Required Description
env_id path string yes Identifier of the env.
limit query integer no Runs per page.
offset query integer no Runs to skip.

Request

curl -X GET https://api.arcnm.io/api/v1/calibration/environments/{env_id}/runs \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.get(
    "https://api.arcnm.io/api/v1/calibration/environments/{env_id}/runs",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/calibration/environments/{env_id}/runs", {
  method: "GET",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
  },
})
const data = await resp.json()

Responses

Status Description
200 Successful Response
422 Validation Error

Errors

Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.

Status Code When
401 invalid_api_key Missing, malformed, or revoked API key.
403 insufficient_scope The key is valid but lacks a scope this endpoint requires.
404 not_found A referenced resource doesn't exist or isn't visible to your organisation.
429 rate_limited Per-key or per-org rate limit exceeded — back off with jitter and retry.

Response body 200

Field Type Description
env_id string Environment this history belongs to.
limit integer Page size used for this response.
offset integer Runs skipped before this page.
runs CalibrationRunHistoryItem[] Runs, newest first.
total integer Total runs in the environment's history.

Example response

{
  "env_id": "string",
  "limit": 0,
  "offset": 0,
  "runs": [
    {
      "created_at": "string",
      "id": "string",
      "is_active": true,
      "n_observations": 0,
      "run_kind": "string"
    }
  ],
  "total": 0
}

Restore an earlier calibration run's result.

POST /api/v1/calibration/environments/{env_id}/runs/{run_id}/revert

Make an earlier run's calibration the active one again: the current result is retired and the selected run's is restored, exactly as it was fitted — except an adjustment calibration no longer makes (retired_drivers on the calibration report), which is not restored. Nothing is refit and no history is lost — every run stays in the history and can be restored again. Quotes priced after this call use the restored calibration.

Parameters

Name In Type Required Description
env_id path string yes Identifier of the env.
run_id path string yes Identifier of the run.

Request

curl -X POST https://api.arcnm.io/api/v1/calibration/environments/{env_id}/runs/{run_id}/revert \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/calibration/environments/{env_id}/runs/{run_id}/revert",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/calibration/environments/{env_id}/runs/{run_id}/revert", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
  },
})
const data = await resp.json()

Responses

Status Description
200 Successful Response
422 Validation Error

Errors

Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.

Status Code When
401 invalid_api_key Missing, malformed, or revoked API key.
403 insufficient_scope The key is valid but lacks a scope this endpoint requires.
404 not_found A referenced resource doesn't exist or isn't visible to your organisation.
409 conflict A conflicting change, or an Idempotency-Key reused with a different body.
429 rate_limited Per-key or per-org rate limit exceeded — back off with jitter and retry.

Response body 200

Field Type Description
a number Fitted first-unit cost coefficient of the curve.
b number Fitted log-log slope exponent of the learning curve.
env_id string Identifier of the calibrated environment.
fit object Structured object with the fitted parameters and fit diagnostics.
fitted_accuracy number Accuracy (0–1) of the fitted policy on the training rows.
fitted_policy object Structured object describing the fitted selection policy.
grid_size integer Number of policy candidates evaluated during the fit.
history_received integer Historical choice records supplied in the request.
history_rejected object[] Records that could not be used, each with its position in the request and why. Capped at 50 entries; the counts above are never truncated.
history_used integer Records the fit actually used. Lower than history_received means some could not be read; see history_rejected.
holdout_accuracy number Accuracy (0–1) of the fitted policy on held-out rows.
holdout_coverage number Fraction (0–1) of held-out actuals falling inside the interval.
holdout_mape number Mean absolute percentage error of the PRE-calibration predictions on the held-out rows — the miss this run set out to close, not the outcome. Read mape_after for the result.
id string Identifier of the resulting record, when applicable.
ingested integer Number of observations persisted by an ingest call.
learning_rate number Learning rate (0–1): cost retained each time output doubles.
mape_after number Holdout MAPE AFTER the fit — the held-out rows re-priced under the calibrated result. The run's headline outcome; null when no rows were held out.
mape_before number Holdout MAPE before the fit (same figure as holdout_mape, named for the before/after pair).
matched integer Number of supplied rows matched to an existing quote.
message string Human-readable note about the run, when present.
model string Learning-curve model that was fit (crawford_unit or wright_cumulative).
n_holdout integer Number of rows held out to size the prediction interval.
n_iterations integer Iterations the midpoint solver ran before converging.
n_lots integer Number of production lots used in the curve fit.
n_observations integer Number of observations the calibration fit used.
n_train integer Number of rows used to fit the calibration.
part_revision_id string Identifier of the part revision that was fit.
policy_history_id string Identifier of the newly written selection-policy row.
r_squared number Coefficient of determination (0–1) of the curve fit.
residual_sigma_log number Standard deviation of the fit residuals in log space.
run_id string Identifier of the calibration run, when one was created.
samples integer Number of samples considered by the run.
samples_applied integer Number of samples actually applied to the fit.
scope string Scope the run applied to: 'platform' or 'environment'.
seed_accuracy number Accuracy (0–1) of the seed policy before calibration.
status string Outcome status of the calibration run.
target_metric string Metric the calibration targeted (e.g. 'unit_cost').
unknown_part_numbers string[] Part numbers that could not be resolved to a known part.
unmatched_part_ids string[] Part revision IDs with no quote at their lot size priced by the current version of the costing model, so they were skipped.
work_orders_received integer Number of ERP work-order rows received.
work_orders_resolved integer Number of work orders resolved to a known part revision.

Example response

{
  "a": 0,
  "b": 0,
  "env_id": "string",
  "fit": {},
  "fitted_accuracy": 0,
  "fitted_policy": {},
  "grid_size": 0,
  "history_received": 0,
  "history_rejected": [
    {}
  ],
  "history_used": 0,
  "holdout_accuracy": 0,
  "holdout_coverage": 0.9,
  "holdout_mape": 0.05,
  "id": "string",
  "ingested": 0,
  "learning_rate": 0,
  "mape_after": 0.05,
  "mape_before": 0.05,
  "matched": 0,
  "message": "string",
  "model": "string",
  "n_holdout": 0,
  "n_iterations": 0,
  "n_lots": 0,
  "n_observations": 0,
  "n_train": 0,
  "part_revision_id": "string",
  "policy_history_id": "string",
  "r_squared": 0,
  "residual_sigma_log": 0,
  "run_id": "string",
  "samples": 0,
  "samples_applied": 0,
  "scope": "string",
  "seed_accuracy": 0,
  "status": "string",
  "target_metric": "string",
  "unknown_part_numbers": [
    "string"
  ],
  "unmatched_part_ids": [
    "string"
  ],
  "work_orders_received": 0,
  "work_orders_resolved": 0
}

Calibration health summary for one environment.

GET /api/v1/calibration/environments/{env_id}/status

Read view of an environment's calibration health: whether it is calibrated, when it was last calibrated, and how many submitted observations are waiting to be applied. Designed as the AI agent's discovery endpoint — call this first to decide whether to submit fresh observations or recalibrate.

Parameters

Name In Type Required Description
env_id path string yes Identifier of the env.

Request

curl -X GET https://api.arcnm.io/api/v1/calibration/environments/{env_id}/status \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.get(
    "https://api.arcnm.io/api/v1/calibration/environments/{env_id}/status",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/calibration/environments/{env_id}/status", {
  method: "GET",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
  },
})
const data = await resp.json()

Responses

Status Description
200 Successful Response
422 Validation Error

Errors

Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.

Status Code When
401 invalid_api_key Missing, malformed, or revoked API key.
403 insufficient_scope The key is valid but lacks a scope this endpoint requires.
404 not_found A referenced resource doesn't exist or isn't visible to your organisation.
429 rate_limited Per-key or per-org rate limit exceeded — back off with jitter and retry.

Response body 200

Field Type Description
env_id string Identifier of the environment this status describes.
is_calibrated boolean Whether this environment has been calibrated to your actuals.
last_fit_at string ISO 8601 timestamp of the last calibration; null if never run.
observations object[] Per-source counts of submitted observations awaiting application.

Example response

{
  "env_id": "string",
  "is_calibrated": true,
  "last_fit_at": "string",
  "observations": [
    {}
  ]
}

Calibrate an environment from (predicted, actual) pairs.

POST /api/v1/calibration/environments/{env_id}/teach

Calibrate an environment from caller-supplied (predicted, actual) rows. Each predicted value must come from a current quote for the part: a part this environment priced only before the costing model last changed has none, and a request naming one is refused until the part is priced again. If you only have actuals, use the one-click auto-calibrate endpoint, which pairs each actual with the part's current quote server-side.

Parameters

Name In Type Required Description
env_id path string yes Identifier of the env.

Request body (application/json)

Field Type Required Description
holdout_pct number no Fraction (0.05–0.5) of rows held out to size the prediction interval.
rows TeachRow[] yes Between 1 and 20,000 (predicted, actual) rows to calibrate from.
target_alpha number no Target miss rate for the prediction interval (0.10 → a 90% interval).

Request

curl -X POST https://api.arcnm.io/api/v1/calibration/environments/{env_id}/teach \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "rows": [
      {
        "actual": 0,
        "bin": "string",
        "part_revision_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "predicted": 0,
        "target_metric": "unit_cost"
      }
    ]
  }'
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/calibration/environments/{env_id}/teach",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "rows": [
            {
                "actual": 0,
                "bin": "string",
                "part_revision_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
                "predicted": 0,
                "target_metric": "unit_cost"
            }
        ]
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/calibration/environments/{env_id}/teach", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "rows": [
      {
        "actual": 0,
        "bin": "string",
        "part_revision_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "predicted": 0,
        "target_metric": "unit_cost"
      }
    ]
  }),
})
const data = await resp.json()

Responses

Status Description
200 Successful Response
422 Validation Error

Errors

Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.

Status Code When
401 invalid_api_key Missing, malformed, or revoked API key.
403 insufficient_scope The key is valid but lacks a scope this endpoint requires.
404 not_found A referenced resource doesn't exist or isn't visible to your organisation.
409 conflict A conflicting change, or an Idempotency-Key reused with a different body.
429 rate_limited Per-key or per-org rate limit exceeded — back off with jitter and retry.

Response body 200

Field Type Description
a number Fitted first-unit cost coefficient of the curve.
b number Fitted log-log slope exponent of the learning curve.
env_id string Identifier of the calibrated environment.
fit object Structured object with the fitted parameters and fit diagnostics.
fitted_accuracy number Accuracy (0–1) of the fitted policy on the training rows.
fitted_policy object Structured object describing the fitted selection policy.
grid_size integer Number of policy candidates evaluated during the fit.
history_received integer Historical choice records supplied in the request.
history_rejected object[] Records that could not be used, each with its position in the request and why. Capped at 50 entries; the counts above are never truncated.
history_used integer Records the fit actually used. Lower than history_received means some could not be read; see history_rejected.
holdout_accuracy number Accuracy (0–1) of the fitted policy on held-out rows.
holdout_coverage number Fraction (0–1) of held-out actuals falling inside the interval.
holdout_mape number Mean absolute percentage error of the PRE-calibration predictions on the held-out rows — the miss this run set out to close, not the outcome. Read mape_after for the result.
id string Identifier of the resulting record, when applicable.
ingested integer Number of observations persisted by an ingest call.
learning_rate number Learning rate (0–1): cost retained each time output doubles.
mape_after number Holdout MAPE AFTER the fit — the held-out rows re-priced under the calibrated result. The run's headline outcome; null when no rows were held out.
mape_before number Holdout MAPE before the fit (same figure as holdout_mape, named for the before/after pair).
matched integer Number of supplied rows matched to an existing quote.
message string Human-readable note about the run, when present.
model string Learning-curve model that was fit (crawford_unit or wright_cumulative).
n_holdout integer Number of rows held out to size the prediction interval.
n_iterations integer Iterations the midpoint solver ran before converging.
n_lots integer Number of production lots used in the curve fit.
n_observations integer Number of observations the calibration fit used.
n_train integer Number of rows used to fit the calibration.
part_revision_id string Identifier of the part revision that was fit.
policy_history_id string Identifier of the newly written selection-policy row.
r_squared number Coefficient of determination (0–1) of the curve fit.
residual_sigma_log number Standard deviation of the fit residuals in log space.
run_id string Identifier of the calibration run, when one was created.
samples integer Number of samples considered by the run.
samples_applied integer Number of samples actually applied to the fit.
scope string Scope the run applied to: 'platform' or 'environment'.
seed_accuracy number Accuracy (0–1) of the seed policy before calibration.
status string Outcome status of the calibration run.
target_metric string Metric the calibration targeted (e.g. 'unit_cost').
unknown_part_numbers string[] Part numbers that could not be resolved to a known part.
unmatched_part_ids string[] Part revision IDs with no quote at their lot size priced by the current version of the costing model, so they were skipped.
work_orders_received integer Number of ERP work-order rows received.
work_orders_resolved integer Number of work orders resolved to a known part revision.

Example response

{
  "a": 0,
  "b": 0,
  "env_id": "string",
  "fit": {},
  "fitted_accuracy": 0,
  "fitted_policy": {},
  "grid_size": 0,
  "history_received": 0,
  "history_rejected": [
    {}
  ],
  "history_used": 0,
  "holdout_accuracy": 0,
  "holdout_coverage": 0.9,
  "holdout_mape": 0.05,
  "id": "string",
  "ingested": 0,
  "learning_rate": 0,
  "mape_after": 0.05,
  "mape_before": 0.05,
  "matched": 0,
  "message": "string",
  "model": "string",
  "n_holdout": 0,
  "n_iterations": 0,
  "n_lots": 0,
  "n_observations": 0,
  "n_train": 0,
  "part_revision_id": "string",
  "policy_history_id": "string",
  "r_squared": 0,
  "residual_sigma_log": 0,
  "run_id": "string",
  "samples": 0,
  "samples_applied": 0,
  "scope": "string",
  "seed_accuracy": 0,
  "status": "string",
  "target_metric": "string",
  "unknown_part_numbers": [
    "string"
  ],
  "unmatched_part_ids": [
    "string"
  ],
  "work_orders_received": 0,
  "work_orders_resolved": 0
}

Ask whether your ERP part numbers resolve, before you calibrate.

POST /api/v1/calibration/erp-preflight

Resolves the part_number column of an ERP export against the parts you hold and returns how many match. Reads only — it needs no environment and it writes nothing.

Request body (application/json)

Field Type Required Description
part_numbers string[] yes The part_number column of the work orders you are about to send, in any order. Duplicates are fine.

Request

curl -X POST https://api.arcnm.io/api/v1/calibration/erp-preflight \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "part_numbers": [
      "string"
    ]
  }'
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/calibration/erp-preflight",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "part_numbers": [
            "string"
        ]
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/calibration/erp-preflight", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "part_numbers": [
      "string"
    ]
  }),
})
const data = await resp.json()

Responses

Status Description
200 Successful Response
422 Validation Error

Errors

Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.

Status Code When
401 invalid_api_key Missing, malformed, or revoked API key.
403 insufficient_scope The key is valid but lacks a scope this endpoint requires.
409 conflict A conflicting change, or an Idempotency-Key reused with a different body.
429 rate_limited Per-key or per-org rate limit exceeded — back off with jitter and retry.

Response body 200

Field Type Description
matched integer How many of them resolve to a part you hold.
requested integer Distinct part numbers you asked about.
unknown integer How many do not.
unknown_part_numbers string[] The ones that do not, up to 100 of them — the list to fix before you run the calibration.

Example response

{
  "matched": 0,
  "requested": 0,
  "unknown": 0,
  "unknown_part_numbers": [
    "string"
  ]
}

Create a calibration import and disclose what pricing it will cost.

POST /api/v1/calibration/imports

Upload historical actuals as rows. Every row is resolved against your part library and bucketed: already priced (free), servable from cache (free), needs a new calculation (billable), or unusable (with the fix named). Only a quote priced by the current version of the costing model counts as already priced; a part quoted only before the model last changed needs a new calculation. NOTHING is priced or spent by this call — the disclosed calculations_required is committed explicitly via POST /calibration/imports/{id}/price. Re-posting the same rows returns the same import until the costing model changes; after that, the same rows start a new import.

Request body (application/json)

Field Type Required Description
basis herstellkosten | selbstkosten | angebotspreis no Which rung of the cost sheet the whole file is measured at: 'herstellkosten' (manufacturing cost per unit — what a work order books), 'selbstkosten' (total cost per unit, including administration, selling, freight and duty) or 'angebotspreis' (the price per unit, including the profit mark-up). One basis per file, like one currency per file. Each row is compared against the model's own figure at the same rung. A column header cannot answer this — 'unit cost' and 'unit price' are used interchangeably in exports — so it is asked rather than guessed. Omit it only if you genuinely do not know: an unstated basis is read as manufacturing cost and the environment's calibration report flags how many rows were unstated. Fixed when the import is created; re-posting the same rows returns the existing import unchanged.
currency string no Currency the actuals are denominated in. Must match the environment's currency; defaults to it.
env_id string yes The costing environment to calibrate.
rows CalibrationImportRowIn[] yes Up to 20,000 historical actuals.

Request

curl -X POST https://api.arcnm.io/api/v1/calibration/imports \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "env_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "rows": [
      {
        "actual_unit_cost": 0,
        "annual_volume": 500,
        "bin": "string",
        "lot_size": 1,
        "part_number": "BRACKET-001",
        "source_ref": "string"
      }
    ]
  }'
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/calibration/imports",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "env_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "rows": [
            {
                "actual_unit_cost": 0,
                "annual_volume": 500,
                "bin": "string",
                "lot_size": 1,
                "part_number": "BRACKET-001",
                "source_ref": "string"
            }
        ]
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/calibration/imports", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "env_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "rows": [
      {
        "actual_unit_cost": 0,
        "annual_volume": 500,
        "bin": "string",
        "lot_size": 1,
        "part_number": "BRACKET-001",
        "source_ref": "string"
      }
    ]
  }),
})
const data = await resp.json()

Responses

Status Description
201 Successful Response
422 Validation Error

Errors

Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.

Status Code When
401 invalid_api_key Missing, malformed, or revoked API key.
403 insufficient_scope The key is valid but lacks a scope this endpoint requires.
409 conflict A conflicting change, or an Idempotency-Key reused with a different body.
429 rate_limited Per-key or per-org rate limit exceeded — back off with jitter and retry.

Response body 201

Field Type Description
calculations_free integer Rows already priced (or servable from cache) — free.
calculations_remaining integer Your plan's remaining calculation allowance, when known.
calculations_required integer Distinct (part, lot) pairs that need a NEW calculation — the billable spend POST /price will commit. Nothing is priced by this call.
created boolean False when these exact rows were already imported into this environment — the existing import is returned instead of a duplicate.
distinct_lot_sizes integer Distinct lot sizes across usable rows. Below two the fit cannot split setup cost from per-part cost.
env_id string The environment this import calibrates.
import_id string Identifier of the created import.
message string The cost disclosure, in plain language.
parts_without_cad string[] Parts with no STEP file to calculate from — upload one.
rows_received integer Rows submitted.
rows_usable integer Rows that can become calibration observations.
status string Import lifecycle status.
unresolved_part_numbers string[] Part numbers not in your library — add the parts.

Example response

{
  "calculations_free": 0,
  "calculations_remaining": 0,
  "calculations_required": 0,
  "created": true,
  "distinct_lot_sizes": 0,
  "env_id": "string",
  "import_id": "string",
  "message": "string",
  "parts_without_cad": [
    "string"
  ],
  "rows_received": 0,
  "rows_usable": 0,
  "status": "string",
  "unresolved_part_numbers": [
    "string"
  ]
}

Import status: per-row states and whether the priced pairs settled.

GET /api/v1/calibration/imports/{import_id}

Joins every priced row to its calculation and folds terminal outcomes back onto the rows (succeeded → priced, failed → pricing_failed, verbatim reason attached). settled is the green light for POST /calibrate. Rows are capped at 1000 in the response; the counts never are.

Parameters

Name In Type Required Description
import_id path string yes Identifier of the import.

Request

curl -X GET https://api.arcnm.io/api/v1/calibration/imports/{import_id} \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.get(
    "https://api.arcnm.io/api/v1/calibration/imports/{import_id}",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/calibration/imports/{import_id}", {
  method: "GET",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
  },
})
const data = await resp.json()

Responses

Status Description
200 Successful Response
422 Validation Error

Errors

Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.

Status Code When
401 invalid_api_key Missing, malformed, or revoked API key.
403 insufficient_scope The key is valid but lacks a scope this endpoint requires.
404 not_found A referenced resource doesn't exist or isn't visible to your organisation.
429 rate_limited Per-key or per-org rate limit exceeded — back off with jitter and retry.

Response body 200

Field Type Description
calculations_required integer Billable pairs disclosed at preflight.
calculations_run integer Calculations actually created by the price commits.
env_id string
error string Import-level failure, when one happened.
import_id string
pending_calculations integer Rows whose calculation is still queued or running.
rows CalibrationImportRowPublic[]
rows_truncated boolean True when more than 1000 rows exist; counts are never truncated.
settled boolean True when no priced pair can change any more — the moment POST /calibrate will run.
state_counts object Row count per disposition state.
status string

Example response

{
  "calculations_required": 0,
  "calculations_run": 0,
  "env_id": "string",
  "error": "string",
  "import_id": "string",
  "pending_calculations": 0,
  "rows": [
    {
      "actual_unit_cost": 0,
      "annual_volume": 500,
      "calculation_id": "string",
      "detail": "string",
      "lot_size": 50,
      "part_number": "BRACKET-001",
      "row_ordinal": 0,
      "state": "string"
    }
  ],
  "rows_truncated": false,
  "settled": true,
  "state_counts": {},
  "status": "string"
}

Fit the environment on every settled (part, lot) actual.

POST /api/v1/calibration/imports/{import_id}/calibrate

Calibrates the environment on every now-matching row, exactly as the one-click auto-calibrate endpoint would, and returns the same result shape (mape_before → mape_after). Refused (409) while priced pairs are still running. Metered as one calibration run. Idempotent: a completed import replays its stored result without metering a second run.

Parameters

Name In Type Required Description
import_id path string yes Identifier of the import.

Request

curl -X POST https://api.arcnm.io/api/v1/calibration/imports/{import_id}/calibrate \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/calibration/imports/{import_id}/calibrate",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/calibration/imports/{import_id}/calibrate", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
  },
})
const data = await resp.json()

Responses

Status Description
200 Successful Response
422 Validation Error

Errors

Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.

Status Code When
401 invalid_api_key Missing, malformed, or revoked API key.
403 insufficient_scope The key is valid but lacks a scope this endpoint requires.
404 not_found A referenced resource doesn't exist or isn't visible to your organisation.
409 conflict A conflicting change, or an Idempotency-Key reused with a different body.
429 rate_limited Per-key or per-org rate limit exceeded — back off with jitter and retry.

Response body 200

Field Type Description
a number Fitted first-unit cost coefficient of the curve.
b number Fitted log-log slope exponent of the learning curve.
env_id string Identifier of the calibrated environment.
fit object Structured object with the fitted parameters and fit diagnostics.
fitted_accuracy number Accuracy (0–1) of the fitted policy on the training rows.
fitted_policy object Structured object describing the fitted selection policy.
grid_size integer Number of policy candidates evaluated during the fit.
history_received integer Historical choice records supplied in the request.
history_rejected object[] Records that could not be used, each with its position in the request and why. Capped at 50 entries; the counts above are never truncated.
history_used integer Records the fit actually used. Lower than history_received means some could not be read; see history_rejected.
holdout_accuracy number Accuracy (0–1) of the fitted policy on held-out rows.
holdout_coverage number Fraction (0–1) of held-out actuals falling inside the interval.
holdout_mape number Mean absolute percentage error of the PRE-calibration predictions on the held-out rows — the miss this run set out to close, not the outcome. Read mape_after for the result.
id string Identifier of the resulting record, when applicable.
ingested integer Number of observations persisted by an ingest call.
learning_rate number Learning rate (0–1): cost retained each time output doubles.
mape_after number Holdout MAPE AFTER the fit — the held-out rows re-priced under the calibrated result. The run's headline outcome; null when no rows were held out.
mape_before number Holdout MAPE before the fit (same figure as holdout_mape, named for the before/after pair).
matched integer Number of supplied rows matched to an existing quote.
message string Human-readable note about the run, when present.
model string Learning-curve model that was fit (crawford_unit or wright_cumulative).
n_holdout integer Number of rows held out to size the prediction interval.
n_iterations integer Iterations the midpoint solver ran before converging.
n_lots integer Number of production lots used in the curve fit.
n_observations integer Number of observations the calibration fit used.
n_train integer Number of rows used to fit the calibration.
part_revision_id string Identifier of the part revision that was fit.
policy_history_id string Identifier of the newly written selection-policy row.
r_squared number Coefficient of determination (0–1) of the curve fit.
residual_sigma_log number Standard deviation of the fit residuals in log space.
run_id string Identifier of the calibration run, when one was created.
samples integer Number of samples considered by the run.
samples_applied integer Number of samples actually applied to the fit.
scope string Scope the run applied to: 'platform' or 'environment'.
seed_accuracy number Accuracy (0–1) of the seed policy before calibration.
status string Outcome status of the calibration run.
target_metric string Metric the calibration targeted (e.g. 'unit_cost').
unknown_part_numbers string[] Part numbers that could not be resolved to a known part.
unmatched_part_ids string[] Part revision IDs with no quote at their lot size priced by the current version of the costing model, so they were skipped.
work_orders_received integer Number of ERP work-order rows received.
work_orders_resolved integer Number of work orders resolved to a known part revision.

Example response

{
  "a": 0,
  "b": 0,
  "env_id": "string",
  "fit": {},
  "fitted_accuracy": 0,
  "fitted_policy": {},
  "grid_size": 0,
  "history_received": 0,
  "history_rejected": [
    {}
  ],
  "history_used": 0,
  "holdout_accuracy": 0,
  "holdout_coverage": 0.9,
  "holdout_mape": 0.05,
  "id": "string",
  "ingested": 0,
  "learning_rate": 0,
  "mape_after": 0.05,
  "mape_before": 0.05,
  "matched": 0,
  "message": "string",
  "model": "string",
  "n_holdout": 0,
  "n_iterations": 0,
  "n_lots": 0,
  "n_observations": 0,
  "n_train": 0,
  "part_revision_id": "string",
  "policy_history_id": "string",
  "r_squared": 0,
  "residual_sigma_log": 0,
  "run_id": "string",
  "samples": 0,
  "samples_applied": 0,
  "scope": "string",
  "seed_accuracy": 0,
  "status": "string",
  "target_metric": "string",
  "unknown_part_numbers": [
    "string"
  ],
  "unmatched_part_ids": [
    "string"
  ],
  "work_orders_received": 0,
  "work_orders_resolved": 0
}

Commit the disclosed spend: price every unpriced (part, lot) pair.

POST /api/v1/calibration/imports/{import_id}/price

Creates and enqueues one calculation per unpriced (part, lot) pair. Each is billed, deduplicated and admitted exactly like a calculation you run directly — an identical prior run is served free. Idempotent: rows already priced or queued are skipped, so a retry resumes instead of duplicating. Poll GET /calibration/imports/{id} until settled.

Parameters

Name In Type Required Description
import_id path string yes Identifier of the import.

Request

curl -X POST https://api.arcnm.io/api/v1/calibration/imports/{import_id}/price \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/calibration/imports/{import_id}/price",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/calibration/imports/{import_id}/price", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
  },
})
const data = await resp.json()

Responses

Status Description
202 Successful Response
422 Validation Error

Errors

Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.

Status Code When
401 invalid_api_key Missing, malformed, or revoked API key.
403 insufficient_scope The key is valid but lacks a scope this endpoint requires.
404 not_found A referenced resource doesn't exist or isn't visible to your organisation.
409 conflict A conflicting change, or an Idempotency-Key reused with a different body.
429 rate_limited Per-key or per-org rate limit exceeded — back off with jitter and retry.

Response body 202

Field Type Description
accepted integer Rows whose calculation was created and enqueued.
calculations_created integer New calculation rows this call created.
import_id string
pairs_priced integer Distinct (part, lot) pairs fanned out by THIS call.
parked integer Rows whose calculation was created but parked on billing (spend cap / approval); resumable, not running.
remaining_needs_calc integer Rows still unpriced after this call (0 when fully committed).
served_from_cache integer Rows answered instantly from an identical prior run — never billed.
skipped integer Rows already priced or already queued before this call — a re-POST resumes, it never duplicates.
status string Import lifecycle status after the commit.

Example response

{
  "accepted": 0,
  "calculations_created": 0,
  "import_id": "string",
  "pairs_priced": 0,
  "parked": 0,
  "remaining_needs_calc": 0,
  "served_from_cache": 0,
  "skipped": 0,
  "status": "string"
}

Submit calibration observations for an environment.

POST /api/v1/calibration/outcomes

Submit a batch of observations (predicted vs. actual outcomes) for an environment so it can be calibrated. Tenant-bound; scope='environment' only. Safe for AI agent use: idempotent on each observation's natural key and rate-limited by the global tenant policy. Recalibrate the environment afterwards to apply the accumulated observations. Take each predicted value from a current quote: an observation is recorded with the version of the costing model current when it is submitted, and once the model changes, recalibration leaves it out until it is submitted again with a predicted value from the new model.

Request body (application/json)

Field Type Required Description
basis herstellkosten | selbstkosten | angebotspreis no Which rung of the cost sheet these numbers are measured at: 'herstellkosten' (manufacturing cost per unit — what a work order books), 'selbstkosten' (total cost per unit, including administration, selling, freight and duty) or 'angebotspreis' (the quoted price per unit, including the profit mark-up). Each is compared against the model's own figure at the same rung. Omit it only if you genuinely do not know: an unstated basis is read as manufacturing cost and the environment's calibration report flags how many rows were unstated.
env_id string no Target environment; required when scope is 'environment'.
observations object[] no Up to 20,000 observations; each carries target_metric, observation_type, predicted, and actual.
oracle string yes Source of the observations (e.g. operator report, MES, supplier quote).
scope platform | environment yes Scope of the observations; tenant tokens may submit 'environment' only.

Request

curl -X POST https://api.arcnm.io/api/v1/calibration/outcomes \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "scope": "platform",
    "oracle": "string"
  }'
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/calibration/outcomes",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "scope": "platform",
        "oracle": "string"
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/calibration/outcomes", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "scope": "platform",
    "oracle": "string"
  }),
})
const data = await resp.json()

Responses

Status Description
200 Successful Response
422 Validation Error

Errors

Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.

Status Code When
401 invalid_api_key Missing, malformed, or revoked API key.
403 insufficient_scope The key is valid but lacks a scope this endpoint requires.
409 conflict A conflicting change, or an Idempotency-Key reused with a different body.
429 rate_limited Per-key or per-org rate limit exceeded — back off with jitter and retry.

Response body 200

Field Type Description
a number Fitted first-unit cost coefficient of the curve.
b number Fitted log-log slope exponent of the learning curve.
env_id string Identifier of the calibrated environment.
fit object Structured object with the fitted parameters and fit diagnostics.
fitted_accuracy number Accuracy (0–1) of the fitted policy on the training rows.
fitted_policy object Structured object describing the fitted selection policy.
grid_size integer Number of policy candidates evaluated during the fit.
history_received integer Historical choice records supplied in the request.
history_rejected object[] Records that could not be used, each with its position in the request and why. Capped at 50 entries; the counts above are never truncated.
history_used integer Records the fit actually used. Lower than history_received means some could not be read; see history_rejected.
holdout_accuracy number Accuracy (0–1) of the fitted policy on held-out rows.
holdout_coverage number Fraction (0–1) of held-out actuals falling inside the interval.
holdout_mape number Mean absolute percentage error of the PRE-calibration predictions on the held-out rows — the miss this run set out to close, not the outcome. Read mape_after for the result.
id string Identifier of the resulting record, when applicable.
ingested integer Number of observations persisted by an ingest call.
learning_rate number Learning rate (0–1): cost retained each time output doubles.
mape_after number Holdout MAPE AFTER the fit — the held-out rows re-priced under the calibrated result. The run's headline outcome; null when no rows were held out.
mape_before number Holdout MAPE before the fit (same figure as holdout_mape, named for the before/after pair).
matched integer Number of supplied rows matched to an existing quote.
message string Human-readable note about the run, when present.
model string Learning-curve model that was fit (crawford_unit or wright_cumulative).
n_holdout integer Number of rows held out to size the prediction interval.
n_iterations integer Iterations the midpoint solver ran before converging.
n_lots integer Number of production lots used in the curve fit.
n_observations integer Number of observations the calibration fit used.
n_train integer Number of rows used to fit the calibration.
part_revision_id string Identifier of the part revision that was fit.
policy_history_id string Identifier of the newly written selection-policy row.
r_squared number Coefficient of determination (0–1) of the curve fit.
residual_sigma_log number Standard deviation of the fit residuals in log space.
run_id string Identifier of the calibration run, when one was created.
samples integer Number of samples considered by the run.
samples_applied integer Number of samples actually applied to the fit.
scope string Scope the run applied to: 'platform' or 'environment'.
seed_accuracy number Accuracy (0–1) of the seed policy before calibration.
status string Outcome status of the calibration run.
target_metric string Metric the calibration targeted (e.g. 'unit_cost').
unknown_part_numbers string[] Part numbers that could not be resolved to a known part.
unmatched_part_ids string[] Part revision IDs with no quote at their lot size priced by the current version of the costing model, so they were skipped.
work_orders_received integer Number of ERP work-order rows received.
work_orders_resolved integer Number of work orders resolved to a known part revision.

Example response

{
  "a": 0,
  "b": 0,
  "env_id": "string",
  "fit": {},
  "fitted_accuracy": 0,
  "fitted_policy": {},
  "grid_size": 0,
  "history_received": 0,
  "history_rejected": [
    {}
  ],
  "history_used": 0,
  "holdout_accuracy": 0,
  "holdout_coverage": 0.9,
  "holdout_mape": 0.05,
  "id": "string",
  "ingested": 0,
  "learning_rate": 0,
  "mape_after": 0.05,
  "mape_before": 0.05,
  "matched": 0,
  "message": "string",
  "model": "string",
  "n_holdout": 0,
  "n_iterations": 0,
  "n_lots": 0,
  "n_observations": 0,
  "n_train": 0,
  "part_revision_id": "string",
  "policy_history_id": "string",
  "r_squared": 0,
  "residual_sigma_log": 0,
  "run_id": "string",
  "samples": 0,
  "samples_applied": 0,
  "scope": "string",
  "seed_accuracy": 0,
  "status": "string",
  "target_metric": "string",
  "unknown_part_numbers": [
    "string"
  ],
  "unmatched_part_ids": [
    "string"
  ],
  "work_orders_received": 0,
  "work_orders_resolved": 0
}