ARCNM

API reference

Tools

The Tools API manages your organisation's cutting-tool library: create, list, fetch, update, and delete tools, set per-material cutting data on each, and…

The Tools API manages your organisation's cutting-tool library: create, list, fetch, update, and delete tools, set per-material cutting data on each, and browse the tool-type catalogue.

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

List Tools

GET /api/v1/tools

Your organization's tool library, name-ordered, with each tool's per-material cutting data inlined.

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

Parameters

Name In Type Required Description
cursor query string no Opaque position token from the previous page's next_cursor (or the Link / X-Next-Cursor response header). Omit it for the first page. Keep every other query parameter identical for the whole walk — a cursor replayed against different filters is rejected.
limit query integer no Maximum rows to return in one page.
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/tools \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

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

Responses

Status Description
200 Successful Response
422 Validation Error

Errors

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

Status Code When
401 invalid_api_key Missing, malformed, or revoked API key.
403 insufficient_scope The key is valid but lacks a scope this endpoint requires.
429 rate_limited Per-key or per-org rate limit exceeded — back off with jitter and retry.

Create Tool

POST /api/v1/tools

Add a tool to your organization's library, optionally with its per-material cutting data.

Request body (application/json)

Field Type Required Description
attach_to_environment_ids string[] no Costing environments the new tool joins (D7 — membership is always explicit). Omit for the documented default: every current environment of your organization. Send [] to attach nowhere (the tool exists in the crib but prices nothing until attached).
coating string no Coating designation (e.g. TiAlN), if any.
cutting_material string no Cutting-material class (HSS, carbide, coated carbide, ...).
diameter_mm number no Cutting diameter, in millimetres (null for turning inserts).
item_no string no Vendor item / order number.
kind end_mill | face_mill | drill | spot_drill | tap | reamer | boring_bar | thread_mill | insert_holder_turn | grooving_tool | grinding_wheel | chamfer_mill | saw_blade | edm_wire | press_brake_punch | press_brake_die | tube_bend_die | plasma_consumable_set | laser_nozzle | waterjet_nozzle | punch_die_set | weld_consumable | broach yes Tool kind (VDI 2852 class), e.g. end_mill or drill.
name string yes Human-readable name of the tool.
notes string no Free-form shop notes.
parameters CuttingToolParameterWrite[] no Optional cutting-data rows to create with the tool — one row per parameter, keyed (material, operation, size, quality).
price_eur number no Replacement price, in EUR.
spec object no Kind-specific properties — e.g. a press-brake die's V opening and load rating, a saw blade's kerf and pitch, a plasma consumable set's amperage. Validated per kind; empty for rotating cutters, whose geometry uses the fields above.
teeth integer no Number of cutting edges / flutes / blade teeth.
tool_life_min number no Expected tool life in cutting minutes (per edge set).
vendor string no Tool manufacturer / vendor.

Request

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

resp = requests.post(
    "https://api.arcnm.io/api/v1/tools",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "name": "string",
        "kind": "end_mill"
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/tools", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "name": "string",
    "kind": "end_mill"
  }),
})
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
baseline ToolBaselineMembership
catalogue_item_id string
coating string Coating designation (e.g. TiAlN), if any.
created_at string
cutting_data_count integer Number of cutting-data rows this tool prices with: the values stated on the tool itself plus its catalogue article's data sheet. 0 means the tool carries none of its own and pricing resolves from the platform's cutting-data tables.
cutting_material string Cutting-material class (HSS, carbide, coated carbide, ...).
diameter_mm number Cutting diameter, in millimetres (null for turning inserts).
differs_from_article boolean
hidden boolean
id string
item_no string Vendor item / order number.
kind end_mill | face_mill | drill | spot_drill | tap | reamer | boring_bar | thread_mill | insert_holder_turn | grooving_tool | grinding_wheel | chamfer_mill | saw_blade | edm_wire | press_brake_punch | press_brake_die | tube_bend_die | plasma_consumable_set | laser_nozzle | waterjet_nozzle | punch_die_set | weld_consumable | broach Tool kind (VDI 2852 class), e.g. end_mill or drill.
name string Human-readable name of the tool.
notes string Free-form shop notes.
parameters CuttingToolParameterPublic[]
price_eur number Replacement price, in EUR.
price_is_inherited boolean
priced_into_quotes boolean Whether a price and a life stated on this tool can reach a quote. False for kinds no pricing path consumes yet (welding and EDM consumables, brake punches, tube-bend dies): the tool is still storable, curatable and attachable to an environment, but its economics change no cost today. Laser and waterjet nozzles, plasma sets and punch sets are priced per pierce, minute or stroke.
spec object Kind-specific properties — e.g. a press-brake die's V opening and load rating, a saw blade's kerf and pitch, a plasma consumable set's amperage. Validated per kind; empty for rotating cutters, whose geometry uses the fields above.
teeth integer Number of cutting edges / flutes / blade teeth.
tool_life_min number Expected tool life in cutting minutes (per edge set).
updated_at string
valid_from string
valid_to string
vendor string Tool manufacturer / vendor.

