API reference
Parts & revisions
The Parts API manages parts and their revisions.
The Parts API manages parts and their revisions: create, list, fetch, update, and delete parts, and manage each part's revision history.
Auto-generated from the public OpenAPI spec — this page never drifts from the running API. Base URL
https://api.arcnm.io. Authenticate with theX-API-Keyheader (see Authentication).
List Parts
GET /api/v1/parts/
Paginated. Pass
limitandoffsetto page through results.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
q |
query | string | no | Substring match |
classification_id |
query | string | no | Identifier of the classification. |
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/ \
-H "X-API-Key: $ARCNM_API_KEY"
import requests
resp = requests.get(
"https://api.arcnm.io/api/v1/parts/",
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/", {
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 |
|---|---|---|
count |
integer | Total number of parts matching the query. |
data |
PartPublic[] | The page of parts matching the query. |
Example response
{
"count": 0,
"data": [
{
"attributes": {},
"base_uom": "EA",
"classification_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"created_at": "2026-06-01T12:00:00Z",
"default_currency": "EUR",
"description": "string",
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"lead_time_days": 0,
"make_or_buy": "make",
"org_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"part_number": "BRACKET-001",
"procurement_type": "in_house",
"updated_at": "2026-06-01T12:00:00Z"
}
]
}
Create Part
POST /api/v1/parts/
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
attributes |
object | no | Free-form key/value metadata for the part. |
base_uom |
string | no | Base unit of measure for the part (ISO unit code, e.g. EA, KG, M). |
classification_id |
string | no | ID of the classification category this part belongs to, if any. |
default_currency |
string | no | Default currency for the part's pricing (ISO 4217 code, e.g. EUR, USD). |
description |
string | no | Human-readable description of the part. |
lead_time_days |
integer | no | Expected lead time to obtain the part, in days. |
make_or_buy |
string | no | Whether the part is manufactured in-house or purchased. |
part_number |
string | yes | Stable identifier (≈ SAP MATNR). Unique per org. |
procurement_type |
string | no | How the part is sourced (e.g. in-house production or external supplier). |
Request
curl -X POST https://api.arcnm.io/api/v1/parts/ \
-H "X-API-Key: $ARCNM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"part_number": "BRACKET-001"
}'
import requests
resp = requests.post(
"https://api.arcnm.io/api/v1/parts/",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"part_number": "BRACKET-001"
},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/parts/", {
method: "POST",
headers: {
"X-API-Key": process.env.ARCNM_API_KEY!,
"Content-Type": "application/json",
},
body: JSON.stringify({
"part_number": "BRACKET-001"
}),
})
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 |
|---|---|---|
attributes |
object | Free-form key/value metadata for the part. |
base_uom |
string | Base unit of measure for the part (ISO unit code, e.g. EA, KG, M). |
classification_id |
string | ID of the classification category this part belongs to, if any. |
created_at |
string | Timestamp when the part was created (UTC, ISO 8601). |
default_currency |
string | Default currency for the part's pricing (ISO 4217 code, e.g. EUR, USD). |
description |
string | Human-readable description of the part. |
id |
string | Unique identifier of the part. |
lead_time_days |
integer | Expected lead time to obtain the part, in days. |
make_or_buy |
string | Whether the part is manufactured in-house or purchased. |
org_id |
string | ID of the organization that owns the part. |
part_number |
string | Stable identifier (≈ SAP MATNR). Unique per org. |
procurement_type |
string | How the part is sourced (e.g. in-house production or external supplier). |
updated_at |
string | Timestamp when the part was last updated (UTC, ISO 8601). |
Example response
{
"attributes": {},
"base_uom": "EA",
"classification_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"created_at": "2026-06-01T12:00:00Z",
"default_currency": "EUR",
"description": "string",
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"lead_time_days": 0,
"make_or_buy": "make",
"org_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"part_number": "BRACKET-001",
"procurement_type": "in_house",
"updated_at": "2026-06-01T12:00:00Z"
}
Delete Part
DELETE /api/v1/parts/{part_id}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
part_id |
path | string | yes | Identifier of the part. |
Request
curl -X DELETE https://api.arcnm.io/api/v1/parts/{part_id} \
-H "X-API-Key: $ARCNM_API_KEY"
import requests
resp = requests.delete(
"https://api.arcnm.io/api/v1/parts/{part_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/{part_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 confirmation that the resource was deleted. |
Example response
{
"message": "string"
}
Get Part
GET /api/v1/parts/{part_id}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
part_id |
path | string | yes | Identifier of the part. |
Request
curl -X GET https://api.arcnm.io/api/v1/parts/{part_id} \
-H "X-API-Key: $ARCNM_API_KEY"
import requests
resp = requests.get(
"https://api.arcnm.io/api/v1/parts/{part_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/{part_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 for the part. |
base_uom |
string | Base unit of measure for the part (ISO unit code, e.g. EA, KG, M). |
classification_id |
string | ID of the classification category this part belongs to, if any. |
created_at |
string | Timestamp when the part was created (UTC, ISO 8601). |
default_currency |
string | Default currency for the part's pricing (ISO 4217 code, e.g. EUR, USD). |
description |
string | Human-readable description of the part. |
id |
string | Unique identifier of the part. |
lead_time_days |
integer | Expected lead time to obtain the part, in days. |
make_or_buy |
string | Whether the part is manufactured in-house or purchased. |
org_id |
string | ID of the organization that owns the part. |
part_number |
string | Stable identifier (≈ SAP MATNR). Unique per org. |
procurement_type |
string | How the part is sourced (e.g. in-house production or external supplier). |
updated_at |
string | Timestamp when the part was last updated (UTC, ISO 8601). |
Example response
{
"attributes": {},
"base_uom": "EA",
"classification_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"created_at": "2026-06-01T12:00:00Z",
"default_currency": "EUR",
"description": "string",
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"lead_time_days": 0,
"make_or_buy": "make",
"org_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"part_number": "BRACKET-001",
"procurement_type": "in_house",
"updated_at": "2026-06-01T12:00:00Z"
}
Update Part
PATCH /api/v1/parts/{part_id}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
part_id |
path | string | yes | Identifier of the part. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
attributes |
object | no | Free-form key/value metadata for the part. |
base_uom |
string | no | Base unit of measure for the part (ISO unit code, e.g. EA, KG, M). |
classification_id |
string | no | ID of the classification category this part belongs to, if any. |
default_currency |
string | no | Default currency for the part's pricing (ISO 4217 code, e.g. EUR, USD). |
description |
string | no | Human-readable description of the part. |
lead_time_days |
integer | no | Expected lead time to obtain the part, in days. |
make_or_buy |
string | no | Whether the part is manufactured in-house or purchased. |
procurement_type |
string | no | How the part is sourced (e.g. in-house production or external supplier). |
Request
curl -X PATCH https://api.arcnm.io/api/v1/parts/{part_id} \
-H "X-API-Key: $ARCNM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"attributes": {},
"base_uom": "EA",
"classification_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"default_currency": "string",
"description": "string",
"lead_time_days": 0,
"make_or_buy": "string",
"procurement_type": "string"
}'
import requests
resp = requests.patch(
"https://api.arcnm.io/api/v1/parts/{part_id}",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"attributes": {},
"base_uom": "EA",
"classification_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"default_currency": "string",
"description": "string",
"lead_time_days": 0,
"make_or_buy": "string",
"procurement_type": "string"
},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/parts/{part_id}", {
method: "PATCH",
headers: {
"X-API-Key": process.env.ARCNM_API_KEY!,
"Content-Type": "application/json",
},
body: JSON.stringify({
"attributes": {},
"base_uom": "EA",
"classification_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"default_currency": "string",
"description": "string",
"lead_time_days": 0,
"make_or_buy": "string",
"procurement_type": "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 |
|---|---|---|
attributes |
object | Free-form key/value metadata for the part. |
base_uom |
string | Base unit of measure for the part (ISO unit code, e.g. EA, KG, M). |
classification_id |
string | ID of the classification category this part belongs to, if any. |
created_at |
string | Timestamp when the part was created (UTC, ISO 8601). |
default_currency |
string | Default currency for the part's pricing (ISO 4217 code, e.g. EUR, USD). |
description |
string | Human-readable description of the part. |
id |
string | Unique identifier of the part. |
lead_time_days |
integer | Expected lead time to obtain the part, in days. |
make_or_buy |
string | Whether the part is manufactured in-house or purchased. |
org_id |
string | ID of the organization that owns the part. |
part_number |
string | Stable identifier (≈ SAP MATNR). Unique per org. |
procurement_type |
string | How the part is sourced (e.g. in-house production or external supplier). |
updated_at |
string | Timestamp when the part was last updated (UTC, ISO 8601). |
Example response
{
"attributes": {},
"base_uom": "EA",
"classification_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"created_at": "2026-06-01T12:00:00Z",
"default_currency": "EUR",
"description": "string",
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"lead_time_days": 0,
"make_or_buy": "make",
"org_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"part_number": "BRACKET-001",
"procurement_type": "in_house",
"updated_at": "2026-06-01T12:00:00Z"
}
Part Preview Url
GET /api/v1/parts/{part_id}/preview-url
Engine-agnostic GLB preview for a Part.
Resolves the part's most-recent revision's primary CAD dataset and hands back a presigned URL for its glTF/GLB. The frontend uses this on the parts overview and part detail pages so the 3D model is surfaced before any calculation has run.
Returns {url, status} (status semantics match
/parts/datasets/{ds_id}/preview-url). The frontend treats the
same payload identically across all surfaces — that is the
"globally unified" 3D preview contract.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
part_id |
path | string | yes | Identifier of the part. |
Request
curl -X GET https://api.arcnm.io/api/v1/parts/{part_id}/preview-url \
-H "X-API-Key: $ARCNM_API_KEY"
import requests
resp = requests.get(
"https://api.arcnm.io/api/v1/parts/{part_id}/preview-url",
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/{part_id}/preview-url", {
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 |
|---|---|---|
data_source_id |
string | Identifier of the resolved CAD file; null when no dataset was found. |
status |
string | Preview readiness state (e.g. ok, pending, failed, no_dataset). |
url |
string | Presigned URL to the GLB preview; null unless status is 'ok'. |
Example response
{
"data_source_id": "string",
"status": "string",
"url": "string"
}
List Revisions
GET /api/v1/parts/{part_id}/revisions
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
part_id |
path | string | yes | Identifier of the part. |
Request
curl -X GET https://api.arcnm.io/api/v1/parts/{part_id}/revisions \
-H "X-API-Key: $ARCNM_API_KEY"
import requests
resp = requests.get(
"https://api.arcnm.io/api/v1/parts/{part_id}/revisions",
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/{part_id}/revisions", {
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 |
|---|---|---|
count |
integer | Total number of revisions matching the query. |
data |
PartRevisionPublic[] | The page of revisions matching the query. |
Example response
{
"count": 0,
"data": [
{
"attributes": {},
"created_at": "2026-06-01T12:00:00Z",
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"parent_revision_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"part_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"revision_code": "string",
"updated_at": "2026-06-01T12:00:00Z"
}
]
}
Create Revision
POST /api/v1/parts/{part_id}/revisions
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
part_id |
path | string | yes | Identifier of the part. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
attributes |
object | no | Free-form key/value metadata for the revision. |
change_request_id |
string | no | ID of the change request that triggered this revision, if any. |
parent_revision_id |
string | no | ID of the revision this one was derived from, recording its lineage. |
revision_code |
string | yes | Customer-facing revision label (A, B, 01, 02, …). |
Request
curl -X POST https://api.arcnm.io/api/v1/parts/{part_id}/revisions \
-H "X-API-Key: $ARCNM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"revision_code": "string"
}'
import requests
resp = requests.post(
"https://api.arcnm.io/api/v1/parts/{part_id}/revisions",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"revision_code": "string"
},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/parts/{part_id}/revisions", {
method: "POST",
headers: {
"X-API-Key": process.env.ARCNM_API_KEY!,
"Content-Type": "application/json",
},
body: JSON.stringify({
"revision_code": "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 |
|---|---|---|
attributes |
object | Free-form key/value metadata for the revision. |
created_at |
string | Timestamp when the revision was created (UTC, ISO 8601). |
id |
string | Unique identifier of the revision. |
parent_revision_id |
string | ID of the revision this one was derived from, recording its lineage. |
part_id |
string | ID of the part this revision belongs to. |
revision_code |
string | Customer-facing revision label (A, B, 01, 02, …). |
updated_at |
string | Timestamp when the revision was last updated (UTC, ISO 8601). |
Example response
{
"attributes": {},
"created_at": "2026-06-01T12:00:00Z",
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"parent_revision_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"part_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"revision_code": "string",
"updated_at": "2026-06-01T12:00:00Z"
}
Delete Revision
DELETE /api/v1/parts/{part_id}/revisions/{revision_id}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
part_id |
path | string | yes | Identifier of the part. |
revision_id |
path | string | yes | Identifier of the revision. |
Request
curl -X DELETE https://api.arcnm.io/api/v1/parts/{part_id}/revisions/{revision_id} \
-H "X-API-Key: $ARCNM_API_KEY"
import requests
resp = requests.delete(
"https://api.arcnm.io/api/v1/parts/{part_id}/revisions/{revision_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/{part_id}/revisions/{revision_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 confirmation that the resource was deleted. |
Example response
{
"message": "string"
}
Get Revision
GET /api/v1/parts/{part_id}/revisions/{revision_id}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
part_id |
path | string | yes | Identifier of the part. |
revision_id |
path | string | yes | Identifier of the revision. |
Request
curl -X GET https://api.arcnm.io/api/v1/parts/{part_id}/revisions/{revision_id} \
-H "X-API-Key: $ARCNM_API_KEY"
import requests
resp = requests.get(
"https://api.arcnm.io/api/v1/parts/{part_id}/revisions/{revision_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/{part_id}/revisions/{revision_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 for the revision. |
created_at |
string | Timestamp when the revision was created (UTC, ISO 8601). |
id |
string | Unique identifier of the revision. |
parent_revision_id |
string | ID of the revision this one was derived from, recording its lineage. |
part_id |
string | ID of the part this revision belongs to. |
revision_code |
string | Customer-facing revision label (A, B, 01, 02, …). |
updated_at |
string | Timestamp when the revision was last updated (UTC, ISO 8601). |
Example response
{
"attributes": {},
"created_at": "2026-06-01T12:00:00Z",
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"parent_revision_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"part_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"revision_code": "string",
"updated_at": "2026-06-01T12:00:00Z"
}
Update Revision
PATCH /api/v1/parts/{part_id}/revisions/{revision_id}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
part_id |
path | string | yes | Identifier of the part. |
revision_id |
path | string | yes | Identifier of the revision. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
attributes |
object | no | Free-form metadata to merge onto the revision, as a JSON object. |
Request
curl -X PATCH https://api.arcnm.io/api/v1/parts/{part_id}/revisions/{revision_id} \
-H "X-API-Key: $ARCNM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"attributes": {}
}'
import requests
resp = requests.patch(
"https://api.arcnm.io/api/v1/parts/{part_id}/revisions/{revision_id}",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"attributes": {}
},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/parts/{part_id}/revisions/{revision_id}", {
method: "PATCH",
headers: {
"X-API-Key": process.env.ARCNM_API_KEY!,
"Content-Type": "application/json",
},
body: JSON.stringify({
"attributes": {}
}),
})
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 |
|---|---|---|
attributes |
object | Free-form key/value metadata for the revision. |
created_at |
string | Timestamp when the revision was created (UTC, ISO 8601). |
id |
string | Unique identifier of the revision. |
parent_revision_id |
string | ID of the revision this one was derived from, recording its lineage. |
part_id |
string | ID of the part this revision belongs to. |
revision_code |
string | Customer-facing revision label (A, B, 01, 02, …). |
updated_at |
string | Timestamp when the revision was last updated (UTC, ISO 8601). |
Example response
{
"attributes": {},
"created_at": "2026-06-01T12:00:00Z",
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"parent_revision_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"part_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"revision_code": "string",
"updated_at": "2026-06-01T12:00:00Z"
}
List Datasets
GET /api/v1/parts/{part_id}/revisions/{revision_id}/datasets
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
part_id |
path | string | yes | Identifier of the part. |
revision_id |
path | string | yes | Identifier of the revision. |
Request
curl -X GET https://api.arcnm.io/api/v1/parts/{part_id}/revisions/{revision_id}/datasets \
-H "X-API-Key: $ARCNM_API_KEY"
import requests
resp = requests.get(
"https://api.arcnm.io/api/v1/parts/{part_id}/revisions/{revision_id}/datasets",
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/{part_id}/revisions/{revision_id}/datasets", {
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 Dataset
POST /api/v1/parts/{part_id}/revisions/{revision_id}/datasets
Attach an existing DataSource row to a revision.
For a fresh file upload, prefer POST .../datasets/upload which
creates the DataSource + link in one shot.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
part_id |
path | string | yes | Identifier of the part. |
revision_id |
path | string | yes | Identifier of the revision. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
attributes |
object | no | Free-form metadata for the attachment, as a JSON object. |
data_source_id |
string | yes | Identifier of the uploaded data source to attach to the revision. |
is_primary |
boolean | no | Whether this dataset is the primary one for the revision. |
role |
string | no | Role of the file on the revision (e.g. cad_3d for a 3D CAD model, drawing_2d for a 2D drawing). |
Request
curl -X POST https://api.arcnm.io/api/v1/parts/{part_id}/revisions/{revision_id}/datasets \
-H "X-API-Key: $ARCNM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"data_source_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}'
import requests
resp = requests.post(
"https://api.arcnm.io/api/v1/parts/{part_id}/revisions/{revision_id}/datasets",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"data_source_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/parts/{part_id}/revisions/{revision_id}/datasets", {
method: "POST",
headers: {
"X-API-Key": process.env.ARCNM_API_KEY!,
"Content-Type": "application/json",
},
body: JSON.stringify({
"data_source_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}),
})
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 |
|---|---|---|
attributes |
object | Free-form key/value metadata for the file attachment. |
content_type |
string | MIME type of the uploaded file (e.g. application/pdf). |
created_at |
string | Timestamp when the file was attached (UTC, ISO 8601). |
data_source_id |
string | ID of the underlying stored file this attachment points to. |
filename |
string | Original filename of the uploaded file. |
id |
string | Unique identifier of the uploaded file attached to the revision. |
is_active |
boolean | Whether this is the current file; superseded re-uploads are marked inactive. |
is_primary |
boolean | Whether this is the primary CAD file used for analysis on the revision. |
part_revision_id |
string | ID of the revision this file is attached to. |
role |
string | The file's role on the revision (e.g. 3D CAD, 2D drawing, spec). |
size_bytes |
integer | Size of the uploaded file in bytes. |
Example response
{
"attributes": {},
"content_type": "string",
"created_at": "2026-06-01T12:00:00Z",
"data_source_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"filename": "bracket.step",
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"is_active": true,
"is_primary": true,
"part_revision_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"role": "primary",
"size_bytes": 204800
}
Detach Dataset
DELETE /api/v1/parts/{part_id}/revisions/{revision_id}/datasets/{dataset_link_id}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
part_id |
path | string | yes | Identifier of the part. |
revision_id |
path | string | yes | Identifier of the revision. |
dataset_link_id |
path | string | yes | Identifier of the dataset link. |
Request
curl -X DELETE https://api.arcnm.io/api/v1/parts/{part_id}/revisions/{revision_id}/datasets/{dataset_link_id} \
-H "X-API-Key: $ARCNM_API_KEY"
import requests
resp = requests.delete(
"https://api.arcnm.io/api/v1/parts/{part_id}/revisions/{revision_id}/datasets/{dataset_link_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/{part_id}/revisions/{revision_id}/datasets/{dataset_link_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 confirmation that the dataset was detached. |
Example response
{
"message": "string"
}
Update Dataset
PATCH /api/v1/parts/{part_id}/revisions/{revision_id}/datasets/{dataset_link_id}
Edit a dataset link.
Currently supports two fields:
filename: rename the underlyingDataSource.is_primary: promote/demote within the parent revision. The "exactly one primary" invariant is maintained server-side: when promoting, every other link on the same revision is demoted in the same transaction so concurrent primaries can't co-exist.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
part_id |
path | string | yes | Identifier of the part. |
revision_id |
path | string | yes | Identifier of the revision. |
dataset_link_id |
path | string | yes | Identifier of the dataset link. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
filename |
string | no | New original filename for the uploaded file. |
is_primary |
boolean | no | Set true to make this the primary CAD file for analysis on the revision. |
Request
curl -X PATCH https://api.arcnm.io/api/v1/parts/{part_id}/revisions/{revision_id}/datasets/{dataset_link_id} \
-H "X-API-Key: $ARCNM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"filename": "bracket.step",
"is_primary": true
}'
import requests
resp = requests.patch(
"https://api.arcnm.io/api/v1/parts/{part_id}/revisions/{revision_id}/datasets/{dataset_link_id}",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"filename": "bracket.step",
"is_primary": True
},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/parts/{part_id}/revisions/{revision_id}/datasets/{dataset_link_id}", {
method: "PATCH",
headers: {
"X-API-Key": process.env.ARCNM_API_KEY!,
"Content-Type": "application/json",
},
body: JSON.stringify({
"filename": "bracket.step",
"is_primary": true
}),
})
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 |
|---|---|---|
attributes |
object | Free-form key/value metadata for the file attachment. |
content_type |
string | MIME type of the uploaded file (e.g. application/pdf). |
created_at |
string | Timestamp when the file was attached (UTC, ISO 8601). |
data_source_id |
string | ID of the underlying stored file this attachment points to. |
filename |
string | Original filename of the uploaded file. |
id |
string | Unique identifier of the uploaded file attached to the revision. |
is_active |
boolean | Whether this is the current file; superseded re-uploads are marked inactive. |
is_primary |
boolean | Whether this is the primary CAD file used for analysis on the revision. |
part_revision_id |
string | ID of the revision this file is attached to. |
role |
string | The file's role on the revision (e.g. 3D CAD, 2D drawing, spec). |
size_bytes |
integer | Size of the uploaded file in bytes. |
Example response
{
"attributes": {},
"content_type": "string",
"created_at": "2026-06-01T12:00:00Z",
"data_source_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"filename": "bracket.step",
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"is_active": true,
"is_primary": true,
"part_revision_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"role": "primary",
"size_bytes": 204800
}
Upload Dataset
POST /api/v1/parts/{part_id}/revisions/{revision_id}/datasets/upload
Upload a file directly onto a revision, in one shot.
Stores the file and links it to the revision. No calculation is
enqueued — use /parts/calculations/upload for the
calculate-on-upload flow.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
part_id |
path | string | yes | Identifier of the part. |
revision_id |
path | string | yes | Identifier of the revision. |
role |
query | string | no | Role of the file on the revision (e.g. cad_3d for a 3D model, drawing_2d for a 2D drawing). |
is_primary |
query | boolean | no | Whether this dataset becomes the primary one for its role on the revision. |
Request body (multipart/form-data)
| Field | Type | Required | Description |
|---|---|---|---|
file |
string | yes | The CAD model or drawing file to attach to the revision. |
Request
curl -X POST https://api.arcnm.io/api/v1/parts/{part_id}/revisions/{revision_id}/datasets/upload \
-H "X-API-Key: $ARCNM_API_KEY" \
-F "[email protected]"
import requests
resp = requests.post(
"https://api.arcnm.io/api/v1/parts/{part_id}/revisions/{revision_id}/datasets/upload",
headers={"X-API-Key": "YOUR_API_KEY"},
files={
"file": open("file.bin", "rb"),
},
)
resp.raise_for_status()
print(resp.json())
const form = new FormData()
form.append("file", file) // a File or Blob
const resp = await fetch("https://api.arcnm.io/api/v1/parts/{part_id}/revisions/{revision_id}/datasets/upload", {
method: "POST",
headers: { "X-API-Key": process.env.ARCNM_API_KEY! },
body: form,
})
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 |
|---|---|---|
attributes |
object | Free-form key/value metadata for the file attachment. |
content_type |
string | MIME type of the uploaded file (e.g. application/pdf). |
created_at |
string | Timestamp when the file was attached (UTC, ISO 8601). |
data_source_id |
string | ID of the underlying stored file this attachment points to. |
filename |
string | Original filename of the uploaded file. |
id |
string | Unique identifier of the uploaded file attached to the revision. |
is_active |
boolean | Whether this is the current file; superseded re-uploads are marked inactive. |
is_primary |
boolean | Whether this is the primary CAD file used for analysis on the revision. |
part_revision_id |
string | ID of the revision this file is attached to. |
role |
string | The file's role on the revision (e.g. 3D CAD, 2D drawing, spec). |
size_bytes |
integer | Size of the uploaded file in bytes. |
Example response
{
"attributes": {},
"content_type": "string",
"created_at": "2026-06-01T12:00:00Z",
"data_source_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"filename": "bracket.step",
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"is_active": true,
"is_primary": true,
"part_revision_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"role": "primary",
"size_bytes": 204800
}