ARCNM

API reference

Environments

The Environments API manages costing environments: create, list, update, and delete them, set their identity, and attach the machines they cost against.

The Environments API manages costing environments: create, list, update, and delete them, set their identity, and attach the machines they cost against.

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).

Create Environment

POST /api/v1/environments

Create a costing environment, a named set of machines, tools, rates and overrides that calculations are priced in. By default it starts as a copy of your default environment; source can start it blank (tools only) or empty. Counts against your plan's environment limit.

Request body (application/json)

Field Type Required Description
currency string no ISO 4217 currency code the environment's rates are denominated in.
description string no Optional longer description of the environment.
empty boolean no Skip the documented default of attaching every tool in your crib to the new environment. With explicit tool membership (each environment prices only the tools attached to it), an empty environment unambiguously means 'deliberately empty'.
name string yes Human-readable name for the new environment.
region string no Geographic region this environment prices for (e.g. DE, US).
source baseline_copy | blank | empty no What the new environment starts from. baseline_copy (the default): a copy of your default environment — its machines, tools, rates and overrides. blank: a new environment with every tool of your crib attached and no machines. empty: nothing attached. Omitted → baseline_copy (or empty when the legacy empty: true is sent).
valid_from string yes ISO date (YYYY-MM-DD) from which the environment is effective.
valid_to string no ISO date (YYYY-MM-DD) the environment stops being effective; null = open-ended.

Request

curl -X POST https://api.arcnm.io/api/v1/environments \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "string",
    "valid_from": "2026-06-01"
  }'
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/environments",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "name": "string",
        "valid_from": "2026-06-01"
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "name": "string",
    "valid_from": "2026-06-01"
  }),
})
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
currency string ISO 4217 currency code the environment's rates are denominated in.
description string Optional longer description of the environment.
id string Unique identifier of the costing environment.
is_baseline boolean Whether this is the auto-provisioned default environment for the tenant.
machine_count integer Machines currently in this environment's fleet.
machines_cloned integer Number of machines copied into the environment; only set by the clone endpoints.
name string Human-readable name of the costing environment.
parent_environment_id string Environment this one inherits from, or null when it stands alone. Anything this environment does not state itself resolves from the parent and, recursively, from the parent's parent.
rate_count integer Rate rows this environment states itself (every kind, every validity window) — what GET /{env_id}/rates returns. Rates it only inherits from its parent are not counted.
rates_cloned integer Number of rate rows copied into the environment; only set by the clone endpoints.
region string Geographic region this environment prices for (e.g. DE, US).
scheduled_changes_dropped integer Memberships of the source whose validity window is not active today (scheduled future additions or removals). A clone is a snapshot as of today, so these do not travel; re-schedule them on the copy if you need them. Only set by the clone endpoint.
subcontractor_count integer Subcontractors this environment orders from.
tool_count integer Crib tools currently attached to this environment.
valid_from string ISO date (YYYY-MM-DD) from which this environment is effective.
valid_to string ISO date (YYYY-MM-DD) the environment stops being effective; null = open-ended.

Example response

{
  "currency": "EUR",
  "description": "string",
  "id": "string",
  "is_baseline": true,
  "machine_count": 0,
  "machines_cloned": 0,
  "name": "string",
  "parent_environment_id": "string",
  "rate_count": 0,
  "rates_cloned": 0,
  "region": "EU",
  "scheduled_changes_dropped": 0,
  "subcontractor_count": 0,
  "tool_count": 0,
  "valid_from": "string",
  "valid_to": "string"
}

List Environments

GET /api/v1/environments/

List the organization's costing environments, newest first.

The response is a plain array, so the page position travels in the Link (RFC 8288) and X-Next-Cursor / X-Has-More response headers.

Paginated. Pass cursor (from the previous response) to fetch the next page; limit caps the page size.

Parameters

Name In Type Required Description
cursor query string no Opaque position token from the previous page's next_cursor (or the Link / X-Next-Cursor response header). Omit it for the first page. Keep every other query parameter identical for the whole walk — a cursor replayed against different filters is rejected.
limit query integer no Maximum rows to return in one page.
order query asc | desc no Sort direction over the collection's ordering key. Use asc to reconcile a batch: rows come oldest-first, so work created while you page lands after your position instead of shifting rows under it.
created_after query string no Only rows created at or after this instant (RFC 3339, e.g. 2026-07-20T09:00:00Z). Inclusive.
created_before query string no Only rows created strictly before this instant (RFC 3339). Exclusive, so an after/before pair tiles a range without overlap.
offset query integer no Rows to skip. Superseded by cursor, which is stable under concurrent writes; kept for existing integrations. Bounded — past the cap, page with cursor.

Request

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

