ARCNM

API reference

Uploads

The Uploads API moves CAD and drawing files into ARCNM.

The Uploads API moves CAD and drawing files into ARCNM: presign a direct upload, confirm it, then list, retry, cancel, or fetch a download URL.

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 Uploads

GET /api/v1/uploads/

List the org's uploads, newest first, hiding cancelled rows by default.

Cancelled uploads are useless — the file may have been written to storage but the download endpoint refuses to serve it (status gate is confirmed only). They just clutter the picker, so the UI gets them filtered out unless an admin explicitly opts in via ?include_cancelled=true (e.g. for an audit view).

The response is a plain array, so the page position travels in the Link (RFC 8288) and X-Next-Cursor / X-Has-More response headers: follow Link rel="next" until it stops being sent.

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

Parameters

Name In Type Required Description
include_cancelled query boolean no Include cancelled uploads in the list (hidden by default).
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.
order query asc | desc no Sort direction over the collection's ordering key. Use asc to reconcile a batch: rows come oldest-first, so work created while you page lands after your position instead of shifting rows under it.
created_after query string no Only rows created at or after this instant (RFC 3339, e.g. 2026-07-20T09:00:00Z). Inclusive.
created_before query string no Only rows created strictly before this instant (RFC 3339). Exclusive, so an after/before pair tiles a range without overlap.

Request

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

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

Responses

Status Description
200 Successful Response
422 Validation Error

Errors

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

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

Delete Upload

DELETE /api/v1/uploads/{data_source_id}

Delete an upload: storage object (best-effort) plus the row.

part_revision_dataset.data_source_id carries ondelete=CASCADE, so any part/revision attachments of this file are removed with it — the UI warns about exactly that before calling here.

Parameters

Name In Type Required Description
data_source_id path string yes Identifier of the data source.

Request

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

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

Example response

{
  "message": "string"
}

Update Upload

PATCH /api/v1/uploads/{data_source_id}

Rename an upload and/or move it to a folder.

The folder lives in metadata_json["folder"] — sending folder: null clears it, omitting the field leaves it untouched (model_fields_set distinguishes the two). Renaming changes the display name only; the storage key is immutable once presigned.

Parameters

Name In Type Required Description
data_source_id path string yes Identifier of the data source.

Request body (application/json)

Field Type Required Description
folder string no Folder name to file this upload under; null clears the folder.
name string no New filename for the upload; omit to keep the current name.

Request

curl -X PATCH https://api.arcnm.io/api/v1/uploads/{data_source_id} \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "folder": "string",
    "name": "string"
  }'
import requests

