ARCNM

Concepts

Materials & overrides

Resolve any material reference to a canonical grade, then override the catalogue per tenant so every quote lands on the metal you actually buy, at your price.

Material is one of the largest line items in a machined part, so every quote has to land on the grade you actually buy — not a generic "steel". ARCNM ships a canonical catalogue of material grades and a resolver that maps your references — Werkstoffnummer, AISI/SAE code, trade name, or your own SKU — onto it.

There are three layers:

  • The platform catalogue — a read-only set of canonical grades (1.4301, AISI 304, Ti-6Al-4V, …), each with a density, ISO machining group, and a stable URN. Shared by every tenant.
  • Your custom grades — grades you register for materials the catalogue doesn't carry. First-class rows with their own URN, density and ISO group: selectable, resolvable and priceable exactly like catalogue grades, visible only to your organization.
  • Your overrides — per-tenant rows that map an internal code or trade name onto a grade (canonical or custom).

Discover a grade

Resolve the material you mean before you quote. Three read paths, all on the parts:read scope.

Search the catalogue

GET /api/v1/materials/grades searches across primary code, display code, name, URN, and every alias — so AISI 304, SUS304, and S30400 all return 1.4301. Filter by category or iso_group, and page with limit / offset.

curl "https://api.arcnm.io/api/v1/materials/grades?q=AISI%20304&limit=5" \
  -H "X-API-Key: $ARCNM_API_KEY"
# → { "count": 1, "data": [ { "id": "…", "urn": "urn:material:din:1.4301",
#     "primary_code": "1.4301", "name": "X5CrNi18-10", "iso_machining_group": "P", … } ] }

Resolve a single reference

When you have one string from a drawing or an ERP export, POST /api/v1/materials/lookup returns the best canonical match (or 404 if nothing matches). Resolve a whole drawing's worth of candidates in one round-trip with POST .../lookup/batch (up to 100; misses come back in-band so you can correlate by position).

curl -X POST https://api.arcnm.io/api/v1/materials/lookup \
  -H "X-API-Key: $ARCNM_API_KEY" -H "Content-Type: application/json" \
  -d '{ "query": "1.4301" }'

Fetch by id or URN

GET .../grades/{grade_id} and GET .../grades/by-urn/{urn} return the full grade with its aliases. GET .../materials/standards lists the standards bodies (DIN, AISI, …) the catalogue is keyed against.


Use a material in a calculation

Hand ARCNM either reference when you quote — the typed material_grade_id (from a lookup) or a free-form material_ref:

{
  "part_revision_id": "1f…",
  "costing_environment_id": "env_2a…",
  "material_ref": "1.4301"
}

material_grade_id wins if both are supplied. A reference that can't be resolved comes back as material_unresolved (422) — resolve it with /lookup first, or add an override.


Register a custom grade

When you buy a material the platform catalogue doesn't carry, register your own grade (parts:write). It behaves exactly like a catalogue grade — selectable, resolvable, priceable per environment — but stays private to your organization:

POST /api/v1/materials/grades
{
  "category": "carbon_steel",
  "designation": "WS-42-Blank",
  "name": "Works standard 42 bright steel",
  "iso_machining_group": "P",
  "density_kg_per_m3": 7850
}

Density and the ISO 513 group are required — they are what makes the grade priceable. The response carries the grade's stable URN (urn:arc:mat:carbon_steel:org:ws-42-blank): pass it (or the material_grade_id) to any calculation, and set a €/kg for it through the environment rates API like any other grade. A designation that already resolves to a catalogue grade is refused with 409 — use the canonical grade, or map an override onto it. Update with PATCH .../grades/{id} (designation and category are immutable); delete is refused while prices or calculations still reference the grade — deactivate it with {"is_active": false} instead.

Override the catalogue

When your shop floor calls a grade by an internal code, create an override (parts:write):

  • Map an internal code → a grade. Your INOX-A2 resolves to 1.4301 for everyone on the org. Works for canonical and custom grades alike.
POST /api/v1/materials/overrides
{
  "material_grade_id": "…",
  "internal_code": "INOX-A2",
  "trade_name": "Acme Stainless A2"
}

List, patch, and delete them under .../materials/overrides. Once created, your codes resolve through /lookup and on every material_ref you pass to a calculation. (A legacy URN-only override — one with no material_grade_id — still resolves for display, but only a registered grade can price a calculation.)


Material master data — properties, provenance, pricing

Every grade is a master record with three layers, resolved in this order (the same split an ERP material master makes between client-level basic data and plant-level views):

  1. Catalogue figures — each canonical grade ships with reference mechanical properties for the common stock delivery condition: yield_mpa (min yield Rp0.2/ReH), tensile_mpa (min tensile Rm), hardness_hb (Brinell, delivery condition), elongation_pct (min elongation A). They are reference values from the issuing standards; supplier certificates remain the source of truth for a given lot.
  2. Your values — override any property per grade; your value wins and the response still carries the shadowed catalogue figure.
  3. €/kg per environment — prices live in the environment rate tables. When no rate row exists for a grade, a platform default applies (per material family); the master record shows which.
GET /api/v1/materials/grades/{grade_id}/master
{
  "grade": { "primary_code": "1.4301", … },
  "override": { "internal_code": "INOX-A2", … },
  "effective_properties": [
    { "key": "yield_mpa", "value": 210.0, "unit": "MPa",
      "source": "org", "canonical_value": 190.0 },
    { "key": "tensile_mpa", "value": 500.0, "unit": "MPa",
      "source": "canonical" }
  ],
  "pricing": {
    "default_price_per_kg": 4.0,
    "environments": [
      { "env_name": "Werk Süd", "price_per_kg": 4.35,
        "source": "env_rate", "valid_from": "2026-01-01" },
      { "env_name": "Sandbox", "price_per_kg": 4.0,
        "source": "platform_default" }
    ]
  }
}

Set your layer with a single upsert (parts:write) — patch semantics, null clears a value back to the catalogue figure:

PUT /api/v1/materials/grades/{grade_id}/override
{
  "internal_code": "INOX-A2",
  "properties": { "yield_mpa": 210, "hardness_hb": null }
}

Property overrides do not change €/kg — set prices through the environment rates API. GET /api/v1/materials/pricing-coverage returns, in one call, which grades carry an environment rate and where — anything absent prices at the platform default.

They do reach the costing engine. A property is either read by a named part of the engine or explicitly declared display-only, and the API says which per key:

GET /api/v1/materials/attribute-vocabulary
[
  { "key": "tensile_mpa", "unit": "MPa", "scope": "grade",
    "basis": "minimum", "display_only": false,
    "consumer": "physics.handlers.press_brake — the air-bend force screen …" },
  { "key": "charpy_j_m20c", "unit": "J", "scope": "grade",
    "basis": "minimum", "display_only": true,
    "consumer": "display-only: impact toughness is what the EN 10025 subgrade letters encode …" }
]

scope is grade for the per-grade vocabulary you can override, and group for the keys the workpiece-material group states and every grade on it inherits (thermal figures, sheet-forming parameters, the laser_cuttable / weldability_class / heat_treatable route gates). The same consumer statement rides on each entry of effective_properties, so a value that moves no number says so where it is read. Density and the workpiece group stay typed columns on the grade record — they are not part of this vocabulary.


See also