resp = requests.get(
    "https://api.arcnm.io/api/v1/environments/",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/", {
  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.
429 rate_limited Per-key or per-org rate limit exceeded — back off with jitter and retry.

Delete Environment

DELETE /api/v1/environments/{env_id}

Delete an environment. ON DELETE CASCADE on the rate + machine- membership tables means the rates + memberships disappear with it; the underlying MachineDefinition rows survive (they're org- scoped, not env-scoped, and may be reused by sibling envs).

Parameters

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

Request

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

resp = requests.delete(
    "https://api.arcnm.io/api/v1/environments/{env_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/environments/{env_id}", {
  method: "DELETE",
  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
message string Human-readable result of the operation.

Example response

{
  "message": "string"
}

Get Environment

GET /api/v1/environments/{env_id}

Read one costing environment in full: its currency, region, validity dates and the rates, overheads and surcharges that calculations in it are priced with.

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/environments/{env_id} \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.get(
    "https://api.arcnm.io/api/v1/environments/{env_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/environments/{env_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
cash_discount_pct number Customer cash discount (Kundenskonto) this environment prices into the offer, as a fraction of the target price, 'im Hundert' (0.02 = 2 %); null = states nothing, so the parent environment's value applies, else the platform default, which is none.
config_revision integer Monotonic configuration revision; every configuration write increments it, and each calculation records the revision it priced under.
currency string ISO 4217 currency code the environment's rates are denominated in.
customer_discount_pct number Customer discount (Kundenrabatt) this environment prices into the offer, as a fraction of the list price, 'im Hundert'; null = states nothing, so the parent environment's value applies, else the platform default, which is none.
default_labour_rate_eur_per_h number Labour rate (EUR/h) this environment states. Read-only here: an effective labour rate row beats this column outright, and that row is the setter. Null = states nothing, so the parent environment's value applies, else the platform default.
default_material_family string Default material family this environment states for calcs that name no grade; null = states nothing, so the parent environment's value applies, else the platform default.
description string Optional longer description of the environment.
duty_eur_per_part number Customs duty per unit this environment states, in its own currency; null = states nothing, so the parent environment's value applies, else the platform default, which is none.
energy_eur_per_kwh number Energy price (EUR/kWh) this environment states; null = states nothing, so the parent environment's value applies, else the platform default.
eoq_holding_cost_per_kg_per_year number Storage charge for finished stock this environment states, per kg and year; null = states nothing, so the parent environment's value applies, else the platform default.
factor_first_off number First-article-inspection factor this environment states: it scales a full inspection into the per-lot first-off time (0 = none). Null = states nothing, so the parent environment's value applies, else the platform default.
fai_cost number Flat first-article-inspection cost per lot this environment states, in its own currency; null = states nothing, so the parent environment's value applies, else the platform default.
freight_eur_per_part number Outbound freight per unit this environment states, in its own currency; null = states nothing, so the parent environment's value applies, else the platform default, which is none.
holding_cost_rate_per_year number Imputed interest rate on capital tied up in finished stock this environment states, per year (0.08 = 8 %); null = states nothing, so the parent environment's value applies, else the platform default.
id string Unique identifier of the costing environment.
is_baseline boolean Whether this is the auto-provisioned default environment for the tenant.
machine_count integer Machines currently in this environment's fleet.
machines_cloned integer Number of machines copied into the environment; only set by the clone endpoints.
margin_base string The base of the profit mark-up, as this environment states it: 'selbstkosten' (total cost) or 'netto_selbstkosten' (total cost without packaging, freight and duty); null = states nothing, so the parent environment's value applies, else the platform default, 'selbstkosten'.
margin_pct number Profit mark-up on total cost this environment states; null = states nothing, so the parent environment's value applies, else the platform default, which adds no mark-up — the quote is a should-cost.
material_overhead_pct number Procurement / storage surcharge on material cost this environment states (0.07 = 7 %); null = states nothing, so the parent environment's value applies, else the platform default, which adds no surcharge.
name string Human-readable name of the costing environment.
ordering_cost_eur_per_order number The buyer's fixed cost of placing and receiving one order (Bestellkosten je Bestellvorgang) this environment states, in its own currency; null = states nothing, so the parent environment's value applies, else the platform default of 0. Read by the lot-size scenarios where the buyer orders in lots.
overhead_fixed_eur_per_part number Fixed overhead (EUR/part) this environment states. Read-only here: an effective fixed-overhead rate row beats this column outright, and that row is the setter. Null = states nothing, so the parent environment's value applies, else the platform default.
overhead_var_pct number Variable-overhead fraction this environment states. Read-only here: an effective variable-overhead rate row beats this column outright, and that row is the setter. Null = states nothing, so the parent environment's value applies, else the platform default (0.15 on direct and machine cost) — unless a material or production overhead is stated, which switches that default off: the Zuschlagskalkulation ladder and this rate are one overhead system, never charged together by default.
packaging_eur_per_part number Packaging material per unit this environment states, in its own currency; null = states nothing, so the parent environment's value applies, else the platform default, which is none.
parent_environment_id string Environment this one inherits from, or null when it stands alone. Anything this environment does not state itself resolves from the parent and, recursively, from the parent's parent.
production_overhead_pct number Surcharge on production wages this environment states (0.80 = 80 %); null = states nothing, so the parent environment's value applies, else the platform default, which adds no surcharge.
rate_count integer Rate rows this environment states itself (every kind, every validity window) — what GET /{env_id}/rates returns. Rates it only inherits from its parent are not counted.
rates_cloned integer Number of rate rows copied into the environment; only set by the clone endpoints.
region string Geographic region this environment prices for (e.g. DE, US).
reject_rate_base string What a reject costs, as this environment states it: 'material' (the stock drawn for the rejected parts) or 'herstellkosten' (the whole part before scrap, for rejects found at final inspection); null = states nothing, so the parent environment's value applies, else the platform default, 'material'.
reject_rate_pct number Share of started parts this environment states that the shop rejects (0.03 = 3 %); null = states nothing, so the parent environment's value applies, else the platform default, which rejects none.
sales_commission_pct number Sales commission (Vertreterprovision) this environment prices into the offer, as a fraction of the target price, 'im Hundert'; null = states nothing, so the parent environment's value applies, else the platform default, which is none.
sample_pct_default number In-process inspection sampling fraction (0..1) this environment states; null = states nothing, so the parent environment's value applies, else the platform default.
scheduled_changes_dropped integer Memberships of the source whose validity window is not active today (scheduled future additions or removals). A clone is a snapshot as of today, so these do not travel; re-schedule them on the copy if you need them. Only set by the clone endpoint.
scrap_credit_eur_per_kg number Flat return per kg of scrap this environment states, net of the cost of selling it, in its own currency; null = states nothing, so the parent environment's value applies, else the platform default, which is none.
sga_pct number Administration and selling surcharge on manufacturing cost this environment states; null = states nothing, so the parent environment's value applies, else the platform default, which adds no surcharge.
source_preset_id string Environment this one was cloned from (lineage).
space_eur_per_m2_y number Floor-space cost (EUR/m²·year) this environment states; null = states nothing, so the parent environment's value applies, else the platform default.
subcontract_overhead_pct number Procurement overhead on subcontract work this environment states, as a fraction of the subcontract cost (0.05 = 5 %); null = states nothing, so the parent environment's value applies, else the platform default, which is none.
subcontractor_count integer Subcontractors this environment orders from.
tool_count integer Crib tools currently attached to this environment.
valid_from string ISO date (YYYY-MM-DD) from which this environment is effective.
valid_to string ISO date (YYYY-MM-DD) the environment stops being effective; null = open-ended.

Example response

{
  "cash_discount_pct": 0,
  "config_revision": 0,
  "currency": "EUR",
  "customer_discount_pct": 0,
  "default_labour_rate_eur_per_h": 0,
  "default_material_family": "string",
  "description": "string",
  "duty_eur_per_part": 0,
  "energy_eur_per_kwh": 0,
  "eoq_holding_cost_per_kg_per_year": 0,
  "factor_first_off": 0,
  "fai_cost": 0,
  "freight_eur_per_part": 0,
  "holding_cost_rate_per_year": 0,
  "id": "string",
  "is_baseline": true,
  "machine_count": 0,
  "machines_cloned": 0,
  "margin_base": "string",
  "margin_pct": 0,
  "material_overhead_pct": 0,
  "name": "string",
  "ordering_cost_eur_per_order": 0,
  "overhead_fixed_eur_per_part": 0,
  "overhead_var_pct": 0,
  "packaging_eur_per_part": 0,
  "parent_environment_id": "string",
  "production_overhead_pct": 0,
  "rate_count": 0,
  "rates_cloned": 0,
  "region": "EU",
  "reject_rate_base": "string",
  "reject_rate_pct": 0,
  "sales_commission_pct": 0,
  "sample_pct_default": 0,
  "scheduled_changes_dropped": 0,
  "scrap_credit_eur_per_kg": 0,
  "sga_pct": 0,
  "source_preset_id": "string",
  "space_eur_per_m2_y": 0,
  "subcontract_overhead_pct": 0,
  "subcontractor_count": 0,
  "tool_count": 0,
  "valid_from": "string",
  "valid_to": "string"
}

Get Environment Assumptions

GET /api/v1/environments/{env_id}/assumptions

Every pricing parameter this costing environment has stated — the value a calculation would use right now, with the tier that won and where it came from. Filter with bin_prefix (e.g. milling.), or ask for the economics alone with parameters=false.

Integration credentials receive your own statements: environment overrides, values inherited from a parent environment, and your tools' own cutting data. The full parameter plane behind them is shown in the app.

Parity contract: values here come from the SAME resolver construction the pricing pipeline uses, at the environment's current config_revision.

Parameters

Name In Type Required Description
env_id path string yes Identifier of the env.
bin_prefix query string no
parameters query boolean no Include the parameter plane — around a thousand rows, each resolved through the environment chain. Pass false when you only need the stated economics: parameters comes back empty and total is 0, which is what the count has always meant — the rows this response carries.

Request

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

resp = requests.get(
    "https://api.arcnm.io/api/v1/environments/{env_id}/assumptions",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/assumptions", {
  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
chain string[] Environment names, leaf to root — the inheritance path.
config_revision integer
economics object
environment_id string
name string
named_sigmas object The named confidence constants manual writes carry (relative 1-sigma): manual_entry, tuning_and_rate_pin, subcontract_override.
parameter_version string Version stamp of the parameter set this resolution ran under; it changes whenever the underlying defaults change, so two responses with the same stamp are directly comparable.
parameters AssumptionRow[]
total integer Number of parameter rows in this response — 0 when it was requested with parameters=false.

Example response

{
  "chain": [
    "string"
  ],
  "config_revision": 0,
  "economics": {},
  "environment_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "name": "string",
  "named_sigmas": {},
  "parameter_version": "string",
  "parameters": [
    {
      "bin": "string",
      "parameter": "string",
      "sigma": 0,
      "source": "string",
      "source_id": "string",
      "unit": "",
      "value": 0
    }
  ],
  "total": 0
}

Get Environment Audit

GET /api/v1/environments/{env_id}/audit

Audit the data a price in this environment depends on: unsourced or out-of-range cutting data and overrides, tools priced without a life, unresolvable machines, spindle classes with no power stated, members that source nothing, rate rows in another currency, and — while the environment credits sold scrap — published scrap prices more than 90 days old. Read-only; every finding names the panel that fixes it.

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/environments/{env_id}/audit \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.get(
    "https://api.arcnm.io/api/v1/environments/{env_id}/audit",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/audit", {
  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
clean boolean No error or warning finding.
counts AuditCountsPublic
findings AuditFindingPublic[]

Example response

{
  "clean": true,
  "counts": {
    "error": 0,
    "info": 0,
    "warning": 0
  },
  "findings": [
    {
      "code": "string",
      "panel": "string",
      "params": {
        "age_days": 0,
        "bin": "string",
        "count": 0,
        "distance_pct": 0,
        "env_currency": "string",
        "material": "string",
        "material_category": "string",
        "max_spindle_rpm": 0,
        "missing": "string",
        "observed_on": "string",
        "parameter": "string",
        "platform": 0,
        "price_eur": 0,
        "retired_on": "string",
        "row_currency": "string",
        "scrap_class": "string",
        "spindle_power_kw": 0,
        "tool_life_min": 0,
        "tools": [],
        "value": 0
      },
      "severity": "string",
      "subject_id": "string",
      "subject_kind": "string",
      "subject_name": "string"
    }
  ]
}

Clone Environment

POST /api/v1/environments/{env_id}/clone

Duplicate one of the tenant's OWN environments (env + rates + fleet) into a fresh, non-baseline, uncalibrated copy — "start from this environment, then tune or calibrate the duplicate". Only the tenant's own envs can be cloned (any other id, including a platform preset or another tenant's env, → 404).

Parameters

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

Request body (application/json)

Field Type Required Description
name string no Optional name for the duplicate; defaults to ' (copy)'.

Request

curl -X POST https://api.arcnm.io/api/v1/environments/{env_id}/clone \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "string"
  }'
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/environments/{env_id}/clone",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "name": "string"
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/clone", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "name": "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.
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 201

Field Type Description
currency string ISO 4217 currency code the environment's rates are denominated in.
description string Optional longer description of the environment.
id string Unique identifier of the costing environment.
is_baseline boolean Whether this is the auto-provisioned default environment for the tenant.
machine_count integer Machines currently in this environment's fleet.
machines_cloned integer Number of machines copied into the environment; only set by the clone endpoints.
name string Human-readable name of the costing environment.
parent_environment_id string Environment this one inherits from, or null when it stands alone. Anything this environment does not state itself resolves from the parent and, recursively, from the parent's parent.
rate_count integer Rate rows this environment states itself (every kind, every validity window) — what GET /{env_id}/rates returns. Rates it only inherits from its parent are not counted.
rates_cloned integer Number of rate rows copied into the environment; only set by the clone endpoints.
region string Geographic region this environment prices for (e.g. DE, US).
scheduled_changes_dropped integer Memberships of the source whose validity window is not active today (scheduled future additions or removals). A clone is a snapshot as of today, so these do not travel; re-schedule them on the copy if you need them. Only set by the clone endpoint.
subcontractor_count integer Subcontractors this environment orders from.
tool_count integer Crib tools currently attached to this environment.
valid_from string ISO date (YYYY-MM-DD) from which this environment is effective.
valid_to string ISO date (YYYY-MM-DD) the environment stops being effective; null = open-ended.

Example response

{
  "currency": "EUR",
  "description": "string",
  "id": "string",
  "is_baseline": true,
  "machine_count": 0,
  "machines_cloned": 0,
  "name": "string",
  "parent_environment_id": "string",
  "rate_count": 0,
  "rates_cloned": 0,
  "region": "EU",
  "scheduled_changes_dropped": 0,
  "subcontractor_count": 0,
  "tool_count": 0,
  "valid_from": "string",
  "valid_to": "string"
}

Get Cutting Data

GET /api/v1/environments/{env_id}/cutting-data

The environment's effective cutting data (Schnittwerte) per process family and material group, and per ISO 513 workpiece group for the cutting speed — the finer axis a grade's speed is resolved from. Your own value on either axis beats a platform figure; where you have set both, the workpiece group wins as the more specific statement. Every row carries its provenance ("platform default vs your override") and the accepted value window for that parameter.

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/environments/{env_id}/cutting-data \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.get(
    "https://api.arcnm.io/api/v1/environments/{env_id}/cutting-data",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/cutting-data", {
  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
rows CuttingDataRowPublic[] Every editable cutting-data entry with its effective value.

Example response

{
  "rows": [
    {
      "axis": "material",
      "hardness_band": "string",
      "iso_group": "string",
      "material_code": "string",
      "material_label": "string",
      "max": 0,
      "min": 0,
      "parameter": "string",
      "process": "string",
      "source": "your_override",
      "unit": "string",
      "value": 0
    }
  ]
}

Update Cutting Data

PUT /api/v1/environments/{env_id}/cutting-data

Write environment-level cutting-data overrides (audited).

Each entry targets one (process, material-or-workpiece-group, parameter) from the closed catalogue; a null value clears your override so the platform default applies again. Your own value on either axis beats a platform figure; where you have set a cutting speed on both the material and its workpiece group, the group wins for every grade mapped to that leaf — the order the pricing engine resolves them in. Values must sit inside the parameter's plausibility window. Changes re-price new calculations in this environment and its child environments immediately.

Parameters

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

Request body (application/json)

Field Type Required Description
values CuttingDataEntryUpdate[] yes Cutting-data entries to write (null value = reset to default).

Request

curl -X PUT https://api.arcnm.io/api/v1/environments/{env_id}/cutting-data \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "values": [
      {
        "material_code": "string",
        "parameter": "string",
        "process": "string",
        "value": 0
      }
    ]
  }'
import requests

resp = requests.put(
    "https://api.arcnm.io/api/v1/environments/{env_id}/cutting-data",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "values": [
            {
                "material_code": "string",
                "parameter": "string",
                "process": "string",
                "value": 0
            }
        ]
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/cutting-data", {
  method: "PUT",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "values": [
      {
        "material_code": "string",
        "parameter": "string",
        "process": "string",
        "value": 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
rows CuttingDataRowPublic[] Every editable cutting-data entry with its effective value.

Example response

{
  "rows": [
    {
      "axis": "material",
      "hardness_band": "string",
      "iso_group": "string",
      "material_code": "string",
      "material_label": "string",
      "max": 0,
      "min": 0,
      "parameter": "string",
      "process": "string",
      "source": "your_override",
      "unit": "string",
      "value": 0
    }
  ]
}

Update the environment's stated economics.

PATCH /api/v1/environments/{env_id}/economics

Set the typed regional economics this environment states itself: energy price, floor-space cost, default material family, the cost-sheet surcharges (material overhead, residual production overhead, administration and selling, profit mark-up) with packaging, freight and duty, the price terms priced in 'im Hundert' (cash discount, sales commission, customer discount), and the inspection / holding scalars. Omit a field to keep it; send null to clear it, which stores nothing on this environment so the value inherits from the parent environment again and ends at the platform default. Takes effect on the next calculation.

Parameters

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

Request body (application/json)

Field Type Required Description
cash_discount_pct number no Customer cash discount (Kundenskonto) priced into the offer 'im Hundert' (0.02 = 2 %): it is a share of the target price that already contains it, so a customer who deducts it still pays the cash price. Null clears back to inherit / platform default, which prices none.
customer_discount_pct number no Customer discount (Kundenrabatt) priced into the offer 'im Hundert', as a share of the list price (the offer price). Null clears back to inherit / platform default, which prices none.
default_material_family string no Default material family for calculations that name no grade (steel, stainless, aluminum, titanium, nickel, other); null clears back to inherit / platform default.
duty_eur_per_part number no Customs duty per unit, in the environment's currency; null clears back to inherit / platform default.
energy_eur_per_kwh number no Stated energy price per kWh, in the environment's currency; null clears back to inherit / platform default.
eoq_holding_cost_per_kg_per_year number no Storage charge for finished stock per kg and year; null clears back to inherit / platform default.
factor_first_off number no First-article-inspection factor: scales a full inspection into the per-lot first-off time (0 = none, and an explicit 0 is a statement that survives). Null clears back to inherit / platform default.
fai_cost number no Flat first-article-inspection cost per lot, in the environment's currency; null clears back to inherit / platform default.
freight_eur_per_part number no Outbound freight per unit, in the environment's currency; null clears back to inherit / platform default.
holding_cost_rate_per_year number no Imputed yearly interest rate on capital tied up in finished stock (0.08 = 8 %); null clears back to inherit / platform default.
margin_base selbstkosten | netto_selbstkosten no The base of the profit mark-up. 'selbstkosten' charges margin_pct on the total cost. 'netto_selbstkosten' charges it on the total cost without packaging, freight and duty — the base German public-contract pricing names. Null clears back to inherit / platform default, 'selbstkosten'.
margin_pct number no Profit mark-up on total cost (0.10 = 10 %); null clears back to inherit / platform default, which adds none.
material_overhead_pct number no Procurement / storage surcharge on material cost (0.07 = 7 %); null clears back to inherit / platform default.
ordering_cost_eur_per_order number no The buyer's fixed cost of placing and receiving one order, in the environment's currency; null clears back to inherit / platform default (0).
packaging_eur_per_part number no Packaging material per unit (Sondereinzelkosten des Vertriebs), in the environment's currency; the packing labour is priced on the labour line, not here. Null clears back to inherit / platform default.
production_overhead_pct number no Surcharge on production wages (0.80 = 80 %); null clears back to inherit / platform default.
reject_rate_base material | herstellkosten no What a reject costs. 'material' charges the reject rate on the stock drawn for the parts that fail — the lower bound, right when rejects are found at the first operation. 'herstellkosten' charges it on the whole part before scrap (material, manufacturing and their overheads), the industry convention for end-item scrap found at final inspection. One method, never both. Null clears back to inherit / platform default, 'material'.
reject_rate_pct number no Share of the parts this shop STARTS that it rejects, as a fraction (0.03 = 3 %, not 3). It is a yield, not a surcharge: to ship 100 good parts at 0.03 the shop starts 104, and the stock drawn for the four that never shipped is charged to the ones that did. Charged on the base reject_rate_base names: the material draw by default, or the whole part when the shop finds its rejects at final inspection. Must be below 0.5 — half a shop's output failing is a process problem, not a costing input. Null clears back to inherit / platform default, which rejects nothing.
sales_commission_pct number no Sales commission (Vertreterprovision) priced into the offer 'im Hundert', as a share of the target price. Null clears back to inherit / platform default, which prices none.
sample_pct_default number no In-process inspection sampling fraction (0..1). An explicit 0 switches sampling off and is a statement that survives; null clears back to inherit / platform default.
scrap_credit_eur_per_kg number no Scrap credit (Reststoffgutschrift): the flat return per kg of scrap, in the environment's currency — what the dealer pays less what sorting, containers and haulage cost you. Paid on the scrap a dealer buys (sheet skeleton, chips, cut-offs) wherever no scrap price is stated for that kind of scrap; never on a reusable remnant or on the part itself. The credit is never more than the material cost; it reduces the material before the material overhead and a material scrap allowance are charged. Null clears back to inherit / platform default, which credits nothing.
sga_pct number no Administration and selling surcharge on manufacturing cost (0.12 = 12 %); null clears back to inherit / platform default.
space_eur_per_m2_y number no Stated floor-space cost per m² and year, in the environment's currency; null clears back to inherit / platform default.
subcontract_overhead_pct number no Procurement overhead on subcontract work, as a fraction of the subcontract cost (0.05 = 5 %), charged inside the manufacturing cost. Null clears back to inherit / platform default, which adds none.

Request

curl -X PATCH https://api.arcnm.io/api/v1/environments/{env_id}/economics \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "cash_discount_pct": 0,
    "customer_discount_pct": 0,
    "default_material_family": "string",
    "duty_eur_per_part": 0,
    "energy_eur_per_kwh": 0,
    "eoq_holding_cost_per_kg_per_year": 0,
    "factor_first_off": 0,
    "fai_cost": 0,
    "freight_eur_per_part": 0,
    "holding_cost_rate_per_year": 0,
    "margin_base": "selbstkosten",
    "margin_pct": 0,
    "material_overhead_pct": 0,
    "ordering_cost_eur_per_order": 0,
    "packaging_eur_per_part": 0,
    "production_overhead_pct": 0,
    "reject_rate_base": "material",
    "reject_rate_pct": 0,
    "sales_commission_pct": 0,
    "sample_pct_default": 0,
    "scrap_credit_eur_per_kg": 0,
    "sga_pct": 0,
    "space_eur_per_m2_y": 0,
    "subcontract_overhead_pct": 0
  }'
import requests

resp = requests.patch(
    "https://api.arcnm.io/api/v1/environments/{env_id}/economics",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "cash_discount_pct": 0,
        "customer_discount_pct": 0,
        "default_material_family": "string",
        "duty_eur_per_part": 0,
        "energy_eur_per_kwh": 0,
        "eoq_holding_cost_per_kg_per_year": 0,
        "factor_first_off": 0,
        "fai_cost": 0,
        "freight_eur_per_part": 0,
        "holding_cost_rate_per_year": 0,
        "margin_base": "selbstkosten",
        "margin_pct": 0,
        "material_overhead_pct": 0,
        "ordering_cost_eur_per_order": 0,
        "packaging_eur_per_part": 0,
        "production_overhead_pct": 0,
        "reject_rate_base": "material",
        "reject_rate_pct": 0,
        "sales_commission_pct": 0,
        "sample_pct_default": 0,
        "scrap_credit_eur_per_kg": 0,
        "sga_pct": 0,
        "space_eur_per_m2_y": 0,
        "subcontract_overhead_pct": 0
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/economics", {
  method: "PATCH",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "cash_discount_pct": 0,
    "customer_discount_pct": 0,
    "default_material_family": "string",
    "duty_eur_per_part": 0,
    "energy_eur_per_kwh": 0,
    "eoq_holding_cost_per_kg_per_year": 0,
    "factor_first_off": 0,
    "fai_cost": 0,
    "freight_eur_per_part": 0,
    "holding_cost_rate_per_year": 0,
    "margin_base": "selbstkosten",
    "margin_pct": 0,
    "material_overhead_pct": 0,
    "ordering_cost_eur_per_order": 0,
    "packaging_eur_per_part": 0,
    "production_overhead_pct": 0,
    "reject_rate_base": "material",
    "reject_rate_pct": 0,
    "sales_commission_pct": 0,
    "sample_pct_default": 0,
    "scrap_credit_eur_per_kg": 0,
    "sga_pct": 0,
    "space_eur_per_m2_y": 0,
    "subcontract_overhead_pct": 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
cash_discount_pct number Customer cash discount (Kundenskonto) this environment prices into the offer, as a fraction of the target price, 'im Hundert' (0.02 = 2 %); null = states nothing, so the parent environment's value applies, else the platform default, which is none.
config_revision integer Monotonic configuration revision; every configuration write increments it, and each calculation records the revision it priced under.
currency string ISO 4217 currency code the environment's rates are denominated in.
customer_discount_pct number Customer discount (Kundenrabatt) this environment prices into the offer, as a fraction of the list price, 'im Hundert'; null = states nothing, so the parent environment's value applies, else the platform default, which is none.
default_labour_rate_eur_per_h number Labour rate (EUR/h) this environment states. Read-only here: an effective labour rate row beats this column outright, and that row is the setter. Null = states nothing, so the parent environment's value applies, else the platform default.
default_material_family string Default material family this environment states for calcs that name no grade; null = states nothing, so the parent environment's value applies, else the platform default.
description string Optional longer description of the environment.
duty_eur_per_part number Customs duty per unit this environment states, in its own currency; null = states nothing, so the parent environment's value applies, else the platform default, which is none.
energy_eur_per_kwh number Energy price (EUR/kWh) this environment states; null = states nothing, so the parent environment's value applies, else the platform default.
eoq_holding_cost_per_kg_per_year number Storage charge for finished stock this environment states, per kg and year; null = states nothing, so the parent environment's value applies, else the platform default.
factor_first_off number First-article-inspection factor this environment states: it scales a full inspection into the per-lot first-off time (0 = none). Null = states nothing, so the parent environment's value applies, else the platform default.
fai_cost number Flat first-article-inspection cost per lot this environment states, in its own currency; null = states nothing, so the parent environment's value applies, else the platform default.
freight_eur_per_part number Outbound freight per unit this environment states, in its own currency; null = states nothing, so the parent environment's value applies, else the platform default, which is none.
holding_cost_rate_per_year number Imputed interest rate on capital tied up in finished stock this environment states, per year (0.08 = 8 %); null = states nothing, so the parent environment's value applies, else the platform default.
id string Unique identifier of the costing environment.
is_baseline boolean Whether this is the auto-provisioned default environment for the tenant.
machine_count integer Machines currently in this environment's fleet.
machines_cloned integer Number of machines copied into the environment; only set by the clone endpoints.
margin_base string The base of the profit mark-up, as this environment states it: 'selbstkosten' (total cost) or 'netto_selbstkosten' (total cost without packaging, freight and duty); null = states nothing, so the parent environment's value applies, else the platform default, 'selbstkosten'.
margin_pct number Profit mark-up on total cost this environment states; null = states nothing, so the parent environment's value applies, else the platform default, which adds no mark-up — the quote is a should-cost.
material_overhead_pct number Procurement / storage surcharge on material cost this environment states (0.07 = 7 %); null = states nothing, so the parent environment's value applies, else the platform default, which adds no surcharge.
name string Human-readable name of the costing environment.
ordering_cost_eur_per_order number The buyer's fixed cost of placing and receiving one order (Bestellkosten je Bestellvorgang) this environment states, in its own currency; null = states nothing, so the parent environment's value applies, else the platform default of 0. Read by the lot-size scenarios where the buyer orders in lots.
overhead_fixed_eur_per_part number Fixed overhead (EUR/part) this environment states. Read-only here: an effective fixed-overhead rate row beats this column outright, and that row is the setter. Null = states nothing, so the parent environment's value applies, else the platform default.
overhead_var_pct number Variable-overhead fraction this environment states. Read-only here: an effective variable-overhead rate row beats this column outright, and that row is the setter. Null = states nothing, so the parent environment's value applies, else the platform default (0.15 on direct and machine cost) — unless a material or production overhead is stated, which switches that default off: the Zuschlagskalkulation ladder and this rate are one overhead system, never charged together by default.
packaging_eur_per_part number Packaging material per unit this environment states, in its own currency; null = states nothing, so the parent environment's value applies, else the platform default, which is none.
parent_environment_id string Environment this one inherits from, or null when it stands alone. Anything this environment does not state itself resolves from the parent and, recursively, from the parent's parent.
production_overhead_pct number Surcharge on production wages this environment states (0.80 = 80 %); null = states nothing, so the parent environment's value applies, else the platform default, which adds no surcharge.
rate_count integer Rate rows this environment states itself (every kind, every validity window) — what GET /{env_id}/rates returns. Rates it only inherits from its parent are not counted.
rates_cloned integer Number of rate rows copied into the environment; only set by the clone endpoints.
region string Geographic region this environment prices for (e.g. DE, US).
reject_rate_base string What a reject costs, as this environment states it: 'material' (the stock drawn for the rejected parts) or 'herstellkosten' (the whole part before scrap, for rejects found at final inspection); null = states nothing, so the parent environment's value applies, else the platform default, 'material'.
reject_rate_pct number Share of started parts this environment states that the shop rejects (0.03 = 3 %); null = states nothing, so the parent environment's value applies, else the platform default, which rejects none.
sales_commission_pct number Sales commission (Vertreterprovision) this environment prices into the offer, as a fraction of the target price, 'im Hundert'; null = states nothing, so the parent environment's value applies, else the platform default, which is none.
sample_pct_default number In-process inspection sampling fraction (0..1) this environment states; null = states nothing, so the parent environment's value applies, else the platform default.
scheduled_changes_dropped integer Memberships of the source whose validity window is not active today (scheduled future additions or removals). A clone is a snapshot as of today, so these do not travel; re-schedule them on the copy if you need them. Only set by the clone endpoint.
scrap_credit_eur_per_kg number Flat return per kg of scrap this environment states, net of the cost of selling it, in its own currency; null = states nothing, so the parent environment's value applies, else the platform default, which is none.
sga_pct number Administration and selling surcharge on manufacturing cost this environment states; null = states nothing, so the parent environment's value applies, else the platform default, which adds no surcharge.
source_preset_id string Environment this one was cloned from (lineage).
space_eur_per_m2_y number Floor-space cost (EUR/m²·year) this environment states; null = states nothing, so the parent environment's value applies, else the platform default.
subcontract_overhead_pct number Procurement overhead on subcontract work this environment states, as a fraction of the subcontract cost (0.05 = 5 %); null = states nothing, so the parent environment's value applies, else the platform default, which is none.
subcontractor_count integer Subcontractors this environment orders from.
tool_count integer Crib tools currently attached to this environment.
valid_from string ISO date (YYYY-MM-DD) from which this environment is effective.
valid_to string ISO date (YYYY-MM-DD) the environment stops being effective; null = open-ended.

Example response

{
  "cash_discount_pct": 0,
  "config_revision": 0,
  "currency": "EUR",
  "customer_discount_pct": 0,
  "default_labour_rate_eur_per_h": 0,
  "default_material_family": "string",
  "description": "string",
  "duty_eur_per_part": 0,
  "energy_eur_per_kwh": 0,
  "eoq_holding_cost_per_kg_per_year": 0,
  "factor_first_off": 0,
  "fai_cost": 0,
  "freight_eur_per_part": 0,
  "holding_cost_rate_per_year": 0,
  "id": "string",
  "is_baseline": true,
  "machine_count": 0,
  "machines_cloned": 0,
  "margin_base": "string",
  "margin_pct": 0,
  "material_overhead_pct": 0,
  "name": "string",
  "ordering_cost_eur_per_order": 0,
  "overhead_fixed_eur_per_part": 0,
  "overhead_var_pct": 0,
  "packaging_eur_per_part": 0,
  "parent_environment_id": "string",
  "production_overhead_pct": 0,
  "rate_count": 0,
  "rates_cloned": 0,
  "region": "EU",
  "reject_rate_base": "string",
  "reject_rate_pct": 0,
  "sales_commission_pct": 0,
  "sample_pct_default": 0,
  "scheduled_changes_dropped": 0,
  "scrap_credit_eur_per_kg": 0,
  "sga_pct": 0,
  "source_preset_id": "string",
  "space_eur_per_m2_y": 0,
  "subcontract_overhead_pct": 0,
  "subcontractor_count": 0,
  "tool_count": 0,
  "valid_from": "string",
  "valid_to": "string"
}

Read the environment's effective rates.

GET /api/v1/environments/{env_id}/effective-rates

The rates this environment's prices are computed with — per driver (machine hour, labour hour, overheads) and per machine, each with its source: your override, a calibrated adjustment, or the platform default. Rate detail is shown in the Arcanum app; integrations receive the status envelope.

Parameters

Name In Type Required Description
env_id path string yes Identifier of the env.
at query string no As-of date for effective-dated rates; defaults to today (UTC).

Request

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

resp = requests.get(
    "https://api.arcnm.io/api/v1/environments/{env_id}/effective-rates",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/effective-rates", {
  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
as_of string
calibrated boolean Whether this environment has been calibrated to your actuals.
currency string
env_id string
mode per_driver | overall
note string
overhead_var_base direct | material | labour | conversion The cost the variable-overhead percentage is charged on: all direct and machine cost, bought-in material only, your own wages only, or your own value-add (direct and machine cost less material and bought-in work). Determines whether a supplier invoice attracts your overhead surcharge.

Example response

{
  "as_of": "2026-06-01",
  "calibrated": true,
  "currency": "EUR",
  "env_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "mode": "per_driver",
  "note": "string",
  "overhead_var_base": "direct"
}

Set an effective rate for one driver.

PUT /api/v1/environments/{env_id}/effective-rates

Set the rate a driver charges, in its own unit. Your number becomes the effective rate; future calibrations adjust around it. Takes effect on the next calculation.

Parameters

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

Request body (application/json)

Field Type Required Description
driver machine | labour | overhead_var | overhead_fix yes Which rate to set.
value number yes The effective rate to charge, in the driver's unit. The two overheads accept 0 (charge none); machine and labour must be above 0.

Request

curl -X PUT https://api.arcnm.io/api/v1/environments/{env_id}/effective-rates \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "driver": "machine",
    "value": 0
  }'
import requests

resp = requests.put(
    "https://api.arcnm.io/api/v1/environments/{env_id}/effective-rates",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "driver": "machine",
        "value": 0
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/effective-rates", {
  method: "PUT",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "driver": "machine",
    "value": 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
as_of string
calibrated boolean Whether this environment has been calibrated to your actuals.
currency string
env_id string
mode per_driver | overall
note string
overhead_var_base direct | material | labour | conversion The cost the variable-overhead percentage is charged on: all direct and machine cost, bought-in material only, your own wages only, or your own value-add (direct and machine cost less material and bought-in work). Determines whether a supplier invoice attracts your overhead surcharge.

Example response

{
  "as_of": "2026-06-01",
  "calibrated": true,
  "currency": "EUR",
  "env_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "mode": "per_driver",
  "note": "string",
  "overhead_var_base": "direct"
}

Get Environment Factors

GET /api/v1/environments/{env_id}/factors

Every number the cost sheet lets you change, resolved for this environment: what it is worth here, where that came from, the range a what-if may explore, which cost lines it moves, and the endpoint that saves it.

Values are read through the same resolvers a calculation uses, so a factor shown here is the factor that was charged.

Integration credentials receive your own statements; the platform's own defaults are shown in the app.

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/environments/{env_id}/factors \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.get(
    "https://api.arcnm.io/api/v1/environments/{env_id}/factors",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/factors", {
  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
config_revision integer
currency string Currency every money unit in 'rows' is denominated in.
environment_id string
rows FactorRowPublic[]
total integer

Example response

{
  "config_revision": 0,
  "currency": "EUR",
  "environment_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "rows": [
    {
      "drives": [
        "string"
      ],
      "edit_path": "string",
      "key": "string",
      "label_id": "string",
      "max": 0,
      "min": 0,
      "moves": "string",
      "platform_value": 0,
      "preview": "linear",
      "process": "string",
      "source": "your_override",
      "source_quality": "sourced",
      "unit": "string",
      "value": 0
    }
  ],
  "total": 0
}

Update Environment Identity

PUT /api/v1/environments/{env_id}/identity

Change a costing environment's name, description, region, currency, validity dates or parent environment. Its machines, tools and rates are changed with their own tools.

Parameters

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

Request body (application/json)

Field Type Required Description
currency string no New ISO 4217 currency code; omit to leave unchanged.
description string no New description for the environment; omit to leave unchanged.
name string no New name for the environment; omit to leave unchanged.
parent_environment_id string no Environment this one inherits from. Anything this environment does not state itself — a machine rate, a labour rate, a subcontract price, a physics override — resolves from the parent, and from ITS parent above that, so a region can be priced once and a quarter or a customer programme can restate only what differs. Send null to detach. Omit to leave unchanged.
region string no New region for the environment; omit to leave unchanged.
valid_from string no New effective-from ISO date (YYYY-MM-DD); omit to leave unchanged.
valid_to string no New effective-to ISO date (YYYY-MM-DD); omit to leave unchanged.

Request

curl -X PUT https://api.arcnm.io/api/v1/environments/{env_id}/identity \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "currency": "EUR",
    "description": "string",
    "name": "string",
    "parent_environment_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "region": "EU",
    "valid_from": "2026-06-01",
    "valid_to": "2026-06-01"
  }'
import requests

resp = requests.put(
    "https://api.arcnm.io/api/v1/environments/{env_id}/identity",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "currency": "EUR",
        "description": "string",
        "name": "string",
        "parent_environment_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "region": "EU",
        "valid_from": "2026-06-01",
        "valid_to": "2026-06-01"
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/identity", {
  method: "PUT",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "currency": "EUR",
    "description": "string",
    "name": "string",
    "parent_environment_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "region": "EU",
    "valid_from": "2026-06-01",
    "valid_to": "2026-06-01"
  }),
})
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
currency string ISO 4217 currency code the environment's rates are denominated in.
description string Optional longer description of the environment.
id string Unique identifier of the costing environment.
is_baseline boolean Whether this is the auto-provisioned default environment for the tenant.
machine_count integer Machines currently in this environment's fleet.
machines_cloned integer Number of machines copied into the environment; only set by the clone endpoints.
name string Human-readable name of the costing environment.
parent_environment_id string Environment this one inherits from, or null when it stands alone. Anything this environment does not state itself resolves from the parent and, recursively, from the parent's parent.
rate_count integer Rate rows this environment states itself (every kind, every validity window) — what GET /{env_id}/rates returns. Rates it only inherits from its parent are not counted.
rates_cloned integer Number of rate rows copied into the environment; only set by the clone endpoints.
region string Geographic region this environment prices for (e.g. DE, US).
scheduled_changes_dropped integer Memberships of the source whose validity window is not active today (scheduled future additions or removals). A clone is a snapshot as of today, so these do not travel; re-schedule them on the copy if you need them. Only set by the clone endpoint.
subcontractor_count integer Subcontractors this environment orders from.
tool_count integer Crib tools currently attached to this environment.
valid_from string ISO date (YYYY-MM-DD) from which this environment is effective.
valid_to string ISO date (YYYY-MM-DD) the environment stops being effective; null = open-ended.

Example response

{
  "currency": "EUR",
  "description": "string",
  "id": "string",
  "is_baseline": true,
  "machine_count": 0,
  "machines_cloned": 0,
  "name": "string",
  "parent_environment_id": "string",
  "rate_count": 0,
  "rates_cloned": 0,
  "region": "EU",
  "scheduled_changes_dropped": 0,
  "subcontractor_count": 0,
  "tool_count": 0,
  "valid_from": "string",
  "valid_to": "string"
}

Get Individualization

GET /api/v1/environments/{env_id}/individualization

What this environment states differently from the platform defaults, per lever — the honest zero-setup meter. Counts only; every line maps to a Setup panel and, on the default environment, to its restore.

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/environments/{env_id}/individualization \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.get(
    "https://api.arcnm.io/api/v1/environments/{env_id}/individualization",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/individualization", {
  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
assumptions AssumptionsCounts
calibration CalibrationCounts
individualized boolean Any lever differs from the platform set / platform defaults.
is_baseline boolean
machines LeverCounts
rates RatesCounts
subcontractors LeverCounts
tools LeverCounts

Example response

{
  "assumptions": {
    "overrides": 0,
    "tuning_changed": true
  },
  "calibration": {
    "is_calibrated": true,
    "observations": 0
  },
  "individualized": true,
  "is_baseline": true,
  "machines": {
    "added": 0,
    "custom": 0,
    "edited": 0,
    "hidden": 0,
    "off": 0,
    "removed": 0
  },
  "rates": {
    "stated": 0
  },
  "subcontractors": {
    "added": 0,
    "custom": 0,
    "edited": 0,
    "hidden": 0,
    "off": 0,
    "removed": 0
  },
  "tools": {
    "added": 0,
    "custom": 0,
    "edited": 0,
    "hidden": 0,
    "off": 0,
    "removed": 0
  }
}

List Env Machines

GET /api/v1/environments/{env_id}/machines

Machines wired to this env, ordered by fleet_priority (lower = earlier candidate). The same fleet feeds every pricing run for the environment. at answers "what does the fleet look like on date X" — the same active_as_of window pricing resolves on that date (rates already take ?at); it defaults to today.

Parameters

Name In Type Required Description
env_id path string yes Identifier of the env.
at query string no As-of date for the fleet window; defaults to today. Answers "what does the fleet look like on date X" — a scheduled addition appears once at reaches its valid_from, a scheduled removal disappears from its valid_to on.

Request

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

resp = requests.get(
    "https://api.arcnm.io/api/v1/environments/{env_id}/machines",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/machines", {
  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.

Attach Machine

POST /api/v1/environments/{env_id}/machines

Add a machine to a costing environment so calculations in it can plan on it: one of your machines, a machine-library entry, or a new machine defined inline, with optional rate and capability overrides.

Parameters

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

Request body (application/json)

Field Type Required Description
capability_overrides object no Sparse per-environment capability overrides merged over the library defaults.
fleet_priority integer no Orders machines within the environment; lower values are tried first.
hourly_rate_override_eur number no Optional flat machine-hour rate; defaults to the library entry's nominal rate.
is_enabled boolean no Whether the machine is active in the environment's fleet on attach.
library_entry_id string no Instantiate a machine from this shared/tenant library entry; mutually exclusive with machine_id / new_machine.
machine_id string no Identifier of an existing machine to attach; mutually exclusive with new_machine.
name_override string no Optional name for the instantiated machine; defaults to the library entry's name.
new_machine MachineDefinitionCreate no Inline definition of a new machine to create and attach; mutually exclusive with machine_id.
rate_operator_eur_per_h_override number no Operator wage already contained in hourly_rate_override_eur, in EUR per hour. Only meaningful with a rate override; without one the library entry's own declaration is inherited along with its rate. 0 means the rate is machine-only.
valid_from string no ISO date (YYYY-MM-DD) the membership starts; defaults to the machine's own valid_from.
valid_to string no ISO date (YYYY-MM-DD) the membership ends; defaults to the machine's own valid_to.

Request

curl -X POST https://api.arcnm.io/api/v1/environments/{env_id}/machines \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "capability_overrides": {},
    "fleet_priority": 100,
    "hourly_rate_override_eur": 0,
    "is_enabled": true,
    "library_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "machine_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "name_override": "string",
    "new_machine": {
      "burden_rate_eur": 0,
      "capabilities": {
        "axes_indexable": 0,
        "axes_simultaneous": 0,
        "bar_feeder": false,
        "certifications": [
          "string"
        ],
        "chatter_stability_lobe": {
          "rpm_to_max_axial_depth_mm": [
            {}
          ]
        },
        "coolant": [
          "flood"
        ],
        "iso_286_achievable_grade": "IT01",
        "klass": "milling.3axis_vmc",
        "laser_power_kw": 0,
        "max_material_thickness_mm": 0,
        "max_part_envelope_mm": [
          null
        ],
        "max_setups_per_part": 6,
        "max_spindle_rpm": 0,
        "max_table_load_kg": 0,
        "max_tool_diameter_mm": 0,
        "max_tool_length_mm": 0,
        "min_material_thickness_mm": 0,
        "nominal_tool_change_time_s_by_class": {},
        "pallet_change_time_s": 0,
        "plasma_amperage_a": 0,
        "positioning_accuracy_mm": 0.01,
        "press_force_kn": 0,
        "rapid_traverse_m_per_min": 24,
        "repeatability_mm": 0.005,
        "schema_version": "1.0.0",
        "spindle_power_kw": 0,
        "subclass": "small",
        "tool_interface": "string",
        "tool_magazine_capacity": 0,
        "turret_stations": 0,
        "vdi_3258": {
          "acquisition_cost_eur": 0,
          "annual_hours_T_G": 0,
          "annual_hours_T_IH": 0,
          "annual_hours_T_ST": 0,
          "capital_interest_rate": 0,
          "depreciation_life_h": 0,
          "energy_eur_per_kwh": 0,
          "energy_kw": 0,
          "floor_space_m2": 0,
          "maintenance_eur_per_year": 0,
          "operator_hourly_eur": 0,
          "operator_share": 0,
          "replacement_value_eur": 0,
          "shift_model": {
            "availability": 1,
            "days_per_week": 0,
            "hours_per_shift": 0,
            "shifts_per_day": 0,
            "weeks_per_year": 47
          },
          "space_eur_per_m2_y": 0,
          "tooling_eur_per_year": 0
        },
        "workholding": [
          "vise.3jaw"
        ]
      },
      "hourly_rate_eur": 0,
      "klass": "milling.3axis_vmc",
      "library_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "model_no": "string",
      "name": "string",
      "programming_rate_eur": 0,
      "rate_operator_eur_per_h": 0,
      "rate_operator_share": 0,
      "setup_rate_eur": 0,
      "subclass": "small",
      "valid_from": "2026-06-01",
      "valid_to": "2026-06-01",
      "vendor": "string"
    },
    "rate_operator_eur_per_h_override": 0,
    "valid_from": "2026-06-01",
    "valid_to": "2026-06-01"
  }'
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/environments/{env_id}/machines",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "capability_overrides": {},
        "fleet_priority": 100,
        "hourly_rate_override_eur": 0,
        "is_enabled": True,
        "library_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "machine_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "name_override": "string",
        "new_machine": {
            "burden_rate_eur": 0,
            "capabilities": {
                "axes_indexable": 0,
                "axes_simultaneous": 0,
                "bar_feeder": False,
                "certifications": [
                    "string"
                ],
                "chatter_stability_lobe": {
                    "rpm_to_max_axial_depth_mm": [
                        {}
                    ]
                },
                "coolant": [
                    "flood"
                ],
                "iso_286_achievable_grade": "IT01",
                "klass": "milling.3axis_vmc",
                "laser_power_kw": 0,
                "max_material_thickness_mm": 0,
                "max_part_envelope_mm": [
                    None
                ],
                "max_setups_per_part": 6,
                "max_spindle_rpm": 0,
                "max_table_load_kg": 0,
                "max_tool_diameter_mm": 0,
                "max_tool_length_mm": 0,
                "min_material_thickness_mm": 0,
                "nominal_tool_change_time_s_by_class": {},
                "pallet_change_time_s": 0,
                "plasma_amperage_a": 0,
                "positioning_accuracy_mm": 0.01,
                "press_force_kn": 0,
                "rapid_traverse_m_per_min": 24,
                "repeatability_mm": 0.005,
                "schema_version": "1.0.0",
                "spindle_power_kw": 0,
                "subclass": "small",
                "tool_interface": "string",
                "tool_magazine_capacity": 0,
                "turret_stations": 0,
                "vdi_3258": {
                    "acquisition_cost_eur": 0,
                    "annual_hours_T_G": 0,
                    "annual_hours_T_IH": 0,
                    "annual_hours_T_ST": 0,
                    "capital_interest_rate": 0,
                    "depreciation_life_h": 0,
                    "energy_eur_per_kwh": 0,
                    "energy_kw": 0,
                    "floor_space_m2": 0,
                    "maintenance_eur_per_year": 0,
                    "operator_hourly_eur": 0,
                    "operator_share": 0,
                    "replacement_value_eur": 0,
                    "shift_model": {
                        "availability": 1,
                        "days_per_week": 0,
                        "hours_per_shift": 0,
                        "shifts_per_day": 0,
                        "weeks_per_year": 47
                    },
                    "space_eur_per_m2_y": 0,
                    "tooling_eur_per_year": 0
                },
                "workholding": [
                    "vise.3jaw"
                ]
            },
            "hourly_rate_eur": 0,
            "klass": "milling.3axis_vmc",
            "library_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
            "model_no": "string",
            "name": "string",
            "programming_rate_eur": 0,
            "rate_operator_eur_per_h": 0,
            "rate_operator_share": 0,
            "setup_rate_eur": 0,
            "subclass": "small",
            "valid_from": "2026-06-01",
            "valid_to": "2026-06-01",
            "vendor": "string"
        },
        "rate_operator_eur_per_h_override": 0,
        "valid_from": "2026-06-01",
        "valid_to": "2026-06-01"
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/machines", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "capability_overrides": {},
    "fleet_priority": 100,
    "hourly_rate_override_eur": 0,
    "is_enabled": true,
    "library_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "machine_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "name_override": "string",
    "new_machine": {
      "burden_rate_eur": 0,
      "capabilities": {
        "axes_indexable": 0,
        "axes_simultaneous": 0,
        "bar_feeder": false,
        "certifications": [
          "string"
        ],
        "chatter_stability_lobe": {
          "rpm_to_max_axial_depth_mm": [
            {}
          ]
        },
        "coolant": [
          "flood"
        ],
        "iso_286_achievable_grade": "IT01",
        "klass": "milling.3axis_vmc",
        "laser_power_kw": 0,
        "max_material_thickness_mm": 0,
        "max_part_envelope_mm": [
          null
        ],
        "max_setups_per_part": 6,
        "max_spindle_rpm": 0,
        "max_table_load_kg": 0,
        "max_tool_diameter_mm": 0,
        "max_tool_length_mm": 0,
        "min_material_thickness_mm": 0,
        "nominal_tool_change_time_s_by_class": {},
        "pallet_change_time_s": 0,
        "plasma_amperage_a": 0,
        "positioning_accuracy_mm": 0.01,
        "press_force_kn": 0,
        "rapid_traverse_m_per_min": 24,
        "repeatability_mm": 0.005,
        "schema_version": "1.0.0",
        "spindle_power_kw": 0,
        "subclass": "small",
        "tool_interface": "string",
        "tool_magazine_capacity": 0,
        "turret_stations": 0,
        "vdi_3258": {
          "acquisition_cost_eur": 0,
          "annual_hours_T_G": 0,
          "annual_hours_T_IH": 0,
          "annual_hours_T_ST": 0,
          "capital_interest_rate": 0,
          "depreciation_life_h": 0,
          "energy_eur_per_kwh": 0,
          "energy_kw": 0,
          "floor_space_m2": 0,
          "maintenance_eur_per_year": 0,
          "operator_hourly_eur": 0,
          "operator_share": 0,
          "replacement_value_eur": 0,
          "shift_model": {
            "availability": 1,
            "days_per_week": 0,
            "hours_per_shift": 0,
            "shifts_per_day": 0,
            "weeks_per_year": 47
          },
          "space_eur_per_m2_y": 0,
          "tooling_eur_per_year": 0
        },
        "workholding": [
          "vise.3jaw"
        ]
      },
      "hourly_rate_eur": 0,
      "klass": "milling.3axis_vmc",
      "library_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "model_no": "string",
      "name": "string",
      "programming_rate_eur": 0,
      "rate_operator_eur_per_h": 0,
      "rate_operator_share": 0,
      "setup_rate_eur": 0,
      "subclass": "small",
      "valid_from": "2026-06-01",
      "valid_to": "2026-06-01",
      "vendor": "string"
    },
    "rate_operator_eur_per_h_override": 0,
    "valid_from": "2026-06-01",
    "valid_to": "2026-06-01"
  }),
})
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.
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 201

Field Type Description
env_count integer How many of your environments this machine is currently in. Editing the machine changes what every one of them prices with, so a save button can say how far the edit reaches before it is pressed.
fleet_priority integer Orders machines within the environment; lower values are tried first.
health string Whether this machine can be used for pricing. 'ok' — it takes part in selection. 'unresolvable' — its capability configuration is incomplete, so it is NOT considered by any calculation, even though it is enabled. Render an unresolvable machine as broken; it used to render as a normal row and silently do nothing.
health_error string Why the machine cannot be evaluated; null when health is 'ok'.
is_enabled boolean Whether this machine is active in the environment's fleet.
machine MachinePublic The attached machine definition; null only when it cannot be re-read.
membership_id string Unique identifier of the environment-machine membership.
tool_kinds_fit string[] The tool kinds among them, for the panel's summary.
tools_fit_count integer How many of this environment's enabled tools this machine can run (kind-based fitment; a tool is never allocated to one machine).
valid_from string ISO date (YYYY-MM-DD) from which this membership is effective.
valid_to string ISO date (YYYY-MM-DD) the membership stops being effective; null = open-ended.

Example response

{
  "env_count": 1,
  "fleet_priority": 0,
  "health": "ok",
  "health_error": "string",
  "is_enabled": true,
  "machine": {
    "burden_rate_eur": 0,
    "capabilities": {},
    "environment_count": 0,
    "hourly_rate_eur": 0,
    "id": "string",
    "klass": "string",
    "model_no": "string",
    "name": "string",
    "programming_rate_eur": 0,
    "rate_operator_eur_per_h": 0,
    "rate_operator_share": 0,
    "setup_rate_eur": 0,
    "source": "custom",
    "subclass": "string",
    "valid_from": "string",
    "valid_to": "string",
    "vendor": "string"
  },
  "membership_id": "string",
  "tool_kinds_fit": [
    "string"
  ],
  "tools_fit_count": 0,
  "valid_from": "string",
  "valid_to": "string"
}

Detach Machine

DELETE /api/v1/environments/{env_id}/machines/{membership_id}

Remove a machine from a costing environment, so calculations run afterwards no longer plan on it. The machine definition itself is kept.

Parameters

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

Request

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

resp = requests.delete(
    "https://api.arcnm.io/api/v1/environments/{env_id}/machines/{membership_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/environments/{env_id}/machines/{membership_id}", {
  method: "DELETE",
  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
message string Human-readable result of the operation.

Example response

{
  "message": "string"
}

Update Membership

PATCH /api/v1/environments/{env_id}/machines/{membership_id}

Change how a machine is used in one costing environment: its priority among machines that can do the same work, whether it is enabled, and its validity dates.

Parameters

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

Request body (application/json)

Field Type Required Description
fleet_priority integer no New ordering within the environment (lower tried first); omit to leave unchanged.
is_enabled boolean no Whether the machine is active in the fleet; omit to leave unchanged.
valid_from string no New membership start ISO date (YYYY-MM-DD); omit to leave unchanged.
valid_to string no New membership end ISO date (YYYY-MM-DD); omit to leave unchanged.

Request

curl -X PATCH https://api.arcnm.io/api/v1/environments/{env_id}/machines/{membership_id} \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "fleet_priority": 0,
    "is_enabled": true,
    "valid_from": "2026-06-01",
    "valid_to": "2026-06-01"
  }'
import requests

resp = requests.patch(
    "https://api.arcnm.io/api/v1/environments/{env_id}/machines/{membership_id}",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "fleet_priority": 0,
        "is_enabled": True,
        "valid_from": "2026-06-01",
        "valid_to": "2026-06-01"
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/machines/{membership_id}", {
  method: "PATCH",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "fleet_priority": 0,
    "is_enabled": true,
    "valid_from": "2026-06-01",
    "valid_to": "2026-06-01"
  }),
})
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
env_count integer How many of your environments this machine is currently in. Editing the machine changes what every one of them prices with, so a save button can say how far the edit reaches before it is pressed.
fleet_priority integer Orders machines within the environment; lower values are tried first.
health string Whether this machine can be used for pricing. 'ok' — it takes part in selection. 'unresolvable' — its capability configuration is incomplete, so it is NOT considered by any calculation, even though it is enabled. Render an unresolvable machine as broken; it used to render as a normal row and silently do nothing.
health_error string Why the machine cannot be evaluated; null when health is 'ok'.
is_enabled boolean Whether this machine is active in the environment's fleet.
machine MachinePublic The attached machine definition; null only when it cannot be re-read.
membership_id string Unique identifier of the environment-machine membership.
tool_kinds_fit string[] The tool kinds among them, for the panel's summary.
tools_fit_count integer How many of this environment's enabled tools this machine can run (kind-based fitment; a tool is never allocated to one machine).
valid_from string ISO date (YYYY-MM-DD) from which this membership is effective.
valid_to string ISO date (YYYY-MM-DD) the membership stops being effective; null = open-ended.

Example response

{
  "env_count": 1,
  "fleet_priority": 0,
  "health": "ok",
  "health_error": "string",
  "is_enabled": true,
  "machine": {
    "burden_rate_eur": 0,
    "capabilities": {},
    "environment_count": 0,
    "hourly_rate_eur": 0,
    "id": "string",
    "klass": "string",
    "model_no": "string",
    "name": "string",
    "programming_rate_eur": 0,
    "rate_operator_eur_per_h": 0,
    "rate_operator_share": 0,
    "setup_rate_eur": 0,
    "source": "custom",
    "subclass": "string",
    "valid_from": "string",
    "valid_to": "string",
    "vendor": "string"
  },
  "membership_id": "string",
  "tool_kinds_fit": [
    "string"
  ],
  "tools_fit_count": 0,
  "valid_from": "string",
  "valid_to": "string"
}

Restore Env Machines

POST /api/v1/environments/{env_id}/machines/restore

Restore the platform machine set in the default environment: removed machines are re-attached (new SCD2 rows), switched-off ones switch on, platform classes the environment never had are added. Capability overrides and machine rates are untouched. 409 on any other environment.

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/environments/{env_id}/machines/restore \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/environments/{env_id}/machines/restore",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/machines/restore", {
  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
added integer Platform machine classes added that the environment never had.
enabled integer Switched-off memberships switched back on.
message string Always 'restored'.
reattached integer Removed machines re-attached (new SCD2 rows).

Example response

{
  "added": 0,
  "enabled": 0,
  "message": "string",
  "reattached": 0
}

List Rates

GET /api/v1/environments/{env_id}/rates

List all rates for an env, uniformly shaped so the rate editor can render every kind side by side. Optional ?kind= filter.

Parameters

Name In Type Required Description
env_id path string yes Identifier of the env.
kind query string no Filter to a single rate kind (labour, overhead, material, fx, subcontract, or scrap).

Request

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

resp = requests.get(
    "https://api.arcnm.io/api/v1/environments/{env_id}/rates",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/rates", {
  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.

Add a price without replacing one you already hold.

POST /api/v1/environments/{env_id}/rates

Adds a rate and leaves every price you already hold open, so you can keep more than one on file — a second subcontractor's quote for the same operation, for instance. Which of them a part is costed at is decided per order.

This one adds. To state the single price this environment should use — closing the price it replaces — use the PUT for the thing itself: .../rates/subcontract/{operation_kind} for bought-in work, .../rates/material/{grade_id} for a material. A scrap price has only its own PUT .../rates/scrap/{material_category}/{scrap_class}.

Parameters

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

Request body (application/json)

Field Type Required Description
currency string no ISO 4217 currency code for the rate; null to inherit.
fields object no Kind-specific columns (category, machine_ref, …).
kind string yes labour
valid_from string yes Date from which the rate is effective (ISO 8601).
valid_to string no Date after which the rate is no longer effective; null if open-ended (ISO 8601).

Request

curl -X POST https://api.arcnm.io/api/v1/environments/{env_id}/rates \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "string",
    "valid_from": "2026-06-01"
  }'
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/environments/{env_id}/rates",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "kind": "string",
        "valid_from": "2026-06-01"
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/rates", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "kind": "string",
    "valid_from": "2026-06-01"
  }),
})
const data = await resp.json()

