ARCNM

API reference

Environments

The Environments API manages costing environments.

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/parts/environments

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.
enabled_extractors string[] no Geometry and enrichment sources enabled for the environment.
name string yes Human-readable name for the new environment.
region string no Geographic region this environment prices for (e.g. DE, US).
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/parts/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/parts/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/parts/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.
enabled_extractors string[] Geometry and enrichment sources enabled for this environment.
id string Unique identifier of the costing environment.
is_baseline boolean Whether this is the auto-provisioned default environment for the tenant.
name string Human-readable name of the costing environment.
region string Geographic region this environment prices for (e.g. DE, US).
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",
  "enabled_extractors": [
    "string"
  ],
  "id": "string",
  "is_baseline": true,
  "name": "string",
  "region": "EU",
  "valid_from": "string",
  "valid_to": "string"
}

List Environments

GET /api/v1/parts/environments/

Paginated. Pass limit and offset to page through results.

Parameters

Name In Type Required Description
limit query integer no Maximum number of results to return.
offset query integer no Number of results to skip before the returned page.

Request

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

resp = requests.get(
    "https://api.arcnm.io/api/v1/parts/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/parts/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/parts/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/parts/environments/{env_id} \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.delete(
    "https://api.arcnm.io/api/v1/parts/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/parts/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/parts/environments/{env_id}

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

resp = requests.get(
    "https://api.arcnm.io/api/v1/parts/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/parts/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
attributes object Free-form key/value metadata attached to the environment.
currency string ISO 4217 currency code the environment's rates are denominated in.
description string Optional longer description of the environment.
enabled_extractors string[] Geometry and enrichment sources enabled for this environment.
id string Unique identifier of the costing environment.
is_baseline boolean Whether this is the auto-provisioned default environment for the tenant.
name string Human-readable name of the costing environment.
region string Geographic region this environment prices for (e.g. DE, US).
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

{
  "attributes": {},
  "currency": "EUR",
  "description": "string",
  "enabled_extractors": [
    "string"
  ],
  "id": "string",
  "is_baseline": true,
  "name": "string",
  "region": "EU",
  "valid_from": "string",
  "valid_to": "string"
}

Update Environment

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

Update the tenant-controllable extractor list on an environment.

The canonical geometry source (arcnm) is always retained.

Tenant-scoped: a 404 is returned for env ids belonging to other orgs so we never confirm cross-tenant existence.

Parameters

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

Request body (application/json)

Field Type Required Description
enabled_extractors string[] yes Geometry-extractor slugs to enable for this environment (at least one).

Request

curl -X PATCH https://api.arcnm.io/api/v1/parts/environments/{env_id} \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled_extractors": [
      "string"
    ]
  }'
import requests

resp = requests.patch(
    "https://api.arcnm.io/api/v1/parts/environments/{env_id}",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "enabled_extractors": [
            "string"
        ]
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/parts/environments/{env_id}", {
  method: "PATCH",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "enabled_extractors": [
      "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
currency string ISO 4217 currency code the environment's rates are denominated in.
description string Optional longer description of the environment.
enabled_extractors string[] Geometry and enrichment sources enabled for this environment.
id string Unique identifier of the costing environment.
is_baseline boolean Whether this is the auto-provisioned default environment for the tenant.
name string Human-readable name of the costing environment.
region string Geographic region this environment prices for (e.g. DE, US).
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",
  "enabled_extractors": [
    "string"
  ],
  "id": "string",
  "is_baseline": true,
  "name": "string",
  "region": "EU",
  "valid_from": "string",
  "valid_to": "string"
}

Update Environment Identity

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

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.
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/parts/environments/{env_id}/identity \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "currency": "EUR",
    "description": "string",
    "name": "string",
    "region": "EU",
    "valid_from": "2026-06-01",
    "valid_to": "2026-06-01"
  }'
import requests

resp = requests.put(
    "https://api.arcnm.io/api/v1/parts/environments/{env_id}/identity",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "currency": "EUR",
        "description": "string",
        "name": "string",
        "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/parts/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",
    "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.
enabled_extractors string[] Geometry and enrichment sources enabled for this environment.
id string Unique identifier of the costing environment.
is_baseline boolean Whether this is the auto-provisioned default environment for the tenant.
name string Human-readable name of the costing environment.
region string Geographic region this environment prices for (e.g. DE, US).
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",
  "enabled_extractors": [
    "string"
  ],
  "id": "string",
  "is_baseline": true,
  "name": "string",
  "region": "EU",
  "valid_from": "string",
  "valid_to": "string"
}

List Env Machines

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

Machines wired to this env, ordered by fleet_priority (lower = earlier candidate). Engine-agnostic — the same fleet feeds ARCNM today and is reused as-is by any future engine via a shared resolver.

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

