---
title: Machine Library
description: The Machine Library API lets you create, list, retrieve, update, and delete machine library.
---

# Machine Library

The Machine Library API lets you create, list, retrieve, update, and delete machine library.

> **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](../authentication.md)).

## 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; `limit` caps 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**

<CodeTabs>

```bash title="cURL"
curl -X GET https://api.arcnm.io/api/v1/machine-library \
  -H "X-API-Key: $ARCNM_API_KEY"
```

```python title="Python"
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())
```

```typescript title="TypeScript"
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()
```

</CodeTabs>

**Responses**

| Status | Description |
| --- | --- |
| `200` | Successful Response |
| `422` | Validation Error |

**Errors**

Standard error responses — see the [Errors catalog](../errors.md) 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` \| `saw.band` \| `saw.circular` | 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**

<CodeTabs>

```bash title="cURL"
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,
      "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",
      "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,
      "positioning_accuracy_mm": 0.01,
      "rapid_traverse_m_per_min": 24,
      "repeatability_mm": 0.005,
      "saw_blade_cost_eur": 0,
      "saw_blade_life_mm2": 0,
      "saw_blade_type": "bimetal",
      "saw_kerf_mm": 0,
      "schema_version": "1.0.0",
      "spindle_power_kw": 0,
      "subclass": "small",
      "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,
        "space_eur_per_m2_y": 0,
        "tooling_eur_per_year": 0
      },
      "workholding": [
        "vise.3jaw"
      ]
    },
    "valid_from": "2026-06-01"
  }'
```

```python title="Python"
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,
            "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",
            "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,
            "positioning_accuracy_mm": 0.01,
            "rapid_traverse_m_per_min": 24,
            "repeatability_mm": 0.005,
            "saw_blade_cost_eur": 0,
            "saw_blade_life_mm2": 0,
            "saw_blade_type": "bimetal",
            "saw_kerf_mm": 0,
            "schema_version": "1.0.0",
            "spindle_power_kw": 0,
            "subclass": "small",
            "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,
                "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())
```

```typescript title="TypeScript"
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,
      "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",
      "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,
      "positioning_accuracy_mm": 0.01,
      "rapid_traverse_m_per_min": 24,
      "repeatability_mm": 0.005,
      "saw_blade_cost_eur": 0,
      "saw_blade_life_mm2": 0,
      "saw_blade_type": "bimetal",
      "saw_kerf_mm": 0,
      "schema_version": "1.0.0",
      "spindle_power_kw": 0,
      "subclass": "small",
      "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,
        "space_eur_per_m2_y": 0,
        "tooling_eur_per_year": 0
      },
      "workholding": [
        "vise.3jaw"
      ]
    },
    "valid_from": "2026-06-01"
  }),
})
const data = await resp.json()
```

</CodeTabs>

**Responses**

| Status | Description |
| --- | --- |
| `201` | Successful Response |
| `422` | Validation Error |

**Errors**

Standard error responses — see the [Errors catalog](../errors.md) 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, 'tenant' 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**

```json
{
  "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**

<CodeTabs>

```bash title="cURL"
curl -X DELETE https://api.arcnm.io/api/v1/machine-library/{entry_id} \
  -H "X-API-Key: $ARCNM_API_KEY"
```

```python title="Python"
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())
```

```typescript title="TypeScript"
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()
```

</CodeTabs>

**Responses**

| Status | Description |
| --- | --- |
| `200` | Successful Response |
| `422` | Validation Error |

**Errors**

Standard error responses — see the [Errors catalog](../errors.md) 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**

```json
{
  "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**

<CodeTabs>

```bash title="cURL"
curl -X GET https://api.arcnm.io/api/v1/machine-library/{entry_id} \
  -H "X-API-Key: $ARCNM_API_KEY"
```

```python title="Python"
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())
```

```typescript title="TypeScript"
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()
```

</CodeTabs>

**Responses**

| Status | Description |
| --- | --- |
| `200` | Successful Response |
| `422` | Validation Error |

**Errors**

Standard error responses — see the [Errors catalog](../errors.md) 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, 'tenant' 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**

```json
{
  "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}`

**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**

<CodeTabs>

```bash title="cURL"
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"
  }'
```

```python title="Python"
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())
```

```typescript title="TypeScript"
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()
```

</CodeTabs>

**Responses**

| Status | Description |
| --- | --- |
| `200` | Successful Response |
| `422` | Validation Error |

**Errors**

Standard error responses — see the [Errors catalog](../errors.md) 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, 'tenant' 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**

```json
{
  "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 (replacement) cost, in EUR. |
| `annual_hours_T_G` | number | yes | VDI 3258 total annual attendance hours (T_G), in hours per year. |
| `annual_hours_T_IH` | number | yes | VDI 3258 annual maintenance/repair hours (T_IH), in hours per year. |
| `annual_hours_T_ST` | number | yes | VDI 3258 annual standstill (idle) hours (T_ST), in hours per year. |
| `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). |
| `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 perishable-tooling spend for the machine, in EUR per year. |

**Request**

<CodeTabs>

```bash title="cURL"
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_G": 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
  }'
```

```python title="Python"
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_G": 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())
```

```typescript title="TypeScript"
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_G": 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()
```

</CodeTabs>

**Responses**

| Status | Description |
| --- | --- |
| `200` | Successful Response |
| `422` | Validation Error |

**Errors**

Standard error responses — see the [Errors catalog](../errors.md) 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**

```json
{
  "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**

<CodeTabs>

```bash title="cURL"
curl -X GET https://api.arcnm.io/api/v1/machine-library/usage \
  -H "X-API-Key: $ARCNM_API_KEY"
```

```python title="Python"
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())
```

```typescript title="TypeScript"
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()
```

</CodeTabs>

**Responses**

| Status | Description |
| --- | --- |
| `200` | Successful Response |
| `422` | Validation Error |

**Errors**

Standard error responses — see the [Errors catalog](../errors.md) 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. |