Responses

Status Description
201 Successful Response
422 Validation Error — or a refused rate, among them scrap_price_use_scrap_route for kind scrap.

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 201

Field Type Description
created_at string ISO 8601 timestamp when the rate row was created.
currency string ISO 4217 currency code for the rate; null for kinds without a currency.
effective_now boolean True when this row is the one currently pricing parts: its validity window contains today and no other row for the same key supersedes it.
engine_honoured boolean True when this row can be applied as written; false rows are listed but never priced (see unhonoured_reason).
env_id string Identifier of the environment this rate belongs to.
fields object Kind-specific rate columns (e.g. hourly_rate, value, price_per_kg, rate).
id string Unique identifier of the rate row.
kind string Rate kind: labour, overhead, material, fx, subcontract, or scrap.
unhonoured_reason string Why the row is never applied, when engine_honoured is false: 'currency_mismatch' (row currency differs from the environment's; fx rows are exempt because they carry their own currency pair), 'pricing_unit_mismatch' (the row's pricing_unit is not the one this operation is quoted in — re-enter the price in the stated unit), 'bucket_combo_unhonoured' (this bucket/kind/base combination is not priceable), 'value_not_positive' (the row's amount is zero or negative and is skipped), 'subcontractor_no_capability' (the subcontractor this price is from does not list this operation — add it to that subcontractor, or enter the price under the one that does) or 'subcontractor_disabled' (that subcontractor is inactive, or is switched off in this environment). Null otherwise. New reasons may be added; treat an unrecognised value as a plain 'not applied'.
valid_from string ISO date (YYYY-MM-DD) from which this rate is effective.
valid_to string ISO date (YYYY-MM-DD) the rate stops being effective; null = open-ended.

Example response

{
  "created_at": "string",
  "currency": "EUR",
  "effective_now": false,
  "engine_honoured": true,
  "env_id": "string",
  "fields": {},
  "id": "string",
  "kind": "string",
  "unhonoured_reason": "string",
  "valid_from": "string",
  "valid_to": "string"
}

Delete Rate

DELETE /api/v1/environments/{env_id}/rates/{kind}/{rate_id}

Delete one rate from a costing environment: a labour, overhead, material, FX or subcontract rate. This cannot be undone.

Parameters

Name In Type Required Description
env_id path string yes Identifier of the env.
kind path string yes Rate kind to delete (labour, overhead, material, fx, or subcontract; a scrap price is cleared on its own path).
rate_id path string yes Identifier of the rate.

Request

curl -X DELETE https://api.arcnm.io/api/v1/environments/{env_id}/rates/{kind}/{rate_id} \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.delete(
    "https://api.arcnm.io/api/v1/environments/{env_id}/rates/{kind}/{rate_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/environments/{env_id}/rates/{kind}/{rate_id}", {
  method: "DELETE",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
  },
})
const data = await resp.json()