resp = requests.get(
    "https://api.arcnm.io/api/v1/parts/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/parts/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/parts/environments/{env_id}/machines

Parameters

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

Request body (application/json)

Field Type Required Description
fleet_priority integer no Orders machines within the environment; lower values are tried first.
is_enabled boolean no Whether the machine is active in the environment's fleet on attach.
machine_id string no Identifier of an existing machine to attach; mutually exclusive with new_machine.
new_machine MachineDefinitionCreate no Inline definition of a new machine to create and attach; mutually exclusive with machine_id.
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/parts/environments/{env_id}/machines \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "fleet_priority": 100,
    "is_enabled": true,
    "machine_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "new_machine": {
      "burden_rate_eur": 0,
      "capabilities": {
        "axes_indexable": 0,
        "axes_simultaneous": 0,
        "certifications": [
          "string"
        ],
        "chatter_stability_lobe": {
          "rpm_to_max_axial_depth_mm": [
            {}
          ]
        },
        "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,
        "nominal_tool_change_time_s_by_class": {},
        "positioning_accuracy_mm": 0.01,
        "rapid_traverse_m_per_min": 24,
        "repeatability_mm": 0.005,
        "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"
        ]
      },
      "hourly_rate_eur": 0,
      "klass": "milling.3axis_vmc",
      "model_no": "string",
      "name": "string",
      "parent_template_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "subclass": "small",
      "valid_from": "2026-06-01",
      "valid_to": "2026-06-01",
      "vendor": "string"
    },
    "valid_from": "2026-06-01",
    "valid_to": "2026-06-01"
  }'
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/parts/environments/{env_id}/machines",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "fleet_priority": 100,
        "is_enabled": True,
        "machine_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "new_machine": {
            "burden_rate_eur": 0,
            "capabilities": {
                "axes_indexable": 0,
                "axes_simultaneous": 0,
                "certifications": [
                    "string"
                ],
                "chatter_stability_lobe": {
                    "rpm_to_max_axial_depth_mm": [
                        {}
                    ]
                },
                "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,
                "nominal_tool_change_time_s_by_class": {},
                "positioning_accuracy_mm": 0.01,
                "rapid_traverse_m_per_min": 24,
                "repeatability_mm": 0.005,
                "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"
                ]
            },
            "hourly_rate_eur": 0,
            "klass": "milling.3axis_vmc",
            "model_no": "string",
            "name": "string",
            "parent_template_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
            "subclass": "small",
            "valid_from": "2026-06-01",
            "valid_to": "2026-06-01",
            "vendor": "string"
        },
        "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/parts/environments/{env_id}/machines", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "fleet_priority": 100,
    "is_enabled": true,
    "machine_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "new_machine": {
      "burden_rate_eur": 0,
      "capabilities": {
        "axes_indexable": 0,
        "axes_simultaneous": 0,
        "certifications": [
          "string"
        ],
        "chatter_stability_lobe": {
          "rpm_to_max_axial_depth_mm": [
            {}
          ]
        },
        "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,
        "nominal_tool_change_time_s_by_class": {},
        "positioning_accuracy_mm": 0.01,
        "rapid_traverse_m_per_min": 24,
        "repeatability_mm": 0.005,
        "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"
        ]
      },
      "hourly_rate_eur": 0,
      "klass": "milling.3axis_vmc",
      "model_no": "string",
      "name": "string",
      "parent_template_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "subclass": "small",
      "valid_from": "2026-06-01",
      "valid_to": "2026-06-01",
      "vendor": "string"
    },
    "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
fleet_priority integer Orders machines within the environment; lower values are tried first.
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.
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

{
  "fleet_priority": 0,
  "is_enabled": true,
  "machine": {
    "burden_rate_eur": 0,
    "capabilities": {},
    "hourly_rate_eur": 0,
    "id": "string",
    "klass": "string",
    "model_no": "string",
    "name": "string",
    "subclass": "string",
    "valid_from": "string",
    "valid_to": "string",
    "vendor": "string"
  },
  "membership_id": "string",
  "valid_from": "string",
  "valid_to": "string"
}

Detach Machine

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

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

resp = requests.delete(
    "https://api.arcnm.io/api/v1/parts/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/parts/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/parts/environments/{env_id}/machines/{membership_id}

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/parts/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/parts/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/parts/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
fleet_priority integer Orders machines within the environment; lower values are tried first.
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.
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

{
  "fleet_priority": 0,
  "is_enabled": true,
  "machine": {
    "burden_rate_eur": 0,
    "capabilities": {},
    "hourly_rate_eur": 0,
    "id": "string",
    "klass": "string",
    "model_no": "string",
    "name": "string",
    "subclass": "string",
    "valid_from": "string",
    "valid_to": "string",
    "vendor": "string"
  },
  "membership_id": "string",
  "valid_from": "string",
  "valid_to": "string"
}

List Rates

GET /api/v1/parts/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, or fx).

Request

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

resp = requests.get(
    "https://api.arcnm.io/api/v1/parts/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/parts/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.

Upsert Rate

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

Create a rate row of the requested kind. Effective-dated: the new row's valid_from opens a window; the previous active row's valid_to should be capped by the caller.

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/parts/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/parts/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/parts/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

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.
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, or fx.
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",
  "env_id": "string",
  "fields": {},
  "id": "string",
  "kind": "string",
  "valid_from": "string",
  "valid_to": "string"
}

Delete Rate

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

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, or fx).
rate_id path string yes Identifier of the rate.

Request

curl -X DELETE https://api.arcnm.io/api/v1/parts/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/parts/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/parts/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

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"
}