ARCNM

Building blocks

Uploading files

Two ways to get CAD and drawings into ARCNM: one-shot upload-and-quote, or a presigned direct upload you can reuse across calculations. STEP, PDF, PNG, JPEG.

Every quote starts from a file. ARCNM accepts STEP 3D geometry (.step/.stp), the DXF flat pattern of a sheet part (.dxf, see below), 2D drawings as PDF, PNG or JPEG, optional STL/OBJ meshes, and supporting documents, up to 512 MB each (CAD is capped lower — see below). There are two ways in.

STEP is the only 3D format accepted — it is the one that carries B-rep geometry. IGES, Parasolid, SolidWorks and other native kernel formats are not accepted: a presign or upload declaring them is rejected rather than accepted and dropped later.

DXF flat patterns

A sheet part that has no 3D model — or whose model does not unfold — arrives as the flat pattern its cutter reads. Upload the .dxf in the CAD slot (cad_file on /upload-and-quote, role cad_3d on a revision) together with two things a 2D file cannot carry: sheet_gauge_mm, the sheet thickness, and a material (material_grade_id or material_ref). Both are required for a DXF (422 dxf_gauge_required / dxf_material_required). The part is then priced as a flat sheet part cut to that pattern — nested on your stocked formats, the contours cut, the skeleton scrap — with one low-severity note that no 3D model was read (so no bends or forms are known).

What the import reads: every geometric entity of the model space, blocks exploded — polylines (bulges honoured), lines, arcs, circles, ellipses and splines, curves flattened at 0.2 mm. Units come from $INSUNITS: millimetres (4) or inches (1); a file with any other unit, unitless files included, is refused (422 dxf_units_unsupported). One closed outer contour per file; every other closed loop inside it is an opening. A file with two outer contours, an island inside an opening or crossing contours (dxf_multiple_outer_contours), a chain of lines and arcs that does not close (dxf_open_loop, the loose end named in details), no closed contour at all (dxf_no_contour) or one that cannot be read (dxf_unreadable) is refused at upload, before anything is stored.

curl -X POST https://api.arcnm.io/api/v1/calculations/upload-and-quote \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -F part_number=BRK-17 -F sheet_gauge_mm=2 -F material_ref=1.4301 \
  -F "[email protected];type=application/dxf"

One-shot: upload and quote

The fastest path — hand the bytes and the costing inputs to POST /api/v1/calculations/upload-and-quote in a single multipart call, and ARCNM stores the file, creates the part + revision, and enqueues the calculation. This is the quickstart path; use it whenever you're costing a file once. To price the upload in several environments at once, repeat the costing_environment_ids field (up to 16): the file is still uploaded and stored once, and one calculation per environment is enqueued under a shared batch_id.


Reusable: presigned direct upload

When you want to attach a file to several calculations, push large files straight to storage, or separate ingest from costing, use the presign → send → confirm flow on the /uploads resource (uploads:write):

# 1. Ask where and how to send the file.
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": "bracket.step", "content_type": "application/step", "size_bytes": 184320 }'
# → { "data_source_id": "ds_…", "url": "https://…", "method": "PUT" or "POST",
#     "headers": { … } or null, "fields": { … } or null, "expires_in": 900 }

# 2. Send the bytes straight to storage — the payload never touches the API.
#    `method` says how; a file sent the other way is refused and never arrives.
#    "PUT": the raw bytes as the body, with exactly the `headers` named.
curl -X PUT "<url>" -H "Content-Type: application/step" --data-binary @bracket.step
#    "POST": a multipart form — every entry of `fields` exactly as answered
#    (one --form-string each: it never reads a leading `@` or `<` as a file),
#    then the file as the last field, named `file`.
curl -X POST "<url>" --form-string "key=$KEY" --form-string "policy=$POLICY" \
  --form-string "…=…" -F "[email protected]"

# 3. Confirm — ARCNM checks the file (magic bytes, declared size) and keeps it.
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": "ds_…" }'
# → { "data_source_id": "ds_…", "status": "pending" }

The check runs after confirm answers: status is pending until it is done, confirmed after. Confirm is safe to repeat and answers the current status; listing your uploads shows it too. When the upload cannot be confirmed, confirm answers 409 and says why in details.reason:

details.reason What to do
upload_not_received No file has arrived yet. Send it as method says, then confirm again
upload_rejected The file was refused when it was checked: its content does not match its declared type, it is larger than declared, or it would exceed your plan's storage. Upload it again
upload_failed The file could not be checked. Retry it with POST /api/v1/uploads/{id}/retry
upload_cancelled The upload was cancelled. Retry it with POST /api/v1/uploads/{id}/retry to have it checked

The data_source_id is durable: attach it to a revision (POST /api/v1/parts/{part_id}/revisions/{revision_id}/datasets) and quote that revision as many times — and in as many environments — as you like without re-uploading a byte. Attached while it is still pending, the upload answers 409 with details.reason upload_pending, a Retry-After header and the same wait as details.retry_after_s: ask again then. upload_rejected, upload_failed and upload_cancelled do not change by waiting.

Storage. Until it is confirmed, an upload reserves its declared size_bytes of your plan's storage — the most its file type allows when it declares none, so declare the size. Confirm checks the plan again on the size that arrived (402 entitlement_exceeded). If url expires with nothing sent, the reservation is released; a file sent through url after its upload was deleted or refused is removed once url expires.

Why presign? The file bytes go directly to object storage, not through the API, so a 512 MB STEP never touches the request path — faster uploads, and no JSON body-size ceiling to fight.


Allowed types

Kind Content types
3D CAD (role cad_3d) application/step, model/step+xml. Validated against the ISO-10303-21 header — a non-STEP payload returns 422 cad_file_not_step
DXF flat pattern (role cad_3d) application/dxf, image/vnd.dxf. Judged as a DXF (header, units, one closed outer contour) and refused with a dxf_* code otherwise; needs sheet_gauge_mm and a material — see above
Mesh (role mesh) model/stl, application/sla. No B-rep: envelope, mass and surface area only
2D drawings (role drawing_2d) application/pdf, image/png, image/jpeg, image/webp
Documents application/pdf, text/csv, text/plain, Office formats

content_type is required at presign and validated again by magic-byte sniffing on confirm. Executable / scriptable types (HTML, JavaScript, SVG) are rejected outright.


Manage uploads

Operation Endpoint
List your uploads GET /api/v1/uploads
Get a download URL GET /api/v1/uploads/{id}/download-url
Retry a failed ingest POST /api/v1/uploads/{id}/retry
Cancel a pending upload POST /api/v1/uploads/{id}/cancel
Bulk retry / cancel POST /api/v1/uploads/bulk-retry, .../bulk-cancel

See also