Example response

{
  "baseline": {
    "fits_machine_names": [
      "string"
    ],
    "fits_machines": true,
    "is_enabled": true,
    "membership_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  },
  "catalogue_item_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "coating": "string",
  "created_at": "2026-06-01T12:00:00Z",
  "cutting_data_count": 0,
  "cutting_material": "carbide",
  "diameter_mm": 0,
  "differs_from_article": false,
  "hidden": false,
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "item_no": "string",
  "kind": "end_mill",
  "name": "string",
  "notes": "string",
  "parameters": [],
  "price_eur": 0,
  "price_is_inherited": false,
  "priced_into_quotes": true,
  "spec": {},
  "teeth": 0,
  "tool_life_min": 0,
  "updated_at": "2026-06-01T12:00:00Z",
  "valid_from": "2026-06-01",
  "valid_to": "2026-06-01",
  "vendor": "string"
}

Delete Tool

DELETE /api/v1/tools/{tool_id}

Retire a tool (SCD2, 0141): the row closes its validity window so a replay of an old calculation still sees it, and its active environment memberships close with it. A tool created and retired the same day is hard-deleted (a zero-length window would still read as active today). Environment cutting-data overrides already applied from it are independent statements of shop practice and stay in force.

Parameters

Name In Type Required Description
tool_id path string yes Identifier of the tool.

Request

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

resp = requests.delete(
    "https://api.arcnm.io/api/v1/tools/{tool_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/tools/{tool_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 Tool

GET /api/v1/tools/{tool_id}

Fetch one tool with its per-material cutting data.

Parameters

Name In Type Required Description
tool_id path string yes Identifier of the tool.

Request

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

resp = requests.get(
    "https://api.arcnm.io/api/v1/tools/{tool_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/tools/{tool_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
baseline ToolBaselineMembership
catalogue_item_id string
coating string Coating designation (e.g. TiAlN), if any.
created_at string
cutting_data_count integer Number of cutting-data rows this tool prices with: the values stated on the tool itself plus its catalogue article's data sheet. 0 means the tool carries none of its own and pricing resolves from the platform's cutting-data tables.
cutting_material string Cutting-material class (HSS, carbide, coated carbide, ...).
diameter_mm number Cutting diameter, in millimetres (null for turning inserts).
differs_from_article boolean
hidden boolean
id string
item_no string Vendor item / order number.
kind end_mill | face_mill | drill | spot_drill | tap | reamer | boring_bar | thread_mill | insert_holder_turn | grooving_tool | grinding_wheel | chamfer_mill | saw_blade | edm_wire | press_brake_punch | press_brake_die | tube_bend_die | plasma_consumable_set | laser_nozzle | waterjet_nozzle | punch_die_set | weld_consumable | broach Tool kind (VDI 2852 class), e.g. end_mill or drill.
name string Human-readable name of the tool.
notes string Free-form shop notes.
parameters CuttingToolParameterPublic[]
price_eur number Replacement price, in EUR.
price_is_inherited boolean
priced_into_quotes boolean Whether a price and a life stated on this tool can reach a quote. False for kinds no pricing path consumes yet (welding and EDM consumables, brake punches, tube-bend dies): the tool is still storable, curatable and attachable to an environment, but its economics change no cost today. Laser and waterjet nozzles, plasma sets and punch sets are priced per pierce, minute or stroke.
spec object Kind-specific properties — e.g. a press-brake die's V opening and load rating, a saw blade's kerf and pitch, a plasma consumable set's amperage. Validated per kind; empty for rotating cutters, whose geometry uses the fields above.
teeth integer Number of cutting edges / flutes / blade teeth.
tool_life_min number Expected tool life in cutting minutes (per edge set).
updated_at string
valid_from string
valid_to string
vendor string Tool manufacturer / vendor.

Example response

{
  "baseline": {
    "fits_machine_names": [
      "string"
    ],
    "fits_machines": true,
    "is_enabled": true,
    "membership_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  },
  "catalogue_item_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "coating": "string",
  "created_at": "2026-06-01T12:00:00Z",
  "cutting_data_count": 0,
  "cutting_material": "carbide",
  "diameter_mm": 0,
  "differs_from_article": false,
  "hidden": false,
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "item_no": "string",
  "kind": "end_mill",
  "name": "string",
  "notes": "string",
  "parameters": [],
  "price_eur": 0,
  "price_is_inherited": false,
  "priced_into_quotes": true,
  "spec": {},
  "teeth": 0,
  "tool_life_min": 0,
  "updated_at": "2026-06-01T12:00:00Z",
  "valid_from": "2026-06-01",
  "valid_to": "2026-06-01",
  "vendor": "string"
}

Update Tool

PATCH /api/v1/tools/{tool_id}

Edit a tool's identity / geometry / economics fields.

Every field but the bookkeeping ones (vendor, item number, coating, notes) can change what this tool costs you, so editing one takes effect immediately in every costing environment the tool is attached to.

Parameters

Name In Type Required Description
tool_id path string yes Identifier of the tool.

Request body (application/json)

Field Type Required Description
coating string no
cutting_material string no
diameter_mm number no
if_unmodified_since string no Optimistic concurrency. Send the updated_at you last read; the write is refused with 409 if the tool changed since. Omit to write unconditionally (last write wins).
item_no string no
kind end_mill | face_mill | drill | spot_drill | tap | reamer | boring_bar | thread_mill | insert_holder_turn | grooving_tool | grinding_wheel | chamfer_mill | saw_blade | edm_wire | press_brake_punch | press_brake_die | tube_bend_die | plasma_consumable_set | laser_nozzle | waterjet_nozzle | punch_die_set | weld_consumable | broach no
name string no
notes string no
price_eur number no
spec object no Kind-specific properties; validated against the (possibly changed) kind. When you change a tool's kind, send the spec for the NEW kind in the same request — the old kind's spec does not carry over.
teeth integer no
tool_life_min number no
vendor string no

Request

curl -X PATCH https://api.arcnm.io/api/v1/tools/{tool_id} \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "coating": "string",
    "cutting_material": "string",
    "diameter_mm": 0,
    "if_unmodified_since": "2026-06-01T12:00:00Z",
    "item_no": "string",
    "kind": "end_mill",
    "name": "string",
    "notes": "string",
    "price_eur": 0,
    "spec": {},
    "teeth": 0,
    "tool_life_min": 0,
    "vendor": "string"
  }'
import requests

resp = requests.patch(
    "https://api.arcnm.io/api/v1/tools/{tool_id}",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "coating": "string",
        "cutting_material": "string",
        "diameter_mm": 0,
        "if_unmodified_since": "2026-06-01T12:00:00Z",
        "item_no": "string",
        "kind": "end_mill",
        "name": "string",
        "notes": "string",
        "price_eur": 0,
        "spec": {},
        "teeth": 0,
        "tool_life_min": 0,
        "vendor": "string"
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/tools/{tool_id}", {
  method: "PATCH",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "coating": "string",
    "cutting_material": "string",
    "diameter_mm": 0,
    "if_unmodified_since": "2026-06-01T12:00:00Z",
    "item_no": "string",
    "kind": "end_mill",
    "name": "string",
    "notes": "string",
    "price_eur": 0,
    "spec": {},
    "teeth": 0,
    "tool_life_min": 0,
    "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
baseline ToolBaselineMembership
catalogue_item_id string
coating string Coating designation (e.g. TiAlN), if any.
created_at string
cutting_data_count integer Number of cutting-data rows this tool prices with: the values stated on the tool itself plus its catalogue article's data sheet. 0 means the tool carries none of its own and pricing resolves from the platform's cutting-data tables.
cutting_material string Cutting-material class (HSS, carbide, coated carbide, ...).
diameter_mm number Cutting diameter, in millimetres (null for turning inserts).
differs_from_article boolean
hidden boolean
id string
item_no string Vendor item / order number.
kind end_mill | face_mill | drill | spot_drill | tap | reamer | boring_bar | thread_mill | insert_holder_turn | grooving_tool | grinding_wheel | chamfer_mill | saw_blade | edm_wire | press_brake_punch | press_brake_die | tube_bend_die | plasma_consumable_set | laser_nozzle | waterjet_nozzle | punch_die_set | weld_consumable | broach Tool kind (VDI 2852 class), e.g. end_mill or drill.
name string Human-readable name of the tool.
notes string Free-form shop notes.
parameters CuttingToolParameterPublic[]
price_eur number Replacement price, in EUR.
price_is_inherited boolean
priced_into_quotes boolean Whether a price and a life stated on this tool can reach a quote. False for kinds no pricing path consumes yet (welding and EDM consumables, brake punches, tube-bend dies): the tool is still storable, curatable and attachable to an environment, but its economics change no cost today. Laser and waterjet nozzles, plasma sets and punch sets are priced per pierce, minute or stroke.
spec object Kind-specific properties — e.g. a press-brake die's V opening and load rating, a saw blade's kerf and pitch, a plasma consumable set's amperage. Validated per kind; empty for rotating cutters, whose geometry uses the fields above.
teeth integer Number of cutting edges / flutes / blade teeth.
tool_life_min number Expected tool life in cutting minutes (per edge set).
updated_at string
valid_from string
valid_to string
vendor string Tool manufacturer / vendor.

Example response

{
  "baseline": {
    "fits_machine_names": [
      "string"
    ],
    "fits_machines": true,
    "is_enabled": true,
    "membership_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  },
  "catalogue_item_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "coating": "string",
  "created_at": "2026-06-01T12:00:00Z",
  "cutting_data_count": 0,
  "cutting_material": "carbide",
  "diameter_mm": 0,
  "differs_from_article": false,
  "hidden": false,
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "item_no": "string",
  "kind": "end_mill",
  "name": "string",
  "notes": "string",
  "parameters": [],
  "price_eur": 0,
  "price_is_inherited": false,
  "priced_into_quotes": true,
  "spec": {},
  "teeth": 0,
  "tool_life_min": 0,
  "updated_at": "2026-06-01T12:00:00Z",
  "valid_from": "2026-06-01",
  "valid_to": "2026-06-01",
  "vendor": "string"
}

Copy Tool

POST /api/v1/tools/{tool_id}/copy

Duplicate one of your own tools into a new tool of your own.

The copy takes the source's geometry, economics, kind-specific properties and cutting data, under a name of its own — " (copy)". The source is untouched.

The copy is your tool, not a platform one: you can delete it, and it is not part of the platform set, so hiding a platform article or restoring the platform set never touches it. It also joins no costing environment on its own — attach it where you want it to price with POST /environments/{env_id}/tools. To install a platform article instead, use POST /tools/catalogue-items/{item_id}/adopt: an adopted tool keeps its link to the article, and this one does not.

Parameters

Name In Type Required Description
tool_id path string yes Identifier of the tool.

Request

curl -X POST https://api.arcnm.io/api/v1/tools/{tool_id}/copy \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/tools/{tool_id}/copy",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/tools/{tool_id}/copy", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
  },
})
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
baseline ToolBaselineMembership
catalogue_item_id string
coating string Coating designation (e.g. TiAlN), if any.
created_at string
cutting_data_count integer Number of cutting-data rows this tool prices with: the values stated on the tool itself plus its catalogue article's data sheet. 0 means the tool carries none of its own and pricing resolves from the platform's cutting-data tables.
cutting_material string Cutting-material class (HSS, carbide, coated carbide, ...).
diameter_mm number Cutting diameter, in millimetres (null for turning inserts).
differs_from_article boolean
hidden boolean
id string
item_no string Vendor item / order number.
kind end_mill | face_mill | drill | spot_drill | tap | reamer | boring_bar | thread_mill | insert_holder_turn | grooving_tool | grinding_wheel | chamfer_mill | saw_blade | edm_wire | press_brake_punch | press_brake_die | tube_bend_die | plasma_consumable_set | laser_nozzle | waterjet_nozzle | punch_die_set | weld_consumable | broach Tool kind (VDI 2852 class), e.g. end_mill or drill.
name string Human-readable name of the tool.
notes string Free-form shop notes.
parameters CuttingToolParameterPublic[]
price_eur number Replacement price, in EUR.
price_is_inherited boolean
priced_into_quotes boolean Whether a price and a life stated on this tool can reach a quote. False for kinds no pricing path consumes yet (welding and EDM consumables, brake punches, tube-bend dies): the tool is still storable, curatable and attachable to an environment, but its economics change no cost today. Laser and waterjet nozzles, plasma sets and punch sets are priced per pierce, minute or stroke.
spec object Kind-specific properties — e.g. a press-brake die's V opening and load rating, a saw blade's kerf and pitch, a plasma consumable set's amperage. Validated per kind; empty for rotating cutters, whose geometry uses the fields above.
teeth integer Number of cutting edges / flutes / blade teeth.
tool_life_min number Expected tool life in cutting minutes (per edge set).
updated_at string
valid_from string
valid_to string
vendor string Tool manufacturer / vendor.

Example response

{
  "baseline": {
    "fits_machine_names": [
      "string"
    ],
    "fits_machines": true,
    "is_enabled": true,
    "membership_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  },
  "catalogue_item_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "coating": "string",
  "created_at": "2026-06-01T12:00:00Z",
  "cutting_data_count": 0,
  "cutting_material": "carbide",
  "diameter_mm": 0,
  "differs_from_article": false,
  "hidden": false,
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "item_no": "string",
  "kind": "end_mill",
  "name": "string",
  "notes": "string",
  "parameters": [],
  "price_eur": 0,
  "price_is_inherited": false,
  "priced_into_quotes": true,
  "spec": {},
  "teeth": 0,
  "tool_life_min": 0,
  "updated_at": "2026-06-01T12:00:00Z",
  "valid_from": "2026-06-01",
  "valid_to": "2026-06-01",
  "vendor": "string"
}

Get Effective Cutting Data

GET /api/v1/tools/{tool_id}/effective-cutting-data

The cutting data this tool actually prices with in an environment (the default one when none is named), per material bin and parameter, with the tier that supplies each value.

In the app, nothing is hidden: a tool with no rows of its own still shows the platform's numbers, so the dialog is never an empty table you are expected to fill in. An API key or an MCP grant is an integration, and gets what the environment cutting-data read gives one: the entries the shop stated themselves, without the platform column.

The table is stated per material bin, and that is the one limit worth naming. Where this tool carries a value of its own, the grade's ISO 513 workpiece group is not consulted at all — that value answers for every grade on the bin. Where it carries none, the platform figure shown here is the material bin's, while a part on a grade mapped to a workpiece group prices on that group's cutting speed, which can differ. The environment's cutting data is where those per-group speeds are read and set.

Parameters

Name In Type Required Description
tool_id path string yes Identifier of the tool.
env_id query string no Identifier of the env.

Request

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

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

Responses

Status Description
200 Successful Response
422 Validation Error

Errors

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

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

Response body 200

Field Type Description
env_id string
own_rows_priced boolean Whether a row stated on THIS tool reaches pricing. False for a kind whose handler resolves its process bin without the tool tier (insert holders, boring bars, thread mills, waterjet nozzles): the rows are reference data and every value below is what the engine actually prices with.
process string The process family whose bins this tool prices through; null for reference-only kinds (a tap prices from the milling reference, a reamer from the drilling one).
reference_only boolean
rows EffectiveCuttingRowPublic[]
tool_id string

Example response

{
  "env_id": "string",
  "own_rows_priced": false,
  "process": "string",
  "reference_only": true,
  "rows": [
    {
      "material_code": "string",
      "material_label": "string",
      "parameter": "string",
      "process": "string",
      "size_mm": 0,
      "source": "string",
      "unit": "string",
      "value": 0
    }
  ],
  "tool_id": "string"
}

Upsert Tool Parameters

PUT /api/v1/tools/{tool_id}/parameters

Create-or-replace cutting-data rows on one of YOUR tools (D1 key: material × operation × size × quality × parameter). Vendor rows live on the catalogue ARTICLE and are read-only here; your instance rows shadow them on the same key at pricing time.

Parameters

Name In Type Required Description
tool_id path string yes Identifier of the tool.

Request

curl -X PUT https://api.arcnm.io/api/v1/tools/{tool_id}/parameters \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

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

Delete Tool Parameter

DELETE /api/v1/tools/{tool_id}/parameters/{parameter_id}

Remove one cutting-data row from one of your tools.

Parameters

Name In Type Required Description
tool_id path string yes Identifier of the tool.
parameter_id path string yes Identifier of the parameter.

Request

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

resp = requests.delete(
    "https://api.arcnm.io/api/v1/tools/{tool_id}/parameters/{parameter_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/tools/{tool_id}/parameters/{parameter_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"
}

Reset Tool To Article

POST /api/v1/tools/{tool_id}/reset

Reset a platform-lineage tool to its article's values ("Reset to platform values"): geometry, economics, cutting material, spec, name and notes come back from the article; the tool's own cutting-data rows and its environment memberships are untouched. A custom tool (no article) has nothing to reset to → 409.

Parameters

Name In Type Required Description
tool_id path string yes Identifier of the tool.

Request

curl -X POST https://api.arcnm.io/api/v1/tools/{tool_id}/reset \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

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

Responses

Status Description
200 Successful Response
422 Validation Error

Errors

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

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

Response body 200

Field Type Description
baseline ToolBaselineMembership
catalogue_item_id string
coating string Coating designation (e.g. TiAlN), if any.
created_at string
cutting_data_count integer Number of cutting-data rows this tool prices with: the values stated on the tool itself plus its catalogue article's data sheet. 0 means the tool carries none of its own and pricing resolves from the platform's cutting-data tables.
cutting_material string Cutting-material class (HSS, carbide, coated carbide, ...).
diameter_mm number Cutting diameter, in millimetres (null for turning inserts).
differs_from_article boolean
hidden boolean
id string
item_no string Vendor item / order number.
kind end_mill | face_mill | drill | spot_drill | tap | reamer | boring_bar | thread_mill | insert_holder_turn | grooving_tool | grinding_wheel | chamfer_mill | saw_blade | edm_wire | press_brake_punch | press_brake_die | tube_bend_die | plasma_consumable_set | laser_nozzle | waterjet_nozzle | punch_die_set | weld_consumable | broach Tool kind (VDI 2852 class), e.g. end_mill or drill.
name string Human-readable name of the tool.
notes string Free-form shop notes.
parameters CuttingToolParameterPublic[]
price_eur number Replacement price, in EUR.
price_is_inherited boolean
priced_into_quotes boolean Whether a price and a life stated on this tool can reach a quote. False for kinds no pricing path consumes yet (welding and EDM consumables, brake punches, tube-bend dies): the tool is still storable, curatable and attachable to an environment, but its economics change no cost today. Laser and waterjet nozzles, plasma sets and punch sets are priced per pierce, minute or stroke.
spec object Kind-specific properties — e.g. a press-brake die's V opening and load rating, a saw blade's kerf and pitch, a plasma consumable set's amperage. Validated per kind; empty for rotating cutters, whose geometry uses the fields above.
teeth integer Number of cutting edges / flutes / blade teeth.
tool_life_min number Expected tool life in cutting minutes (per edge set).
updated_at string
valid_from string
valid_to string
vendor string Tool manufacturer / vendor.

Example response

{
  "baseline": {
    "fits_machine_names": [
      "string"
    ],
    "fits_machines": true,
    "is_enabled": true,
    "membership_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  },
  "catalogue_item_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "coating": "string",
  "created_at": "2026-06-01T12:00:00Z",
  "cutting_data_count": 0,
  "cutting_material": "carbide",
  "diameter_mm": 0,
  "differs_from_article": false,
  "hidden": false,
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "item_no": "string",
  "kind": "end_mill",
  "name": "string",
  "notes": "string",
  "parameters": [],
  "price_eur": 0,
  "price_is_inherited": false,
  "priced_into_quotes": true,
  "spec": {},
  "teeth": 0,
  "tool_life_min": 0,
  "updated_at": "2026-06-01T12:00:00Z",
  "valid_from": "2026-06-01",
  "valid_to": "2026-06-01",
  "vendor": "string"
}

Tool Catalogue

GET /api/v1/tools/catalogue

The closed vocabulary for tools and cutting data — tool kinds, cutting materials, canonical material bins and parameter windows.

Request

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

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

Response body 200

Field Type Description
cutting_materials string[] Accepted cutting-material classes.
materials ToolCatalogueMaterial[] Canonical material bins cutting data can target.
parameters ToolCatalogueParameter[] Cutting parameters with units and accepted windows.
process_parameters object Per process family: which parameters its pricing bins use.
spec_schemas object Per tool kind: the JSON schema of its kind-specific spec — required fields, bounds and the wear basis. The spec form renders from these; no per-kind client code.
tool_kind_operations object Per tool kind: the operations a cutting-data row on it may be scoped to with operation. A row scoped to any other operation is refused (422) — no handler would read it.
tool_kind_process object Per tool kind: the process family whose pricing bins hold that tool's own cutting data, and which its data can therefore be applied to. Kinds absent from this map are reference-only — pricing derives their values from another tool's bin (a tap from the milling reference, a reamer from the drilling one) or has no cutting-parameter bin at all (grinding).
tool_kinds string[] Accepted tool kinds (VDI 2852).

Example response

{
  "cutting_materials": [
    "string"
  ],
  "materials": [
    {
      "axis": "bin",
      "code": "string",
      "iso_group": "string",
      "label": "string"
    }
  ],
  "parameters": [
    {
      "key": "string",
      "max": 0,
      "min": 0,
      "unit": "string"
    }
  ],
  "process_parameters": {},
  "spec_schemas": {},
  "tool_kind_operations": {},
  "tool_kind_process": {},
  "tool_kinds": [
    "string"
  ]
}

List Catalogue Items

GET /api/v1/tools/catalogue-items

The tool catalogue — platform articles plus your own type definitions (0141 split; the machine-library sibling).

Every article stays visible after adoption: adopted_tool_id names your crib instance so the UI can show "in your crib" and a diff-vs-article, instead of silently hiding adopted presets. suppressed marks articles you chose to hide (a per-org preference, never a delete — platform rows are read-only at the database).

Request

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

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

Adopt Catalogue Item

POST /api/v1/tools/catalogue-items/{item_id}/adopt

Adopt an article into your crib — the machine-library instantiate move: the instance is yours to rename, re-spec and price; the article stays untouched and catalogue_item_id records the lineage.

Adopting an article you already have returns the tool you have — never a second copy, and never renamed — and still attaches it to the environments you name.

Parameters

Name In Type Required Description
item_id path string yes Identifier of the item.

Request body (application/json)

Field Type Required Description
attach_to_environment_ids string[] no Costing environments the adopted tool joins. Omit to let a newly created instance join every current environment; [] to attach nowhere. An environment you do not own is not found. If you already own this article, the environments you name here are joined and every other one is left as it is — adopting again never puts a tool back into an environment you took it out of.
name string no Name for your instance, used only when this call creates it; defaults to the article's. If you already own this article, adopting returns the tool you have and leaves its name alone — a name you chose is never overwritten. Rename it with PATCH /tools/{tool_id}.

Request

curl -X POST https://api.arcnm.io/api/v1/tools/catalogue-items/{item_id}/adopt \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "attach_to_environment_ids": [
      "3fa85f64-5717-4562-b3fc-2c963f66afa6"
    ],
    "name": "string"
  }'
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/tools/catalogue-items/{item_id}/adopt",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "attach_to_environment_ids": [
            "3fa85f64-5717-4562-b3fc-2c963f66afa6"
        ],
        "name": "string"
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/tools/catalogue-items/{item_id}/adopt", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "attach_to_environment_ids": [
      "3fa85f64-5717-4562-b3fc-2c963f66afa6"
    ],
    "name": "string"
  }),
})
const data = await resp.json()