Responses

Status Description
200 Successful Response
422 Validation Error — or scrap_price_use_scrap_route: a scrap price is cleared on its own path.

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
message string Human-readable result of the operation.

Example response

{
  "message": "string"
}

Drop your price for a material in this environment.

DELETE /api/v1/environments/{env_id}/rates/material/{grade_id}

Removes the environment's own price rows for this material, so it prices at the platform figure for its category again. Takes effect immediately.

Parameters

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

Request

curl -X DELETE https://api.arcnm.io/api/v1/environments/{env_id}/rates/material/{grade_id} \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.delete(
    "https://api.arcnm.io/api/v1/environments/{env_id}/rates/material/{grade_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/environments/{env_id}/rates/material/{grade_id}", {
  method: "DELETE",
  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
message string Human-readable result of the operation.

Example response

{
  "message": "string"
}

Set what a material costs in this environment.

PUT /api/v1/environments/{env_id}/rates/material/{grade_id}

Your number becomes the price per kilogram this environment calculates with, from valid_from onwards. The price it replaces is closed the same day rather than deleted, so past quotes keep the figure they were costed at.

Parameters

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

Request body (application/json)

Field Type Required Description
price_per_kg number yes Price per kilogram, in the environment's currency. Takes effect on the next calculation.
valid_from string no Day the price starts applying. Defaults to today. The price you replace is closed on the same day, so the two never overlap.

Request

curl -X PUT https://api.arcnm.io/api/v1/environments/{env_id}/rates/material/{grade_id} \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "price_per_kg": 0
  }'