resp = requests.patch(
    "https://api.arcnm.io/api/v1/uploads/{data_source_id}",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "folder": "string",
        "name": "string"
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/uploads/{data_source_id}", {
  method: "PATCH",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "folder": "string",
    "name": "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
content_type string MIME type of the file (e.g. 'application/pdf'); null if unknown.
created_at string ISO 8601 timestamp when the upload record was created.
folder string User-assigned folder name for organising files; null if unfiled.
id string Unique identifier of the uploaded data source.
linked_datasets integer Number of part revision dataset attachments that reference this file.
name string Original filename of the uploaded file.
sha256 string Hex-encoded SHA-256 checksum of the file; null until confirmed.
size_bytes integer Size of the file in bytes; null until the upload is confirmed.
status string Upload lifecycle state: pending, confirmed, failed, or cancelled.
system_folder string Folder assigned by the platform: 'parts_uploads' for files that belong to a part, revision, or calculation workflow; null otherwise.

Example response

{
  "content_type": "string",
  "created_at": "2026-06-01T12:00:00Z",
  "folder": "string",
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "linked_datasets": 0,
  "name": "string",
  "sha256": "9f86d081884c7d659a2feaa0c55ad015…",
  "size_bytes": 204800,
  "status": "string",
  "system_folder": "string"
}

Cancel Upload

POST /api/v1/uploads/{data_source_id}/cancel

Mark a pending upload as cancelled.

Idempotent for already-cancelled rows; rejects rows that have moved past pending since cancelling a confirmed file would silently delete a valid artefact.

Parameters

Name In Type Required Description
data_source_id path string yes Identifier of the data source.

Request

curl -X POST https://api.arcnm.io/api/v1/uploads/{data_source_id}/cancel \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/uploads/{data_source_id}/cancel",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/uploads/{data_source_id}/cancel", {
  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
content_type string MIME type of the file (e.g. 'application/pdf'); null if unknown.
created_at string ISO 8601 timestamp when the upload record was created.
folder string User-assigned folder name for organising files; null if unfiled.
id string Unique identifier of the uploaded data source.
linked_datasets integer Number of part revision dataset attachments that reference this file.
name string Original filename of the uploaded file.
sha256 string Hex-encoded SHA-256 checksum of the file; null until confirmed.
size_bytes integer Size of the file in bytes; null until the upload is confirmed.
status string Upload lifecycle state: pending, confirmed, failed, or cancelled.
system_folder string Folder assigned by the platform: 'parts_uploads' for files that belong to a part, revision, or calculation workflow; null otherwise.

Example response

{
  "content_type": "string",
  "created_at": "2026-06-01T12:00:00Z",
  "folder": "string",
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "linked_datasets": 0,
  "name": "string",
  "sha256": "9f86d081884c7d659a2feaa0c55ad015…",
  "size_bytes": 204800,
  "status": "string",
  "system_folder": "string"
}

Download Url

GET /api/v1/uploads/{data_source_id}/download-url

Parameters

Name In Type Required Description
data_source_id path string yes Identifier of the data source.

Request

curl -X GET https://api.arcnm.io/api/v1/uploads/{data_source_id}/download-url \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.get(
    "https://api.arcnm.io/api/v1/uploads/{data_source_id}/download-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/uploads/{data_source_id}/download-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
url string Short-lived presigned URL to download the confirmed file.

Example response

{
  "url": "string"
}

Retry Upload

POST /api/v1/uploads/{data_source_id}/retry

Re-queue the confirm worker for a non-confirmed upload.

Parameters

Name In Type Required Description
data_source_id path string yes Identifier of the data source.

Request

curl -X POST https://api.arcnm.io/api/v1/uploads/{data_source_id}/retry \
  -H "X-API-Key: $ARCNM_API_KEY"
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/uploads/{data_source_id}/retry",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/uploads/{data_source_id}/retry", {
  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
content_type string MIME type of the file (e.g. 'application/pdf'); null if unknown.
created_at string ISO 8601 timestamp when the upload record was created.
folder string User-assigned folder name for organising files; null if unfiled.
id string Unique identifier of the uploaded data source.
linked_datasets integer Number of part revision dataset attachments that reference this file.
name string Original filename of the uploaded file.
sha256 string Hex-encoded SHA-256 checksum of the file; null until confirmed.
size_bytes integer Size of the file in bytes; null until the upload is confirmed.
status string Upload lifecycle state: pending, confirmed, failed, or cancelled.
system_folder string Folder assigned by the platform: 'parts_uploads' for files that belong to a part, revision, or calculation workflow; null otherwise.

Example response

{
  "content_type": "string",
  "created_at": "2026-06-01T12:00:00Z",
  "folder": "string",
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "linked_datasets": 0,
  "name": "string",
  "sha256": "9f86d081884c7d659a2feaa0c55ad015…",
  "size_bytes": 204800,
  "status": "string",
  "system_folder": "string"
}

Bulk Cancel Uploads

POST /api/v1/uploads/bulk-cancel

Cancel many pending uploads at once.

Per-row failures are reported in skipped so the UI can render a summary toast — the whole batch isn't rolled back if a single id is already confirmed. The batch size is capped on BulkUploadIds.

Request body (application/json)

Field Type Required Description
ids string[] yes Upload (data source) IDs to cancel or retry in bulk (1–200).

Request

curl -X POST https://api.arcnm.io/api/v1/uploads/bulk-cancel \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "ids": [
      "3fa85f64-5717-4562-b3fc-2c963f66afa6"
    ]
  }'
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/uploads/bulk-cancel",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "ids": [
            "3fa85f64-5717-4562-b3fc-2c963f66afa6"
        ]
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/uploads/bulk-cancel", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "ids": [
      "3fa85f64-5717-4562-b3fc-2c963f66afa6"
    ]
  }),
})
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.
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
cancelled string[] Upload IDs that were successfully cancelled.
retried string[] Upload IDs that were successfully re-queued for confirmation.
skipped object[] Uploads that were skipped, each as {id, reason}.