Responses

Status Description
201 Successful Response
422 Validation Error

Errors

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

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

Response body 201

Field Type Description
baseline ToolBaselineMembership
catalogue_item_id string
coating string Coating designation (e.g. TiAlN), if any.
created_at string
cutting_data_count integer Number of cutting-data rows this tool prices with: the values stated on the tool itself plus its catalogue article's data sheet. 0 means the tool carries none of its own and pricing resolves from the platform's cutting-data tables.
cutting_material string Cutting-material class (HSS, carbide, coated carbide, ...).
diameter_mm number Cutting diameter, in millimetres (null for turning inserts).
differs_from_article boolean
hidden boolean
id string
item_no string Vendor item / order number.
kind end_mill | face_mill | drill | spot_drill | tap | reamer | boring_bar | thread_mill | insert_holder_turn | grooving_tool | grinding_wheel | chamfer_mill | saw_blade | edm_wire | press_brake_punch | press_brake_die | tube_bend_die | plasma_consumable_set | laser_nozzle | waterjet_nozzle | punch_die_set | weld_consumable | broach Tool kind (VDI 2852 class), e.g. end_mill or drill.
name string Human-readable name of the tool.
notes string Free-form shop notes.
parameters CuttingToolParameterPublic[]
price_eur number Replacement price, in EUR.
price_is_inherited boolean
priced_into_quotes boolean Whether a price and a life stated on this tool can reach a quote. False for kinds no pricing path consumes yet (welding and EDM consumables, brake punches, tube-bend dies): the tool is still storable, curatable and attachable to an environment, but its economics change no cost today. Laser and waterjet nozzles, plasma sets and punch sets are priced per pierce, minute or stroke.
spec object Kind-specific properties — e.g. a press-brake die's V opening and load rating, a saw blade's kerf and pitch, a plasma consumable set's amperage. Validated per kind; empty for rotating cutters, whose geometry uses the fields above.
teeth integer Number of cutting edges / flutes / blade teeth.
tool_life_min number Expected tool life in cutting minutes (per edge set).
updated_at string
valid_from string
valid_to string
vendor string Tool manufacturer / vendor.