import requests

resp = requests.put(
    "https://api.arcnm.io/api/v1/environments/{env_id}/rates/material/{grade_id}",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "price_per_kg": 0
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/rates/material/{grade_id}", {
  method: "PUT",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "price_per_kg": 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
created_at string ISO 8601 timestamp when the rate row was created.
currency string ISO 4217 currency code for the rate; null for kinds without a currency.
effective_now boolean True when this row is the one currently pricing parts: its validity window contains today and no other row for the same key supersedes it.
engine_honoured boolean True when this row can be applied as written; false rows are listed but never priced (see unhonoured_reason).
env_id string Identifier of the environment this rate belongs to.
fields object Kind-specific rate columns (e.g. hourly_rate, value, price_per_kg, rate).
id string Unique identifier of the rate row.
kind string Rate kind: labour, overhead, material, fx, subcontract, or scrap.
unhonoured_reason string Why the row is never applied, when engine_honoured is false: 'currency_mismatch' (row currency differs from the environment's; fx rows are exempt because they carry their own currency pair), 'pricing_unit_mismatch' (the row's pricing_unit is not the one this operation is quoted in — re-enter the price in the stated unit), 'bucket_combo_unhonoured' (this bucket/kind/base combination is not priceable), 'value_not_positive' (the row's amount is zero or negative and is skipped), 'subcontractor_no_capability' (the subcontractor this price is from does not list this operation — add it to that subcontractor, or enter the price under the one that does) or 'subcontractor_disabled' (that subcontractor is inactive, or is switched off in this environment). Null otherwise. New reasons may be added; treat an unrecognised value as a plain 'not applied'.
valid_from string ISO date (YYYY-MM-DD) from which this rate is effective.
valid_to string ISO date (YYYY-MM-DD) the rate stops being effective; null = open-ended.

Example response

{
  "created_at": "string",
  "currency": "EUR",
  "effective_now": false,
  "engine_honoured": true,
  "env_id": "string",
  "fields": {},
  "id": "string",
  "kind": "string",
  "unhonoured_reason": "string",
  "valid_from": "string",
  "valid_to": "string"
}

Restore Env Rates

POST /api/v1/environments/{env_id}/rates/restore

Drops every rate this environment states, so it prices at the platform defaults again with honest "platform default" provenance. A scrap price that applied on an earlier day stays on record, ending yesterday, so quotes already made keep it. Your assumptions, tuning and calibration are untouched. Takes effect immediately. Only the default environment can be restored.

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/environments/{env_id}/rates/restore \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/environments/{env_id}/rates/restore",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/rates/restore", {
  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
closed integer Stated rate rows removed, so the platform defaults price again. Takes effect immediately.
message string Always 'restored'.

Example response

{
  "closed": 0,
  "message": "string"
}

Read the scrap prices this environment uses today.

GET /api/v1/environments/{env_id}/rates/scrap

Every scrap price a calculation in this environment would use today, one row per material, grade and kind of scrap, with whose price it is — this environment's, a parent environment's or the platform's published reference figure — and whether a platform figure is stale. credit_scrap_stated says whether any of them changes a quote yet.

Integration credentials receive your own prices; the platform's reference figures are shown in the app.

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/environments/{env_id}/rates/scrap \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

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

Responses

Status Description
200 Successful Response
404 No environment with this id is visible to your organization.
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
credit_scrap_stated boolean True when this environment, or a parent it inherits from, credits sold scrap at these prices. Until it does, none of these prices changes a quote; an environment that states only a flat return per kg is credited at that.
rows ScrapPriceResolvedPublic[]

Example response

{
  "credit_scrap_stated": true,
  "rows": [
    {
      "age_days": 0,
      "as_of": "string",
      "currency": "EUR",
      "grade_label": "string",
      "material_category": "string",
      "material_grade_id": "string",
      "price_per_kg": 0,
      "price_source": "string",
      "reference": "string",
      "scrap_class": "string",
      "source_environment_id": "string",
      "source_environment_name": "string",
      "stale": true,
      "stated_grade_id": "string",
      "valid_from": "string",
      "valid_to": "string"
    }
  ]
}

Stop using your price for one kind of scrap.

DELETE /api/v1/environments/{env_id}/rates/scrap/{material_category}/{scrap_class}

The environment's own price for this material and kind of scrap stops applying today. One that started on an earlier day stays on record, so quotes already made keep it; one set today, or for a later day, is withdrawn. Calculations then use the price a parent environment states, or the platform's reference figure.

Parameters

Name In Type Required Description
env_id path string yes Identifier of the env.
material_category path carbon_steel | alloy_steel | stainless_steel | tool_steel | cast_iron | aluminium | copper_alloy | nickel_alloy | titanium_alloy | magnesium_alloy | zinc_alloy | thermoplastic | thermoset | elastomer | composite | other yes
scrap_class path sheet_skeleton | chips | drop yes
grade_id query string no The grade the price is for. Leave it out to price every grade of the category that states no price of its own.

Request

curl -X DELETE https://api.arcnm.io/api/v1/environments/{env_id}/rates/scrap/{material_category}/{scrap_class} \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.delete(
    "https://api.arcnm.io/api/v1/environments/{env_id}/rates/scrap/{material_category}/{scrap_class}",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/rates/scrap/{material_category}/{scrap_class}", {
  method: "DELETE",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
  },
})
const data = await resp.json()

Responses

Status Description
200 Successful Response
404 No environment with this id is visible to your organization, or it states no price for this material and kind of scrap (scrap_price_not_found).
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
message string Human-readable result of the operation.

Example response

{
  "message": "string"
}

Set what your scrap dealer pays for one kind of scrap.

PUT /api/v1/environments/{env_id}/rates/scrap/{material_category}/{scrap_class}

Your price per kilogram for the scrap parts of this material leave behind — sheet skeletons and offcuts, chips, or bar ends and cut-offs — from valid_from onwards. Name a grade with grade_id to price it apart from the rest of its category. A price it replaces that started on an earlier day stops the day before rather than being deleted, so quotes already made keep the figure they were costed at; one set earlier the same day is simply replaced.

A scrap price is credited only in an environment that credits sold scrap: the shop-practice question scrap_credit.

Parameters

Name In Type Required Description
env_id path string yes Identifier of the env.
material_category path carbon_steel | alloy_steel | stainless_steel | tool_steel | cast_iron | aluminium | copper_alloy | nickel_alloy | titanium_alloy | magnesium_alloy | zinc_alloy | thermoplastic | thermoset | elastomer | composite | other yes
scrap_class path sheet_skeleton | chips | drop yes
grade_id query string no The grade the price is for. Leave it out to price every grade of the category that states no price of its own.

Request body (application/json)

Field Type Required Description
price_per_kg number yes What you get back per kilogram, in the environment's currency: what the dealer pays less what sorting, containers and haulage cost you. Zero is allowed: a shop that sells nothing back states 0.
valid_from string no Day the price starts applying: today, which is the default, or later — never earlier, so quotes already made keep the price they were costed at. The price it replaces stops the day before.

Request

