API reference
Machine Library
The Machine Library API manages your machine catalogue: create, list, fetch, update, and delete entries, see where each is used, and compute an hourly rate.
The Machine Library API manages your machine catalogue: create, list, fetch, update, and delete entries, see where each is used, and compute an hourly rate.
Auto-generated from the public OpenAPI spec — this page never drifts from the running API. Base URL
https://api.arcnm.io. Authenticate with theX-API-Keyheader (see Authentication).
List Library
GET /api/v1/machine-library
The browsable machine park: the shared platform catalog plus your organization's own entries, in one list.
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;limitcaps the page size.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
klass |
query | string | no | Filter to a single machine class (e.g. milling.5axis_full). |
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. |
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/machine-library \
-H "X-API-Key: $ARCNM_API_KEY"
import requests
resp = requests.get(
"https://api.arcnm.io/api/v1/machine-library",
headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/machine-library", {
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. |
Create Library Entry
POST /api/v1/machine-library
Add a tenant-private machine to the library. Platform-catalog
entries are curated out-of-band (seed), never via this endpoint —
the row is always stamped with the caller's org_id.
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
default_capabilities |
MachineCapabilities | yes | Default capability matrix for the type; instances override individual fields. |
default_vdi_3258 |
VDI3258Inputs | no | Optional VDI 3258 cost-structure defaults enabling (machine, region) hour-rate resolution. |
is_generic |
boolean | no | True for a class-default type template; False for a representative named machine. |
klass |
milling.3axis_vmc | milling.5axis_full | milling.indexable_3plus2 | turning.2axis_cnc | turning.live_tool | turning.swiss_type | turning.mill_integrex | press_brake | laser_cutter | plasma_cutter | guillotine_shear | tube_bender | turret_punch | waterjet | wire_edm | sinter.dmls | fdm_industrial | injection_molding_press | grinder.surface | grinder.cylindrical | slotter | sink_edm | weld_station | saw.band | saw.circular | drill.pillar |
yes | Machine class (process family), e.g. milling.5axis_full. |
model_no |
string | yes | Vendor model number, or a generic-type slug. |
name |
string | yes | Human-readable catalog name (e.g. '3-axis VMC — generic' or 'DMG MORI CMX 800 V'). |
nominal_hourly_rate_eur |
number | no | Type-default machine-hour rate in EUR/h; copied into an instance on instantiation. |
nominal_rate_operator_eur_per_h |
number | no | Operator wage already contained in nominal_hourly_rate_eur, in EUR per hour; copied into an instance alongside the rate. 0 means the rate is machine-only and operator labour is billed separately. |
nominal_rate_operator_share |
number | no | Operator attendance the nominal rate was built at (0-1), and the type's run-attendance default. NULL falls back to the class default. |
subclass |
small | medium | large |
no | Size tier of the machine within its class (small/medium/large). |
valid_from |
string | yes | Date from which this library entry is effective (ISO 8601). |
valid_to |
string | no | Date after which this entry is superseded; null if open-ended (ISO 8601). |
vendor |
string | yes | Machine manufacturer / vendor (or 'generic'). |
Request
curl -X POST https://api.arcnm.io/api/v1/machine-library \
-H "X-API-Key: $ARCNM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "string",
"vendor": "string",
"model_no": "string",
"klass": "milling.3axis_vmc",
"default_capabilities": {
"axes_indexable": 0,
"axes_simultaneous": 0,
"bar_feeder": false,
"certifications": [
"string"
],
"chatter_stability_lobe": {
"rpm_to_max_axial_depth_mm": [
{
"max_axial_depth_mm": 0,
"rpm": 0
}
]
},
"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"
]
},
"valid_from": "2026-06-01"
}'
import requests
resp = requests.post(
"https://api.arcnm.io/api/v1/machine-library",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"name": "string",
"vendor": "string",
"model_no": "string",
"klass": "milling.3axis_vmc",
"default_capabilities": {
"axes_indexable": 0,
"axes_simultaneous": 0,
"bar_feeder": False,
"certifications": [
"string"
],
"chatter_stability_lobe": {
"rpm_to_max_axial_depth_mm": [
{
"max_axial_depth_mm": 0,
"rpm": 0
}
]
},
"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"
]
},
"valid_from": "2026-06-01"
},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/machine-library", {
method: "POST",
headers: {
"X-API-Key": process.env.ARCNM_API_KEY!,
"Content-Type": "application/json",
},
body: JSON.stringify({
"name": "string",
"vendor": "string",
"model_no": "string",
"klass": "milling.3axis_vmc",
"default_capabilities": {
"axes_indexable": 0,
"axes_simultaneous": 0,
"bar_feeder": false,
"certifications": [
"string"
],
"chatter_stability_lobe": {
"rpm_to_max_axial_depth_mm": [
{
"max_axial_depth_mm": 0,
"rpm": 0
}
]
},
"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"
]
},
"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 |
|---|---|---|
default_capabilities |
object | Default capability matrix (axes, envelope, spindle, tolerances). Null on the shared platform catalog for integration callers; populated for your own catalog entries. |
default_vdi_3258 |
object | Optional VDI 3258 cost structure for (machine, region) rate resolution. |
id |
string | Unique identifier of the library entry. |
is_generic |
boolean | True for a class-default type; False for a representative named machine. |
klass |
string | Machine class (process family). |
model_no |
string | Vendor model number, or generic-type slug. |
name |
string | Human-readable catalog name. |
nominal_hourly_rate_eur |
number | Type-default machine-hour rate in EUR/h. Null on the shared platform catalog for integration callers; populated for your own catalog entries. |
nominal_rate_operator_eur_per_h |
number | Operator wage already contained in nominal_hourly_rate_eur, in EUR/h. 0 means the rate is machine-only and operator labour is billed separately. Null on the shared platform catalog for integration callers, alongside the rate it qualifies. |
nominal_rate_operator_share |
number | Operator attendance the nominal rate was built at (0-1), and the type's run-attendance default. Null when not declared. |
org_id |
string | Owning organisation; null for a platform-shared catalog entry. |
owner_scope |
string | 'platform' for the shared catalog, 'org' for a private addition. |
subclass |
string | Size tier within the class. |
valid_from |
string | ISO date this entry becomes effective. |
valid_to |
string | ISO date this entry is superseded; null = open-ended. |
vendor |
string | Machine manufacturer / vendor. |
Example response
{
"default_capabilities": {},
"default_vdi_3258": {},
"id": "string",
"is_generic": true,
"klass": "string",
"model_no": "string",
"name": "string",
"nominal_hourly_rate_eur": 0,
"nominal_rate_operator_eur_per_h": 0,
"nominal_rate_operator_share": 0,
"org_id": "string",
"owner_scope": "string",
"subclass": "string",
"valid_from": "string",
"valid_to": "string",
"vendor": "string"
}
Delete Library Entry
DELETE /api/v1/machine-library/{entry_id}
Delete a tenant-private entry. Refused (409) while any environment machine still references it — a library-derived instance resolves its capabilities from the entry, so detach those machines first.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
entry_id |
path | string | yes | Identifier of the entry. |
Request
curl -X DELETE https://api.arcnm.io/api/v1/machine-library/{entry_id} \
-H "X-API-Key: $ARCNM_API_KEY"
import requests
resp = requests.delete(
"https://api.arcnm.io/api/v1/machine-library/{entry_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/machine-library/{entry_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 Library Entry
GET /api/v1/machine-library/{entry_id}
Fetch one machine-library entry.
Environments instantiate machines by library_entry_id, so a caller
holding one previously had to page the whole catalog to resolve it.
Redaction is identical to the list: platform-catalog rows lose our cost structure for integration / MCP callers, tenant-private rows pass through.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
entry_id |
path | string | yes | Identifier of the entry. |
Request
curl -X GET https://api.arcnm.io/api/v1/machine-library/{entry_id} \
-H "X-API-Key: $ARCNM_API_KEY"
import requests
resp = requests.get(
"https://api.arcnm.io/api/v1/machine-library/{entry_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/machine-library/{entry_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 |
|---|---|---|
default_capabilities |
object | Default capability matrix (axes, envelope, spindle, tolerances). Null on the shared platform catalog for integration callers; populated for your own catalog entries. |
default_vdi_3258 |
object | Optional VDI 3258 cost structure for (machine, region) rate resolution. |
id |
string | Unique identifier of the library entry. |
is_generic |
boolean | True for a class-default type; False for a representative named machine. |
klass |
string | Machine class (process family). |
model_no |
string | Vendor model number, or generic-type slug. |
name |
string | Human-readable catalog name. |
nominal_hourly_rate_eur |
number | Type-default machine-hour rate in EUR/h. Null on the shared platform catalog for integration callers; populated for your own catalog entries. |
nominal_rate_operator_eur_per_h |
number | Operator wage already contained in nominal_hourly_rate_eur, in EUR/h. 0 means the rate is machine-only and operator labour is billed separately. Null on the shared platform catalog for integration callers, alongside the rate it qualifies. |
nominal_rate_operator_share |
number | Operator attendance the nominal rate was built at (0-1), and the type's run-attendance default. Null when not declared. |
org_id |
string | Owning organisation; null for a platform-shared catalog entry. |
owner_scope |
string | 'platform' for the shared catalog, 'org' for a private addition. |
subclass |
string | Size tier within the class. |
valid_from |
string | ISO date this entry becomes effective. |
valid_to |
string | ISO date this entry is superseded; null = open-ended. |
vendor |
string | Machine manufacturer / vendor. |
Example response
{
"default_capabilities": {},
"default_vdi_3258": {},
"id": "string",
"is_generic": true,
"klass": "string",
"model_no": "string",
"name": "string",
"nominal_hourly_rate_eur": 0,
"nominal_rate_operator_eur_per_h": 0,
"nominal_rate_operator_share": 0,
"org_id": "string",
"owner_scope": "string",
"subclass": "string",
"valid_from": "string",
"valid_to": "string",
"vendor": "string"
}
Update Library Entry
PATCH /api/v1/machine-library/{entry_id}
Change one of your organisation's machine-library entries: name, vendor, model, nominal rates, default capabilities, or the date it stops being valid. Platform entries are read-only and answer 403.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
entry_id |
path | string | yes | Identifier of the entry. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
default_capabilities |
object | no | |
default_vdi_3258 |
object | no | |
model_no |
string | no | |
name |
string | no | |
nominal_hourly_rate_eur |
number | no | |
nominal_rate_operator_eur_per_h |
number | no | |
nominal_rate_operator_share |
number | no | |
valid_to |
string | no | |
vendor |
string | no |
Request
curl -X PATCH https://api.arcnm.io/api/v1/machine-library/{entry_id} \
-H "X-API-Key: $ARCNM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"default_capabilities": {},
"default_vdi_3258": {},
"model_no": "string",
"name": "string",
"nominal_hourly_rate_eur": 0,
"nominal_rate_operator_eur_per_h": 0,
"nominal_rate_operator_share": 0,
"valid_to": "2026-06-01",
"vendor": "string"
}'
import requests
resp = requests.patch(
"https://api.arcnm.io/api/v1/machine-library/{entry_id}",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"default_capabilities": {},
"default_vdi_3258": {},
"model_no": "string",
"name": "string",
"nominal_hourly_rate_eur": 0,
"nominal_rate_operator_eur_per_h": 0,
"nominal_rate_operator_share": 0,
"valid_to": "2026-06-01",
"vendor": "string"
},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/machine-library/{entry_id}", {
method: "PATCH",
headers: {
"X-API-Key": process.env.ARCNM_API_KEY!,
"Content-Type": "application/json",
},
body: JSON.stringify({
"default_capabilities": {},
"default_vdi_3258": {},
"model_no": "string",
"name": "string",
"nominal_hourly_rate_eur": 0,
"nominal_rate_operator_eur_per_h": 0,
"nominal_rate_operator_share": 0,
"valid_to": "2026-06-01",
"vendor": "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 |
|---|---|---|
default_capabilities |
object | Default capability matrix (axes, envelope, spindle, tolerances). Null on the shared platform catalog for integration callers; populated for your own catalog entries. |
default_vdi_3258 |
object | Optional VDI 3258 cost structure for (machine, region) rate resolution. |
id |
string | Unique identifier of the library entry. |
is_generic |
boolean | True for a class-default type; False for a representative named machine. |
klass |
string | Machine class (process family). |
model_no |
string | Vendor model number, or generic-type slug. |
name |
string | Human-readable catalog name. |
nominal_hourly_rate_eur |
number | Type-default machine-hour rate in EUR/h. Null on the shared platform catalog for integration callers; populated for your own catalog entries. |
nominal_rate_operator_eur_per_h |
number | Operator wage already contained in nominal_hourly_rate_eur, in EUR/h. 0 means the rate is machine-only and operator labour is billed separately. Null on the shared platform catalog for integration callers, alongside the rate it qualifies. |
nominal_rate_operator_share |
number | Operator attendance the nominal rate was built at (0-1), and the type's run-attendance default. Null when not declared. |
org_id |
string | Owning organisation; null for a platform-shared catalog entry. |
owner_scope |
string | 'platform' for the shared catalog, 'org' for a private addition. |
subclass |
string | Size tier within the class. |
valid_from |
string | ISO date this entry becomes effective. |
valid_to |
string | ISO date this entry is superseded; null = open-ended. |
vendor |
string | Machine manufacturer / vendor. |
Example response
{
"default_capabilities": {},
"default_vdi_3258": {},
"id": "string",
"is_generic": true,
"klass": "string",
"model_no": "string",
"name": "string",
"nominal_hourly_rate_eur": 0,
"nominal_rate_operator_eur_per_h": 0,
"nominal_rate_operator_share": 0,
"org_id": "string",
"owner_scope": "string",
"subclass": "string",
"valid_from": "string",
"valid_to": "string",
"vendor": "string"
}
Rate Calculator
POST /api/v1/machine-library/rate-calculator
Compute a VDI 3258 machine-hour rate from cost-structure inputs.
Pure computation — nothing is persisted. Use the returned
total_per_h as a library entry's nominal rate, or store the
inputs as default_vdi_3258 for region-aware resolution.
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
acquisition_cost_eur |
number | yes | Machine acquisition cost (Anschaffungskosten), in EUR. The basis of the imputed interest, and of the depreciation where no replacement_value_eur is stated. |
annual_hours_T_G |
number | no | VDI 3258 total annual attendance hours (T_G), in hours per year. Omit it and state shift_model instead to derive T_G from your shift calendar; exactly one of the two is required. |
annual_hours_T_IH |
number | yes | VDI 3258 annual maintenance/repair hours (T_IH), in hours per year. |
annual_hours_T_ST |
number | yes | Annual standstill (idle) hours (T_ST), in hours per year — EXCLUDING setup: the platform bills setup hours explicitly at the setup rate, so they are run hours here. Counting them as standstill (as VDI 3258 does) would recover them twice. |
capital_interest_rate |
number | yes | Imputed annual interest rate on tied-up capital (0–1, e.g. 0.06 = 6%). |
depreciation_life_h |
number | yes | Depreciation life of the machine, in operating hours. |
energy_eur_per_kwh |
number | yes | Electricity price, in EUR per kilowatt-hour. |
energy_kw |
number | yes | Average electrical power draw while running, in kilowatts. |
floor_space_m2 |
number | yes | Floor space occupied by the machine, in square metres. |
maintenance_eur_per_year |
number | yes | Annual maintenance spend for the machine, in EUR per year. |
operator_hourly_eur |
number | yes | Fully-loaded operator labour cost, in EUR per hour. |
operator_share |
number | yes | Fraction of an operator's time allocated to this machine (0–1). |
replacement_value_eur |
number | no | Replacement value of the machine (Wiederbeschaffungswert), in EUR — what an equivalent machine costs today. Depreciation is computed on it; interest stays on the acquisition cost. Omit it to depreciate the acquisition cost. |
shift_model |
ShiftModelInputs | no | Shift calendar the annual hours derive from (shifts/day × hours/shift × days/week × weeks/year × availability). Mutually exclusive with an explicit annual_hours_T_G. |
space_eur_per_m2_y |
number | yes | Annual floor-space cost rate, in EUR per square metre per year. |
tooling_eur_per_year |
number | yes | Annual spend on general tooling and fixtures for the machine that no single part is charged for (Gemeinkostenwerkzeuge, Vorrichtungen), in EUR per year. Never perishable cutting tools: their wear is charged per part on the cost sheet's own tooling row, so stating them here would recover them twice. |
Request
curl -X POST https://api.arcnm.io/api/v1/machine-library/rate-calculator \
-H "X-API-Key: $ARCNM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"acquisition_cost_eur": 0,
"depreciation_life_h": 0,
"annual_hours_T_ST": 0,
"annual_hours_T_IH": 0,
"capital_interest_rate": 0,
"space_eur_per_m2_y": 0,
"floor_space_m2": 0,
"energy_kw": 0,
"energy_eur_per_kwh": 0,
"operator_hourly_eur": 0,
"operator_share": 0,
"maintenance_eur_per_year": 0,
"tooling_eur_per_year": 0
}'
import requests
resp = requests.post(
"https://api.arcnm.io/api/v1/machine-library/rate-calculator",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"acquisition_cost_eur": 0,
"depreciation_life_h": 0,
"annual_hours_T_ST": 0,
"annual_hours_T_IH": 0,
"capital_interest_rate": 0,
"space_eur_per_m2_y": 0,
"floor_space_m2": 0,
"energy_kw": 0,
"energy_eur_per_kwh": 0,
"operator_hourly_eur": 0,
"operator_share": 0,
"maintenance_eur_per_year": 0,
"tooling_eur_per_year": 0
},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/machine-library/rate-calculator", {
method: "POST",
headers: {
"X-API-Key": process.env.ARCNM_API_KEY!,
"Content-Type": "application/json",
},
body: JSON.stringify({
"acquisition_cost_eur": 0,
"depreciation_life_h": 0,
"annual_hours_T_ST": 0,
"annual_hours_T_IH": 0,
"capital_interest_rate": 0,
"space_eur_per_m2_y": 0,
"floor_space_m2": 0,
"energy_kw": 0,
"energy_eur_per_kwh": 0,
"operator_hourly_eur": 0,
"operator_share": 0,
"maintenance_eur_per_year": 0,
"tooling_eur_per_year": 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. |
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 |
|---|---|---|
annual_hours_T_G |
number | |
annual_hours_T_L |
number | |
capital_cost_per_h |
number | |
depreciation_per_h |
number | |
energy_per_h |
number | |
maintenance_per_h |
number | |
operator_share_per_h |
number | |
space_per_h |
number | |
tooling_allocation_per_h |
number | |
total_per_h |
number |
Example response
{
"annual_hours_T_G": 0,
"annual_hours_T_L": 0,
"capital_cost_per_h": 0,
"depreciation_per_h": 0,
"energy_per_h": 0,
"maintenance_per_h": 0,
"operator_share_per_h": 0,
"space_per_h": 0,
"tooling_allocation_per_h": 0,
"total_per_h": 0
}
Library Usage
GET /api/v1/machine-library/usage
Which environment fleets actually use each machine-library entry.
A library entry is a type — a template. It takes part in calculations only after a machine has been instantiated from it into an environment's fleet. This endpoint reports, per entry, the environments of YOUR organization whose active fleet contains such a machine, so a type that no calculation will ever use is visible as exactly that.
Entries with no usage are omitted — treat absence as "not in any fleet".
Request
curl -X GET https://api.arcnm.io/api/v1/machine-library/usage \
-H "X-API-Key: $ARCNM_API_KEY"
import requests
resp = requests.get(
"https://api.arcnm.io/api/v1/machine-library/usage",
headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/machine-library/usage", {
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. |