Example response

{
  "baseline": {
    "fits_machine_names": [
      "string"
    ],
    "fits_machines": true,
    "is_enabled": true,
    "membership_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  },
  "catalogue_item_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "coating": "string",
  "created_at": "2026-06-01T12:00:00Z",
  "cutting_data_count": 0,
  "cutting_material": "carbide",
  "diameter_mm": 0,
  "differs_from_article": false,
  "hidden": false,
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "item_no": "string",
  "kind": "end_mill",
  "name": "string",
  "notes": "string",
  "parameters": [],
  "price_eur": 0,
  "price_is_inherited": false,
  "priced_into_quotes": true,
  "spec": {},
  "teeth": 0,
  "tool_life_min": 0,
  "updated_at": "2026-06-01T12:00:00Z",
  "valid_from": "2026-06-01",
  "valid_to": "2026-06-01",
  "vendor": "string"
}

Unsuppress Catalogue Item

DELETE /api/v1/tools/catalogue-items/{item_id}/suppression

Show a platform tool-catalogue article again that you had hidden, so it is offered for selection once more.

Parameters

Name In Type Required Description
item_id path string yes Identifier of the item.

Request

curl -X DELETE https://api.arcnm.io/api/v1/tools/catalogue-items/{item_id}/suppression \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

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

Suppress Catalogue Item

POST /api/v1/tools/catalogue-items/{item_id}/suppression

Hide a platform article from your catalogue view — a per-org preference, not a delete (M5).

Parameters

Name In Type Required Description
item_id path string yes Identifier of the item.

Request body (application/json)

Field Type Required Description
reason string no

Request

curl -X POST https://api.arcnm.io/api/v1/tools/catalogue-items/{item_id}/suppression \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "reason": "string"
  }'
import requests

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

Responses

Status Description
201 Successful Response
422 Validation Error

Errors

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

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

Response body 201

Field Type Description
message string Human-readable result of the operation.

Example response

{
  "message": "string"
}