curl -X PUT https://api.arcnm.io/api/v1/environments/{env_id}/rates/scrap/{material_category}/{scrap_class} \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "price_per_kg": 0
  }'
import requests

resp = requests.put(
    "https://api.arcnm.io/api/v1/environments/{env_id}/rates/scrap/{material_category}/{scrap_class}",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "price_per_kg": 0
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/rates/scrap/{material_category}/{scrap_class}", {
  method: "PUT",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "price_per_kg": 0
  }),
})
const data = await resp.json()

Responses

Status Description
200 Successful Response
404 No environment or material grade with this id is visible to your organization.
422 Validation Error — or a refused price: scrap_price_backdated (valid_from lies before today) or scrap_price_grade_category_mismatch (the grade is not of this material category).

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
created_at string ISO 8601 timestamp when the rate row was created.
currency string ISO 4217 currency code for the rate; null for kinds without a currency.
effective_now boolean True when this row is the one currently pricing parts: its validity window contains today and no other row for the same key supersedes it.
engine_honoured boolean True when this row can be applied as written; false rows are listed but never priced (see unhonoured_reason).
env_id string Identifier of the environment this rate belongs to.
fields object Kind-specific rate columns (e.g. hourly_rate, value, price_per_kg, rate).
id string Unique identifier of the rate row.
kind string Rate kind: labour, overhead, material, fx, subcontract, or scrap.
unhonoured_reason string Why the row is never applied, when engine_honoured is false: 'currency_mismatch' (row currency differs from the environment's; fx rows are exempt because they carry their own currency pair), 'pricing_unit_mismatch' (the row's pricing_unit is not the one this operation is quoted in — re-enter the price in the stated unit), 'bucket_combo_unhonoured' (this bucket/kind/base combination is not priceable), 'value_not_positive' (the row's amount is zero or negative and is skipped), 'subcontractor_no_capability' (the subcontractor this price is from does not list this operation — add it to that subcontractor, or enter the price under the one that does) or 'subcontractor_disabled' (that subcontractor is inactive, or is switched off in this environment). Null otherwise. New reasons may be added; treat an unrecognised value as a plain 'not applied'.
valid_from string ISO date (YYYY-MM-DD) from which this rate is effective.
valid_to string ISO date (YYYY-MM-DD) the rate stops being effective; null = open-ended.

Example response

{
  "created_at": "string",
  "currency": "EUR",
  "effective_now": false,
  "engine_honoured": true,
  "env_id": "string",
  "fields": {},
  "id": "string",
  "kind": "string",
  "unhonoured_reason": "string",
  "valid_from": "string",
  "valid_to": "string"
}

Set what an outside operation costs in this environment.

PUT /api/v1/environments/{env_id}/rates/subcontract/{operation_kind}

Your number becomes the price this environment uses for this operation, from valid_from onwards. Prices you were holding open for the same operation are closed the same day rather than deleted, so quotes you have already sent keep the figure they were costed at.

This one replaces: afterwards there is a single open price for the operation. To put a second subcontractor's quote on file alongside the one you already hold, use POST /environments/{env_id}/rates, which adds a price and closes nothing.

Parameters

Name In Type Required Description
env_id path string yes Identifier of the env.
operation_kind path string yes The operation this price is for — heat_treatment:<kind>, coating:<kind>, marking:<kind> or weld.

Request body (application/json)

Field Type Required Description
lot_minimum_eur number no Minimum order value, in the environment's currency: the shop invoices one order at max(its work, this). The difference is the small-lot surcharge, spread over the lot — it dominates small batches and is zero once the lot's own work is worth the minimum.
order_fee_eur number no Fee the shop charges on every order regardless of its value, in the environment's currency, on top of the work or the minimum. Spread over the lot. Zero for a shop that charges none.
pricing_unit string yes The cost basis your price is quoted per: kg, m2 or part for any heat-treatment, coating or marking operation (the part's mass, surface area or one piece is what the rate multiplies), m for a weld (metres of deposited seam). Each shop states its own; the answer names the accepted bases and stores nothing otherwise.
subcontractor_id string no Which of your subcontractors this price is from. Optional — leave it out for a figure you carry without naming a shop.
unit_rate_eur number yes What the subcontractor charges per pricing unit, in the environment's currency. Zero is allowed — a shop that charges a minimum and nothing per unit.
valid_from string no Day the price starts applying. Defaults to today. The price you replace is closed on the same day, so the two never overlap.

Request

curl -X PUT https://api.arcnm.io/api/v1/environments/{env_id}/rates/subcontract/{operation_kind} \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "unit_rate_eur": 0,
    "pricing_unit": "string"
  }'
import requests

resp = requests.put(
    "https://api.arcnm.io/api/v1/environments/{env_id}/rates/subcontract/{operation_kind}",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "unit_rate_eur": 0,
        "pricing_unit": "string"
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/rates/subcontract/{operation_kind}", {
  method: "PUT",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "unit_rate_eur": 0,
    "pricing_unit": "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
created_at string ISO 8601 timestamp when the rate row was created.
currency string ISO 4217 currency code for the rate; null for kinds without a currency.
effective_now boolean True when this row is the one currently pricing parts: its validity window contains today and no other row for the same key supersedes it.
engine_honoured boolean True when this row can be applied as written; false rows are listed but never priced (see unhonoured_reason).
env_id string Identifier of the environment this rate belongs to.
fields object Kind-specific rate columns (e.g. hourly_rate, value, price_per_kg, rate).
id string Unique identifier of the rate row.
kind string Rate kind: labour, overhead, material, fx, subcontract, or scrap.
unhonoured_reason string Why the row is never applied, when engine_honoured is false: 'currency_mismatch' (row currency differs from the environment's; fx rows are exempt because they carry their own currency pair), 'pricing_unit_mismatch' (the row's pricing_unit is not the one this operation is quoted in — re-enter the price in the stated unit), 'bucket_combo_unhonoured' (this bucket/kind/base combination is not priceable), 'value_not_positive' (the row's amount is zero or negative and is skipped), 'subcontractor_no_capability' (the subcontractor this price is from does not list this operation — add it to that subcontractor, or enter the price under the one that does) or 'subcontractor_disabled' (that subcontractor is inactive, or is switched off in this environment). Null otherwise. New reasons may be added; treat an unrecognised value as a plain 'not applied'.
valid_from string ISO date (YYYY-MM-DD) from which this rate is effective.
valid_to string ISO date (YYYY-MM-DD) the rate stops being effective; null = open-ended.

Example response

{
  "created_at": "string",
  "currency": "EUR",
  "effective_now": false,
  "engine_honoured": true,
  "env_id": "string",
  "fields": {},
  "id": "string",
  "kind": "string",
  "unhonoured_reason": "string",
  "valid_from": "string",
  "valid_to": "string"
}

Read the environment's shop-practice profile.

GET /api/v1/environments/{env_id}/shop-practice

How this environment answers the shop-practice questions — packaging, deburring, and whether sold scrap and reusable remnants (sheet remnants, bar and tube ends) are credited against the material bought. Each answer says where it comes from: stated here, inherited from a parent environment, or the platform default.

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/environments/{env_id}/shop-practice \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.get(
    "https://api.arcnm.io/api/v1/environments/{env_id}/shop-practice",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/shop-practice", {
  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
questions object Per-question state: effective answer, source, and options.

Example response

{
  "questions": {}
}

Update the environment's shop-practice profile.

PUT /api/v1/environments/{env_id}/shop-practice

Answer the shop-practice questions (packaging, deburring, scrap credit, remnant credit, and how sheet is cut: grain direction, mirroring, the web by thickness, micro-joints, common-line cutting, the job nest's rotation step). Each answer sets this environment's time allowances, credit policy or cutting practice from a closed set of choices and is recorded in the audit log; a question you leave out keeps its answer. 'standard' removes this environment's own answer: where a parent environment states one, that answer applies and is returned with source 'parent_environment'; otherwise the platform behaviour does.

Parameters

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

Request body (application/json)

Field Type Required Description
answers object yes Question key → answer. Only catalogued questions and answers are accepted. 'standard' removes this environment's own answer: where a parent environment states one, that answer applies (source 'parent_environment'); otherwise the platform behaviour does.

Request

curl -X PUT https://api.arcnm.io/api/v1/environments/{env_id}/shop-practice \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "answers": {}
  }'
import requests

resp = requests.put(
    "https://api.arcnm.io/api/v1/environments/{env_id}/shop-practice",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "answers": {}
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/shop-practice", {
  method: "PUT",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "answers": {}
  }),
})
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
questions object Per-question state: effective answer, source, and options.

Example response

{
  "questions": {}
}

List Env Subcontractors

GET /api/v1/environments/{env_id}/subcontractors

The subcontractors THIS environment orders from (active memberships). An external operation priced here that no member offers carries a subcontract_unsourced note on the result — a note, never a veto.

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/environments/{env_id}/subcontractors \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.get(
    "https://api.arcnm.io/api/v1/environments/{env_id}/subcontractors",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/subcontractors", {
  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.

Attach Env Subcontractor

POST /api/v1/environments/{env_id}/subcontractors

Attach one of your subcontractors to this environment.

Parameters

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

Request body (application/json)

Field Type Required Description
is_enabled boolean no
subcontractor_id string yes

Request

curl -X POST https://api.arcnm.io/api/v1/environments/{env_id}/subcontractors \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "subcontractor_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  }'
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/environments/{env_id}/subcontractors",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "subcontractor_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/subcontractors", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "subcontractor_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  }),
})
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.
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 201

Field Type Description
is_active boolean
is_enabled boolean
lead_time_days integer
membership_id string
name string
operation_kinds string[] External operations this member offers (capability slugs).
preset_key string Platform preset this shop was installed from (heat_treatment_shop, electroplating_shop, …); null = the org's own subcontractor.
region string
subcontractor_id string
valid_from string

Example response

{
  "is_active": true,
  "is_enabled": true,
  "lead_time_days": 0,
  "membership_id": "string",
  "name": "string",
  "operation_kinds": [
    "string"
  ],
  "preset_key": "string",
  "region": "EU",
  "subcontractor_id": "string",
  "valid_from": "string"
}

Detach Env Subcontractor

DELETE /api/v1/environments/{env_id}/subcontractors/{membership_id}

Detach a subcontractor (SCD2: the membership closes its window; a same-day attach+detach hard-deletes the zero-length row).

Parameters

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

Request

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

resp = requests.delete(
    "https://api.arcnm.io/api/v1/environments/{env_id}/subcontractors/{membership_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/environments/{env_id}/subcontractors/{membership_id}", {
  method: "DELETE",
  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
message string Human-readable result of the operation.

Example response

{
  "message": "string"
}

Update Env Subcontractor Membership

PATCH /api/v1/environments/{env_id}/subcontractors/{membership_id}

Enable/disable a subcontractor here without detaching it.

The engine has always gated on this flag — pipeline_context_cache filters is_enabled.is_(True) when it collects the sourced operation kinds — but no route set it, so it could never be anything but the True it defaults to on attach. A shop wanting to stop sourcing one operation from one subcontractor in one environment had to detach and re-attach, discarding the SCD2 history the membership exists to keep. Same shape as the tool sibling above.

Parameters

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

Request body (application/json)

Field Type Required Description
is_enabled boolean yes Enable/disable without detaching.

Request

curl -X PATCH https://api.arcnm.io/api/v1/environments/{env_id}/subcontractors/{membership_id} \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "is_enabled": true
  }'
import requests

resp = requests.patch(
    "https://api.arcnm.io/api/v1/environments/{env_id}/subcontractors/{membership_id}",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "is_enabled": True
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/subcontractors/{membership_id}", {
  method: "PATCH",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "is_enabled": true
  }),
})
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
is_active boolean
is_enabled boolean
lead_time_days integer
membership_id string
name string
operation_kinds string[] External operations this member offers (capability slugs).
preset_key string Platform preset this shop was installed from (heat_treatment_shop, electroplating_shop, …); null = the org's own subcontractor.
region string
subcontractor_id string
valid_from string

Example response

{
  "is_active": true,
  "is_enabled": true,
  "lead_time_days": 0,
  "membership_id": "string",
  "name": "string",
  "operation_kinds": [
    "string"
  ],
  "preset_key": "string",
  "region": "EU",
  "subcontractor_id": "string",
  "valid_from": "string"
}

Restore Env Subcontractors

POST /api/v1/environments/{env_id}/subcontractors/restore

Restore the platform subcontractor presets in the default environment: hidden presets return, removed ones re-attach, switched-off ones switch on and are active again. Your own subcontractors and every other environment are untouched. 409 on any other environment.

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/environments/{env_id}/subcontractors/restore \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/environments/{env_id}/subcontractors/restore",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/subcontractors/restore", {
  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
enabled integer Switched-off memberships switched back on.
message string Always 'restored'.
reattached integer Presets re-attached (new SCD2 rows).
unhidden integer Hidden presets that returned.

Example response

{
  "enabled": 0,
  "message": "string",
  "reattached": 0,
  "unhidden": 0
}

List Env Tools

GET /api/v1/environments/{env_id}/tools

The crib tools THIS environment runs (active memberships). What the engine prices with — an empty list means the environment deliberately runs no crib tool and every handler keeps its own constants. at answers "what does the crib look like on date X" — the same active_as_of window pricing resolves on that date (rates already take ?at); it defaults to today.

Parameters

Name In Type Required Description
env_id path string yes Identifier of the env.
at query string no As-of date for the crib window; defaults to today. Answers "what does the crib look like on date X" — a scheduled addition appears once at reaches its valid_from, a scheduled removal disappears from its valid_to on.

Request

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

resp = requests.get(
    "https://api.arcnm.io/api/v1/environments/{env_id}/tools",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/tools", {
  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.

Attach Env Tool

POST /api/v1/environments/{env_id}/tools

Attach one of your crib tools to this environment.

Parameters

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

Request body (application/json)

Field Type Required Description
is_enabled boolean no
tool_id string yes Crib tool to attach.

Request

curl -X POST https://api.arcnm.io/api/v1/environments/{env_id}/tools \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tool_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  }'
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/environments/{env_id}/tools",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "tool_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/tools", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "tool_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  }),
})
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.
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 201

Field Type Description
catalogue_item_id string Platform article this tool was adopted from; null = custom tool.
diameter_mm number Cutting diameter, mm.
differs_from_article boolean The tool's geometry / economics / spec differ from its article's (edited); always false for a custom tool.
fits_machine_names string[] The enabled machines that can run the tool, by name — the positive half of the fitment answer. Null when fitment does not apply to this tool kind (nothing mounts it).
fits_machines boolean Whether any enabled machine of this environment can run the tool; false = the tool is inert here whatever its switch says.
is_enabled boolean Whether pricing may use this tool here.
kind string Tool kind (VDI 2852 class).
membership_id string Membership row id (detach/toggle handle).
name string Tool name.
price_eur number Replacement price, EUR.
teeth integer Cutting edges / flutes.
tool_id string Crib tool id.
tool_life_min number Edge life, cutting minutes.
valid_from string Membership validity start (ISO date).

Example response

{
  "catalogue_item_id": "string",
  "diameter_mm": 0,
  "differs_from_article": false,
  "fits_machine_names": [
    "string"
  ],
  "fits_machines": true,
  "is_enabled": true,
  "kind": "string",
  "membership_id": "string",
  "name": "string",
  "price_eur": 0,
  "teeth": 0,
  "tool_id": "string",
  "tool_life_min": 0,
  "valid_from": "string"
}

Detach Env Tool

DELETE /api/v1/environments/{env_id}/tools/{membership_id}