Example response

{
  "cancelled": [
    "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  ],
  "retried": [
    "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  ],
  "skipped": [
    {}
  ]
}

Bulk Delete Uploads

POST /api/v1/uploads/bulk-delete

Delete many uploads at once.

Mirrors bulk-cancel: per-row failures land in skipped instead of rolling back the batch; the batch size is capped on BulkUploadIds.

Request body (application/json)

Field Type Required Description
ids string[] yes Upload (data source) IDs to cancel or retry in bulk (1–200).

Request

curl -X POST https://api.arcnm.io/api/v1/uploads/bulk-delete \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "ids": [
      "3fa85f64-5717-4562-b3fc-2c963f66afa6"
    ]
  }'
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/uploads/bulk-delete",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "ids": [
            "3fa85f64-5717-4562-b3fc-2c963f66afa6"
        ]
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/uploads/bulk-delete", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "ids": [
      "3fa85f64-5717-4562-b3fc-2c963f66afa6"
    ]
  }),
})
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.
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
deleted string[] Upload IDs that were successfully deleted.
skipped object[] Uploads that were skipped, each as {id, reason}.

Example response

{
  "deleted": [
    "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  ],
  "skipped": [
    {}
  ]
}

Bulk Retry Uploads

POST /api/v1/uploads/bulk-retry

Re-queue many uploads at once.

Request body (application/json)

Field Type Required Description
ids string[] yes Upload (data source) IDs to cancel or retry in bulk (1–200).

Request

curl -X POST https://api.arcnm.io/api/v1/uploads/bulk-retry \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "ids": [
      "3fa85f64-5717-4562-b3fc-2c963f66afa6"
    ]
  }'
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/uploads/bulk-retry",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "ids": [
            "3fa85f64-5717-4562-b3fc-2c963f66afa6"
        ]
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/uploads/bulk-retry", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "ids": [
      "3fa85f64-5717-4562-b3fc-2c963f66afa6"
    ]
  }),
})
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.
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
cancelled string[] Upload IDs that were successfully cancelled.
retried string[] Upload IDs that were successfully re-queued for confirmation.
skipped object[] Uploads that were skipped, each as {id, reason}.

Example response

{
  "cancelled": [
    "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  ],
  "retried": [
    "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  ],
  "skipped": [
    {}
  ]
}

Confirm

POST /api/v1/uploads/confirm

Request body (application/json)

Field Type Required Description
data_source_id string yes UUID of the presigned upload to validate and ingest.

Request

curl -X POST https://api.arcnm.io/api/v1/uploads/confirm \
  -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/uploads/confirm",
    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/uploads/confirm", {
  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
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.
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
job_id string Identifier of the background job validating and ingesting the file.

Example response

{
  "job_id": "string"
}

Presign

POST /api/v1/uploads/presign

Request body (application/json)

Field Type Required Description
content_type string no MIME type of the file (e.g. application/pdf).
name string yes Original filename of the file to upload.
size_bytes integer no Size of the file in bytes, if known.

Request

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

resp = requests.post(
    "https://api.arcnm.io/api/v1/uploads/presign",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "name": "string"
    },
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/uploads/presign", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "name": "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.
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
data_source_id string Identifier of the data source record; pass it to /uploads/confirm.
expires_in integer Seconds until the presigned URL expires.
fields object Form fields to include for a multipart POST upload; null if none.
headers object Headers that must be sent with the upload request; null if none.
method string HTTP method to use against url (typically PUT or POST).
url string Presigned URL the client uploads the file bytes to.

Example response

{
  "data_source_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "expires_in": 0,
  "fields": {},
  "headers": {},
  "method": "string",
  "url": "string"
}