Detach a tool from this environment (SCD2 retire; a same-day attach is hard-deleted). The heal-forward record means a detached tool is never silently re-attached by provisioning.

Parameters

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

Request

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

resp = requests.delete(
    "https://api.arcnm.io/api/v1/environments/{env_id}/tools/{membership_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/environments/{env_id}/tools/{membership_id}", {
  method: "DELETE",
  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
message string Human-readable result of the operation.

Example response

{
  "message": "string"
}

Update Env Tool Membership

PATCH /api/v1/environments/{env_id}/tools/{membership_id}

Enable/disable a tool here without detaching it.

Parameters

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

Request body (application/json)

Field Type Required Description
is_enabled boolean yes Enable/disable without detaching.

Request

curl -X PATCH https://api.arcnm.io/api/v1/environments/{env_id}/tools/{membership_id} \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "is_enabled": true
  }'
import requests

resp = requests.patch(
    "https://api.arcnm.io/api/v1/environments/{env_id}/tools/{membership_id}",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "is_enabled": True
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/tools/{membership_id}", {
  method: "PATCH",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "is_enabled": true
  }),
})
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
catalogue_item_id string Platform article this tool was adopted from; null = custom tool.
diameter_mm number Cutting diameter, mm.
differs_from_article boolean The tool's geometry / economics / spec differ from its article's (edited); always false for a custom tool.
fits_machine_names string[] The enabled machines that can run the tool, by name — the positive half of the fitment answer. Null when fitment does not apply to this tool kind (nothing mounts it).
fits_machines boolean Whether any enabled machine of this environment can run the tool; false = the tool is inert here whatever its switch says.
is_enabled boolean Whether pricing may use this tool here.
kind string Tool kind (VDI 2852 class).
membership_id string Membership row id (detach/toggle handle).
name string Tool name.
price_eur number Replacement price, EUR.
teeth integer Cutting edges / flutes.
tool_id string Crib tool id.
tool_life_min number Edge life, cutting minutes.
valid_from string Membership validity start (ISO date).

Example response

{
  "catalogue_item_id": "string",
  "diameter_mm": 0,
  "differs_from_article": false,
  "fits_machine_names": [
    "string"
  ],
  "fits_machines": true,
  "is_enabled": true,
  "kind": "string",
  "membership_id": "string",
  "name": "string",
  "price_eur": 0,
  "teeth": 0,
  "tool_id": "string",
  "tool_life_min": 0,
  "valid_from": "string"
}

Restore Env Tools

POST /api/v1/environments/{env_id}/tools/restore

Restore the platform tool set in the default environment: hidden platform tools return, removed ones are re-attached, switched-off ones switch on. Edited values, custom tools and every other environment are untouched. 409 on any other environment.

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/environments/{env_id}/tools/restore \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/environments/{env_id}/tools/restore",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/tools/restore", {
  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
enabled integer Switched-off memberships switched back on.
message string Always 'restored'.
reattached integer Tools re-attached to the default environment (new SCD2 rows).
unhidden integer Hidden platform tools that returned.

Example response

{
  "enabled": 0,
  "message": "string",
  "reattached": 0,
  "unhidden": 0
}

Get Calculation Tuning

GET /api/v1/environments/{env_id}/tuning

Read the environment's calculation-tuning knobs with provenance: stated on this environment, inherited from a parent environment, or the platform default — the order calculations read them in.

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/environments/{env_id}/tuning \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.get(
    "https://api.arcnm.io/api/v1/environments/{env_id}/tuning",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/tuning", {
  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
bar_clamp_remnant_mm TuningKnob End of each bar, mm, the clamp holds and no part is cut from. It leaves as scrap.
bar_length_mm TuningKnob Length, mm, the round bar a turned part is cut from is bought in. A part carries its share of one bar. Once your stock formats list bars of your own, a part is priced on the lengths listed there, and this length is only the fallback when none of them can be used.
bar_remnant_min_l_mm TuningKnob Shortest end, mm, a bar leaves past its last part — or a tube past its part — that counts as a reusable remnant. Applies only where remnants are credited.
custom_format_surcharge_pct TuningKnob Premium a sheet cut to an ordered size carries on its metal over the same metal in a stocked format, as a fraction of the metal's price (0.15 = 15 %). Read by the nesting workbench's sheet comparison: a proposed custom size must beat the best stocked format after this premium and the cut-to-size fees. A re-run on a stated size books no surcharge yet.
cut_to_size_increment_mm TuningKnob Step, mm, a plate ordered cut to size is bought in: each side rounds up to a whole multiple of it. 0 buys the exact size.
cycle_factor TuningKnob Multiplier on the WHOLE per-unit run time across all process bins — cutting, air moves, peck retract, tool changes and load/unload, read off the reference process (milling). See 'processes'. Setup and programming have their own factors.
edge_margin_mm TuningKnob Edge margin kept to every side of a purchased sheet, mm — clamping and edge trim. A plate ordered cut to size carries it on each side too.
erholzeit_pct TuningKnob Recovery allowance (Erholzeit) as a fraction OF THE RUN BASE TIME it is added to. Setup has its own, ruesterholzeit_pct.
full_stock_threshold TuningKnob The fill of a started stock piece — a sheet, a bar — from which a lot pays the piece whole, as a fraction of the parts it holds (0.85 = 85 %). Below it the lot pays its share and the rest goes back to stock; from it on the whole piece is the lot's and its empty cells are credited as scrap where scrap is credited. 1.0 turns the rule off (every lot pays its share).
learning_rate TuningKnob Wright learning-curve rate applied to the lot-size curve (1.0 = off).
micro_joint_width_mm TuningKnob Width of a micro-joint, mm — the uncut tab that holds a part in the skeleton; applies where micro-joints are used (shop practice).
micro_joints_over_1000mm TuningKnob Micro-joints per part whose outline is over 1000 mm long.
micro_joints_to_1000mm TuningKnob Micro-joints per part whose outline is over 300 up to 1000 mm.
micro_joints_to_300mm TuningKnob Micro-joints per part whose outline is up to 300 mm long.
min_web_mm_over_12mm TuningKnob Minimum web between parts, mm, for plate over 12 mm.
min_web_mm_to_12mm TuningKnob Minimum web between parts, mm, for plate over 6 up to 12 mm.
min_web_mm_to_3mm TuningKnob Minimum web between parts, mm, for sheet up to 3 mm thick — applies where the web depends on the thickness (shop practice).
min_web_mm_to_6mm TuningKnob Minimum web between parts, mm, for sheet over 3 up to 6 mm.
processes object Every process that states a factor of its own on this environment, keyed by process (milling, turning, sawing, ...). A flat write states all of them; a per-process write states only the ones it names. Empty when this environment sets nothing; a parent environment's per-process factors are not listed.
programming_factor TuningKnob Multiplier on estimated programming times, read off the reference process (milling). See 'processes'.
remnant_min_area_share TuningKnob Area a reusable remnant needs, as a fraction of the purchased sheet's (0.05 = 5 %). Applies only where remnants are credited.
remnant_min_l_mm TuningKnob Longer side, mm, a rest of the sheet needs to count as a reusable remnant. Applies only where remnants are credited.
remnant_min_w_mm TuningKnob Shorter side, mm, a rest of the sheet needs to count as a reusable remnant. Applies only where remnants are credited.
remnant_value_factor TuningKnob What a reusable remnant — a rest of a sheet, an end of a bar or a tube — is worth against new stock of its kind, as a fraction of its price per kg (0.8 = 80 %). Applies only where remnants are credited.
ruesterholzeit_pct TuningKnob Recovery allowance on setup (Rüsterholzeit) as a fraction OF THE SETUP BASE TIME. Separate from the run plane's erholzeit_pct: setting up and running are different work.
ruestverteilzeit_pct TuningKnob Distribution allowance on setup (Rüstverteilzeit) as a fraction OF THE SETUP BASE TIME.
setup_factor TuningKnob Multiplier on estimated setup times, read off the reference process (milling). Per-process factors are in 'processes'.
skeleton_web_mm TuningKnob Web left standing between two nested parts, mm, WITHOUT the cutting gap: the cutter's own kerf is added to it, so parts sit this web plus the kerf apart.
verteilzeit_pct TuningKnob Distribution allowance (Verteilzeit) as a fraction OF THE RUN BASE TIME — 0.10 adds 10 % to every machining time.

Example response

{
  "bar_clamp_remnant_mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "bar_length_mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "bar_remnant_min_l_mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "custom_format_surcharge_pct": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "cut_to_size_increment_mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "cycle_factor": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "edge_margin_mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "erholzeit_pct": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "full_stock_threshold": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "learning_rate": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "micro_joint_width_mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "micro_joints_over_1000mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "micro_joints_to_1000mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "micro_joints_to_300mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "min_web_mm_over_12mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "min_web_mm_to_12mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "min_web_mm_to_3mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "min_web_mm_to_6mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "processes": {},
  "programming_factor": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "remnant_min_area_share": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "remnant_min_l_mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "remnant_min_w_mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "remnant_value_factor": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "ruesterholzeit_pct": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "ruestverteilzeit_pct": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "setup_factor": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "skeleton_web_mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "verteilzeit_pct": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  }
}

Update Calculation Tuning

PUT /api/v1/environments/{env_id}/tuning

Write env-tier tuning overrides (audited).

setup_factor / programming_factor / cycle_factor write the per-bin setup_efficiency / programming_efficiency / cycle_efficiency resolver overrides for every routable process bin at ENV tier; 1.0, their identity, clears the override. processes writes the same three cells for only the processes it names, applied after the flat fields.

The four REFA allowances (verteilzeit_pct, erholzeit_pct, ruestverteilzeit_pct, ruesterholzeit_pct) write the same per-bin overrides, over every bin that reads them.

The six sheet-nesting settings (edge_margin_mm, skeleton_web_mm, the three remnant thresholds and cut_to_size_increment_mm) each write their one cell of the sheet-nesting settings, which is where a sheet part's purchase reads them, and remnant_value_factor its cell of the material valuation policy, beside the remnant-credit answer that switches it on. The three bar stock settings (bar_length_mm, bar_clamp_remnant_mm and the shortest end that counts as a remnant, bar_remnant_min_l_mm) write theirs, which is where a turned part's bar is bought. Like the allowances, 0 is a statement and reset is the way back.

The four inspection_* fields write the platform-wide inspection constants in the global bin — the cells the cost sheet's Prüfung line lets a reader move. They read back on GET /environments/{env_id}/factors with their provenance and the platform value beside them, which is where every resolved factor is published; this endpoint's own read model stays the tuning knobs.

learning_rate writes the env's Wright rate. Every change lands an audit row.

null means "no statement" on every field of this body, and never a write: a client that serialises its whole model sends nulls for the knobs it is silent about, and that must leave them alone. Clearing a knob, so that it reads a parent environment's statement where one exists and else the platform default, is the named reset list — the allowances need it (0 is a statable allowance, so they have no identity value to send), and the multipliers accept it as the spelled-out equivalent of sending 1.0. The inspection cells are the one exception: an explicit null there clears the override, the contract the cost sheet's save dialog was built against.

Parameters

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

Request body (application/json)

Field Type Required Description
bar_clamp_remnant_mm number no End of each bar the clamp holds and no part is cut from, mm; omit or send null to keep, "reset" to clear it back to inherit / platform default.
bar_length_mm number no Length the round bar a turned part is cut from is bought in, mm; omit or send null to keep, "reset" to clear it back to inherit / platform default.
bar_remnant_min_l_mm number no Shortest bar or tube end that counts as a reusable remnant, mm; omit or send null to keep, "reset" to clear it back to inherit / platform default.
custom_format_surcharge_pct number no Premium a sheet cut to an ordered size carries on its metal, as a fraction of the metal's price (0.15 = 15 %); omit or send null to keep, "reset" to clear it back to inherit / platform default.
cut_to_size_increment_mm number no Step a plate ordered cut to size is bought in, mm — each side rounds up to a multiple of it, 0 buys the exact size; omit or send null to keep, "reset" to clear it back to inherit / platform default.
cycle_factor number no Multiplier on the whole per-unit run time — cutting, air moves, peck retract, tool changes and load/unload; omit or send null to keep, 1.0 (its identity) or "reset" to clear it back to inherit / platform default.
edge_margin_mm number no Edge margin kept to every side of a purchased sheet, mm; omit or send null to keep, "reset" to clear it back to inherit / platform default. 0 is a statement, not a reset.
erholzeit_pct number no Recovery allowance on the RUN base time, as a fraction (0.045 = 4.5 %); omit or send null to keep, "reset" to clear it back to inherit / platform default.
full_stock_threshold number no The fill of a started stock piece from which a lot pays it whole, as a fraction of the parts it holds (0.85 = 85 %; 1.0 turns the rule off); omit or send null to keep, "reset" to clear it back to inherit / platform default.
inspection_tau_s_per_element number no Seconds to measure one characteristic; omit to keep, send null to clear your override.
inspection_weight_datum number no Effort weight of a datum characteristic; omit to keep, null to clear.
inspection_weight_gdt number no Effort weight of a geometric-tolerance characteristic; omit to keep, null to clear.
inspection_weight_ndt number no Effort weight of a non-destructive-testing callout; omit to keep, null to clear.
learning_rate number no Wright rate in [0.5, 1.0]; 1.0 disables learning. Omit or send null to keep it; name it in "reset" to return it to the platform default.
micro_joint_width_mm number no Micro-joint width, mm; omit or send null to keep, "reset" to clear it back to inherit / platform default.
micro_joints_over_1000mm number no Micro-joints per part with an outline over 1000 mm; omit or send null to keep, "reset" to clear it back to inherit / platform default.
micro_joints_to_1000mm number no Micro-joints per part with an outline over 300 up to 1000 mm; omit or send null to keep, "reset" to clear it back to inherit / platform default.
micro_joints_to_300mm number no Micro-joints per part with an outline up to 300 mm; omit or send null to keep, "reset" to clear it back to inherit / platform default.
min_web_mm_over_12mm number no Minimum web for plate over 12 mm, mm; omit or send null to keep, "reset" to clear it back to inherit / platform default.
min_web_mm_to_12mm number no Minimum web for plate over 6 up to 12 mm, mm; omit or send null to keep, "reset" to clear it back to inherit / platform default.
min_web_mm_to_3mm number no Minimum web for sheet up to 3 mm, mm; omit or send null to keep, "reset" to clear it back to inherit / platform default.
min_web_mm_to_6mm number no Minimum web for sheet over 3 up to 6 mm, mm; omit or send null to keep, "reset" to clear it back to inherit / platform default.
processes object no Per-process factors, keyed by process (milling, turning, sawing, ...). Applied after the flat fields, so a flat value sets every process and a named one overrides it.
programming_factor number no Programming-time multiplier; omit or send null to keep, 1.0 (its identity) or "reset" to clear it back to inherit / platform default.
remnant_min_area_share number no Area a reusable remnant needs, as a fraction of the purchased sheet's (0.05 = 5 %); omit or send null to keep, "reset" to clear it back to inherit / platform default.
remnant_min_l_mm number no Longer side a reusable remnant needs, mm; omit or send null to keep, "reset" to clear it back to inherit / platform default.
remnant_min_w_mm number no Shorter side a reusable remnant needs, mm; omit or send null to keep, "reset" to clear it back to inherit / platform default.
remnant_value_factor number no What a reusable remnant is worth against new stock of its kind, as a fraction of its price per kg (0.8 = 80 %); omit or send null to keep, "reset" to clear it back to inherit / platform default.
reset string[] no Knobs to clear on this environment, by name — learning_rate, setup_factor, programming_factor, cycle_factor, verteilzeit_pct, erholzeit_pct, ruestverteilzeit_pct, ruesterholzeit_pct, edge_margin_mm, skeleton_web_mm, remnant_min_l_mm, remnant_min_w_mm, remnant_min_area_share, remnant_value_factor, full_stock_threshold, cut_to_size_increment_mm, custom_format_surcharge_pct, bar_length_mm, bar_clamp_remnant_mm, bar_remnant_min_l_mm, min_web_mm_to_3mm, min_web_mm_to_6mm, min_web_mm_to_12mm, min_web_mm_over_12mm, micro_joint_width_mm, micro_joints_to_300mm, micro_joints_to_1000mm, micro_joints_over_1000mm. A cleared knob reads what a parent environment states, else the platform default; learning_rate is this environment's own and returns to the platform default. Clearing is a named gesture because a null in a knob's own field means 'no statement' and must never delete one. An unknown name is rejected.
ruesterholzeit_pct number no Recovery allowance on the SETUP base time, as a fraction (0.045 = 4.5 %); omit or send null to keep, "reset" to clear it back to inherit / platform default.
ruestverteilzeit_pct number no Distribution allowance on setup as a fraction of the setup base time (0.10 = 10 %); omit or send null to keep, "reset" to clear it back to inherit / platform default.
setup_factor number no Setup-time multiplier; omit or send null to keep, 1.0 (its identity) or "reset" to clear it back to inherit / platform default.
skeleton_web_mm number no Web between two nested parts WITHOUT the cutting gap, mm — the cutter's kerf is added to it; omit or send null to keep, "reset" to clear it back to inherit / platform default.
verteilzeit_pct number no Distribution allowance as a fraction of the run base time (0.10 = 10 %); omit or send null to keep, "reset" to clear it back to inherit / platform default. 0 is a statable allowance, not a reset.

Request

curl -X PUT https://api.arcnm.io/api/v1/environments/{env_id}/tuning \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "bar_clamp_remnant_mm": 0,
    "bar_length_mm": 0,
    "bar_remnant_min_l_mm": 0,
    "custom_format_surcharge_pct": 0,
    "cut_to_size_increment_mm": 0,
    "cycle_factor": 0,
    "edge_margin_mm": 0,
    "erholzeit_pct": 0,
    "full_stock_threshold": 0,
    "inspection_tau_s_per_element": 0,
    "inspection_weight_datum": 0,
    "inspection_weight_gdt": 0,
    "inspection_weight_ndt": 0,
    "learning_rate": 0,
    "micro_joint_width_mm": 0,
    "micro_joints_over_1000mm": 0,
    "micro_joints_to_1000mm": 0,
    "micro_joints_to_300mm": 0,
    "min_web_mm_over_12mm": 0,
    "min_web_mm_to_12mm": 0,
    "min_web_mm_to_3mm": 0,
    "min_web_mm_to_6mm": 0,
    "processes": {},
    "programming_factor": 0,
    "remnant_min_area_share": 0,
    "remnant_min_l_mm": 0,
    "remnant_min_w_mm": 0,
    "remnant_value_factor": 0,
    "reset": [
      "string"
    ],
    "ruesterholzeit_pct": 0,
    "ruestverteilzeit_pct": 0,
    "setup_factor": 0,
    "skeleton_web_mm": 0,
    "verteilzeit_pct": 0
  }'
import requests

resp = requests.put(
    "https://api.arcnm.io/api/v1/environments/{env_id}/tuning",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "bar_clamp_remnant_mm": 0,
        "bar_length_mm": 0,
        "bar_remnant_min_l_mm": 0,
        "custom_format_surcharge_pct": 0,
        "cut_to_size_increment_mm": 0,
        "cycle_factor": 0,
        "edge_margin_mm": 0,
        "erholzeit_pct": 0,
        "full_stock_threshold": 0,
        "inspection_tau_s_per_element": 0,
        "inspection_weight_datum": 0,
        "inspection_weight_gdt": 0,
        "inspection_weight_ndt": 0,
        "learning_rate": 0,
        "micro_joint_width_mm": 0,
        "micro_joints_over_1000mm": 0,
        "micro_joints_to_1000mm": 0,
        "micro_joints_to_300mm": 0,
        "min_web_mm_over_12mm": 0,
        "min_web_mm_to_12mm": 0,
        "min_web_mm_to_3mm": 0,
        "min_web_mm_to_6mm": 0,
        "processes": {},
        "programming_factor": 0,
        "remnant_min_area_share": 0,
        "remnant_min_l_mm": 0,
        "remnant_min_w_mm": 0,
        "remnant_value_factor": 0,
        "reset": [
            "string"
        ],
        "ruesterholzeit_pct": 0,
        "ruestverteilzeit_pct": 0,
        "setup_factor": 0,
        "skeleton_web_mm": 0,
        "verteilzeit_pct": 0
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/{env_id}/tuning", {
  method: "PUT",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "bar_clamp_remnant_mm": 0,
    "bar_length_mm": 0,
    "bar_remnant_min_l_mm": 0,
    "custom_format_surcharge_pct": 0,
    "cut_to_size_increment_mm": 0,
    "cycle_factor": 0,
    "edge_margin_mm": 0,
    "erholzeit_pct": 0,
    "full_stock_threshold": 0,
    "inspection_tau_s_per_element": 0,
    "inspection_weight_datum": 0,
    "inspection_weight_gdt": 0,
    "inspection_weight_ndt": 0,
    "learning_rate": 0,
    "micro_joint_width_mm": 0,
    "micro_joints_over_1000mm": 0,
    "micro_joints_to_1000mm": 0,
    "micro_joints_to_300mm": 0,
    "min_web_mm_over_12mm": 0,
    "min_web_mm_to_12mm": 0,
    "min_web_mm_to_3mm": 0,
    "min_web_mm_to_6mm": 0,
    "processes": {},
    "programming_factor": 0,
    "remnant_min_area_share": 0,
    "remnant_min_l_mm": 0,
    "remnant_min_w_mm": 0,
    "remnant_value_factor": 0,
    "reset": [
      "string"
    ],
    "ruesterholzeit_pct": 0,
    "ruestverteilzeit_pct": 0,
    "setup_factor": 0,
    "skeleton_web_mm": 0,
    "verteilzeit_pct": 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
bar_clamp_remnant_mm TuningKnob End of each bar, mm, the clamp holds and no part is cut from. It leaves as scrap.
bar_length_mm TuningKnob Length, mm, the round bar a turned part is cut from is bought in. A part carries its share of one bar. Once your stock formats list bars of your own, a part is priced on the lengths listed there, and this length is only the fallback when none of them can be used.
bar_remnant_min_l_mm TuningKnob Shortest end, mm, a bar leaves past its last part — or a tube past its part — that counts as a reusable remnant. Applies only where remnants are credited.
custom_format_surcharge_pct TuningKnob Premium a sheet cut to an ordered size carries on its metal over the same metal in a stocked format, as a fraction of the metal's price (0.15 = 15 %). Read by the nesting workbench's sheet comparison: a proposed custom size must beat the best stocked format after this premium and the cut-to-size fees. A re-run on a stated size books no surcharge yet.
cut_to_size_increment_mm TuningKnob Step, mm, a plate ordered cut to size is bought in: each side rounds up to a whole multiple of it. 0 buys the exact size.
cycle_factor TuningKnob Multiplier on the WHOLE per-unit run time across all process bins — cutting, air moves, peck retract, tool changes and load/unload, read off the reference process (milling). See 'processes'. Setup and programming have their own factors.
edge_margin_mm TuningKnob Edge margin kept to every side of a purchased sheet, mm — clamping and edge trim. A plate ordered cut to size carries it on each side too.
erholzeit_pct TuningKnob Recovery allowance (Erholzeit) as a fraction OF THE RUN BASE TIME it is added to. Setup has its own, ruesterholzeit_pct.
full_stock_threshold TuningKnob The fill of a started stock piece — a sheet, a bar — from which a lot pays the piece whole, as a fraction of the parts it holds (0.85 = 85 %). Below it the lot pays its share and the rest goes back to stock; from it on the whole piece is the lot's and its empty cells are credited as scrap where scrap is credited. 1.0 turns the rule off (every lot pays its share).
learning_rate TuningKnob Wright learning-curve rate applied to the lot-size curve (1.0 = off).
micro_joint_width_mm TuningKnob Width of a micro-joint, mm — the uncut tab that holds a part in the skeleton; applies where micro-joints are used (shop practice).
micro_joints_over_1000mm TuningKnob Micro-joints per part whose outline is over 1000 mm long.
micro_joints_to_1000mm TuningKnob Micro-joints per part whose outline is over 300 up to 1000 mm.
micro_joints_to_300mm TuningKnob Micro-joints per part whose outline is up to 300 mm long.
min_web_mm_over_12mm TuningKnob Minimum web between parts, mm, for plate over 12 mm.
min_web_mm_to_12mm TuningKnob Minimum web between parts, mm, for plate over 6 up to 12 mm.
min_web_mm_to_3mm TuningKnob Minimum web between parts, mm, for sheet up to 3 mm thick — applies where the web depends on the thickness (shop practice).
min_web_mm_to_6mm TuningKnob Minimum web between parts, mm, for sheet over 3 up to 6 mm.
processes object Every process that states a factor of its own on this environment, keyed by process (milling, turning, sawing, ...). A flat write states all of them; a per-process write states only the ones it names. Empty when this environment sets nothing; a parent environment's per-process factors are not listed.
programming_factor TuningKnob Multiplier on estimated programming times, read off the reference process (milling). See 'processes'.
remnant_min_area_share TuningKnob Area a reusable remnant needs, as a fraction of the purchased sheet's (0.05 = 5 %). Applies only where remnants are credited.
remnant_min_l_mm TuningKnob Longer side, mm, a rest of the sheet needs to count as a reusable remnant. Applies only where remnants are credited.
remnant_min_w_mm TuningKnob Shorter side, mm, a rest of the sheet needs to count as a reusable remnant. Applies only where remnants are credited.
remnant_value_factor TuningKnob What a reusable remnant — a rest of a sheet, an end of a bar or a tube — is worth against new stock of its kind, as a fraction of its price per kg (0.8 = 80 %). Applies only where remnants are credited.
ruesterholzeit_pct TuningKnob Recovery allowance on setup (Rüsterholzeit) as a fraction OF THE SETUP BASE TIME. Separate from the run plane's erholzeit_pct: setting up and running are different work.
ruestverteilzeit_pct TuningKnob Distribution allowance on setup (Rüstverteilzeit) as a fraction OF THE SETUP BASE TIME.
setup_factor TuningKnob Multiplier on estimated setup times, read off the reference process (milling). Per-process factors are in 'processes'.
skeleton_web_mm TuningKnob Web left standing between two nested parts, mm, WITHOUT the cutting gap: the cutter's own kerf is added to it, so parts sit this web plus the kerf apart.
verteilzeit_pct TuningKnob Distribution allowance (Verteilzeit) as a fraction OF THE RUN BASE TIME — 0.10 adds 10 % to every machining time.

Example response

{
  "bar_clamp_remnant_mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "bar_length_mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "bar_remnant_min_l_mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "custom_format_surcharge_pct": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "cut_to_size_increment_mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "cycle_factor": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "edge_margin_mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "erholzeit_pct": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "full_stock_threshold": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "learning_rate": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "micro_joint_width_mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "micro_joints_over_1000mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "micro_joints_to_1000mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "micro_joints_to_300mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "min_web_mm_over_12mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "min_web_mm_to_12mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "min_web_mm_to_3mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "min_web_mm_to_6mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "processes": {},
  "programming_factor": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "remnant_min_area_share": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "remnant_min_l_mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "remnant_min_w_mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "remnant_value_factor": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "ruesterholzeit_pct": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "ruestverteilzeit_pct": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "setup_factor": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "skeleton_web_mm": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  },
  "verteilzeit_pct": {
    "platform_default": 0,
    "source": "string",
    "value": 0
  }
}

Clone Preset

POST /api/v1/environments/clone-preset

Deep-copy a platform region preset (env + rates + fleet) into the tenant.

The clone is immediately runnable and priceable for every discipline. Only a genuine platform preset can be cloned — any other id (including another tenant's env) reads as 404.

Request body (application/json)

Field Type Required Description
name string no Optional name for the cloned environment; defaults to ' (copy)'.
preset_id string yes Identifier of the platform region preset to clone.

Request

curl -X POST https://api.arcnm.io/api/v1/environments/clone-preset \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "preset_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  }'
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/environments/clone-preset",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "preset_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/clone-preset", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "preset_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  }),
})
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
currency string ISO 4217 currency code the environment's rates are denominated in.
description string Optional longer description of the environment.
id string Unique identifier of the costing environment.
is_baseline boolean Whether this is the auto-provisioned default environment for the tenant.
machine_count integer Machines currently in this environment's fleet.
machines_cloned integer Number of machines copied into the environment; only set by the clone endpoints.
name string Human-readable name of the costing environment.
parent_environment_id string Environment this one inherits from, or null when it stands alone. Anything this environment does not state itself resolves from the parent and, recursively, from the parent's parent.
rate_count integer Rate rows this environment states itself (every kind, every validity window) — what GET /{env_id}/rates returns. Rates it only inherits from its parent are not counted.
rates_cloned integer Number of rate rows copied into the environment; only set by the clone endpoints.
region string Geographic region this environment prices for (e.g. DE, US).
scheduled_changes_dropped integer Memberships of the source whose validity window is not active today (scheduled future additions or removals). A clone is a snapshot as of today, so these do not travel; re-schedule them on the copy if you need them. Only set by the clone endpoint.
subcontractor_count integer Subcontractors this environment orders from.
tool_count integer Crib tools currently attached to this environment.
valid_from string ISO date (YYYY-MM-DD) from which this environment is effective.
valid_to string ISO date (YYYY-MM-DD) the environment stops being effective; null = open-ended.

Example response

{
  "currency": "EUR",
  "description": "string",
  "id": "string",
  "is_baseline": true,
  "machine_count": 0,
  "machines_cloned": 0,
  "name": "string",
  "parent_environment_id": "string",
  "rate_count": 0,
  "rates_cloned": 0,
  "region": "EU",
  "scheduled_changes_dropped": 0,
  "subcontractor_count": 0,
  "tool_count": 0,
  "valid_from": "string",
  "valid_to": "string"
}

List Presets

GET /api/v1/environments/presets

The 6 platform region presets a tenant can clone into a runnable env.

Read-only and platform-scoped: only platform rows (org_id IS NULL) are returned (bypass-read inside the service), so no tenant data is exposed.

Request

curl -X GET https://api.arcnm.io/api/v1/environments/presets \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.get(
    "https://api.arcnm.io/api/v1/environments/presets",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/presets", {
  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.
429 rate_limited Per-key or per-org rate limit exceeded — back off with jitter and retry.

List published surcharge-rate presets.

GET /api/v1/environments/surcharge-presets

Benchmark Zuschlagskalkulation rates from primary sources (automotive supplier mark-ups on total manufacturing cost; the usual profit mark-up of German public price law), each with its citation and the equivalent environment economics. Nothing here prices on its own: applying a preset means sending its economics to PATCH /environments/{env_id}/economics, which records the rates as that environment's own statement.

Request

curl -X GET https://api.arcnm.io/api/v1/environments/surcharge-presets \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.get(
    "https://api.arcnm.io/api/v1/environments/surcharge-presets",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/environments/surcharge-presets", {
  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.
429 rate_limited Per-key or per-org rate limit exceeded — back off with jitter and retry.