DFM Platform — Digital Metal
Digital Metal

DFM Platform

Manufacturability analysis as an API

A general-purpose manufacturability engine: hand it a STEP / glTF, get back a recognized feature graph (holes, bosses, pockets, fillets, ...), per-vertex thickness / clearance / draft fields, and rule-pack verdicts for whichever process you’re evaluating against — CNC, casting, injection molding, additive, sheet metal, or a rule pack you author yourself.

https://digitalmetal.io/api/v1/dfm

What it does

Three analysis layers, all from one upload:

Layer 1

Feature recognition

Every face on the part gets a primitive role. Recognized features (holes, bosses, slots, fins, pockets, protrusions, rounds, fillets) are reported with their dimensions and member faces.

Layer 2

Per-vertex measurements

Local wall thickness, clearance to the closest opposite wall, signed draft angle against the chosen pull direction — available per-vertex, mappable back to any face under the cursor.

Layer 3

Rule modules

Declarative rules combine selectors (which features to inspect), metrics (what to measure), and tier ladders (how the value maps to pass / warning / fail). Ship with shipped packs, or author your own.

Use cases

  • In-house quoting / procurement. Auto-flag manufacturability issues on inbound RFQs before a human looks at them.
  • CAD plug-ins. Run analysis on save and surface findings inline; render the per-face severity over the live model.
  • Custom design guidelines. Encode your house DFM rules (“every wall ≥ 2.0 mm; every hole < Ø1.5 mm reviewed by an engineer”) once, evaluate against every design.
  • AI agents. The MCP server gives an LLM tools to upload, analyze, and author rule packs in-conversation.

Recognized features

Every POST /dfm/analyze result includes a features.features[] array; each entry has a kind,fid (face ID), and dimensions where applicable. Use related_fids to highlight an entire composite feature on the model.

Feature kindDescription
WallPlane-bounded face that forms an outer or inner wall of the part.
SlabPlate-like body region — a thick wall whose two faces are antiparallel planes.
HoleCylindrical (or conical) inward-pointing feature. Carries diameter, depth, and stages for stepped holes.
BossCylindrical (or conical) outward-pointing feature. Carries diameter, height, and base relief geometry.
SlotWall pair forming a through or blind slot. Carries width, depth, and the bridging floor/ceiling face.
FinWall pair forming a thin protrusion. Carries thickness and height.
PocketAggregate inward feature whose member faces line a recess. Members include floor, walls, and any sub-features (holes, bosses) inside.
ProtrusionAggregate outward feature whose member faces form a raised region attached to the part body.
RoundConvex blend (negative bevel) running along an external edge.
FilletConcave blend (positive radius) running along an internal corner.
BendCylindrical region between two planar walls — characteristic of sheet-metal bends.

Shipped rule modules

Five rule packs ship out of the box, each with one or more configurations (e.g. cnc:5-axis versus cnc:3-axis). Hit GET /dfm/modules to fetch their full specs (tier thresholds, parameters, message templates) suitable for cloning into a custom pack.

CNC Machining

key: cncConfigurations: 3-axis, 5-axis

Subtractive milling. Rules govern tool reach, internal radii, undercut access, and hole proportions.

Rules

solid_bodySolid bodyInternal hollows are not machinable — the toolpath cannot reach a sealed inner cavity.
setup_countRequired setupsNumber of workpiece orientations the configuration needs to reach every face. 3-axis: cardinal-only spindle; 5-axis: tilted access cone widens what one setup covers.
min_wall_thicknessMin wall thicknessWalls thinner than the configured floor (chatter / stiffness).
min_internal_radiusInternal corner radiusFillets must exceed tool radius — an end-mill cannot cut a sharper inside corner.
tool_clearanceTool clearance in slots / finsSlot or fin clearance must admit the tool diameter.
undercut_accessUndercut access3-axis: any undercut is unmachinable from the chosen pull. 5-axis: undercuts are usually accessible but flagged for setup.
tool_inaccessibleTool-inaccessible regionFace is occluded along every candidate approach axis. 3-axis: hard fail; 5-axis: warning.
thread_candidatesThread candidatesHoles whose diameter matches a standard tap-drill, and bosses whose diameter matches a standard external major diameter — flagged so the operator can confirm intended threads.
hole_depth_to_diameterHole depth / diameterDeep holes need peck drilling or special tooling.
pocket_aspectPocket aspect / tool reachTight pockets may not admit the configured tool.

Casting

key: castingConfigurations: sand, investment

Cast metal processes — sand and investment. Rules govern wall thickness, mold release, section uniformity, and internal corner stresses.

Rules

solid_bodySolid bodyCasting requires a single closed shell — internal hollow cavities cannot be reproduced by mold filling.
min_wall_thicknessMin wall thicknessCast walls below process minimum risk short fills and cold shuts.
thickness_uniformitySection uniformityLarge thick→thin transitions cause shrinkage cavities and hot spots.
internal_filletInternal fillet radiusSharp internal corners crack on cooling.
sharp_internal_cornerSharp internal cornersConcave edges with no fillet bridging the dihedral are stress risers.

Injection Molding

key: injectionConfigurations: standard, side-action

Plastic injection molding. The strictest geometric rule-set: uniform walls, mandatory draft, no undercuts without side actions.

Rules

solid_bodySolid bodyInjection requires a single closed shell — internal hollow cavities are not reproducible by the molding cycle.
applicabilityApplicability — moldable shellInjection requires a thin-walled shell. FAILs solid bulk parts and warns on highly non-uniform thickness.
wall_thicknessWall thickness uniformityWalls should sit within tolerance of a single nominal value to avoid sink marks and uneven cooling.
draft_requiredDraft for ejectionEvery face needs positive draft from the pull direction. Undercuts FAIL unless side actions are permitted.
no_undercutsNo undercuts (gate)Yes/no gate independent of draft thresholds. Useful as a quick tooling-cost flag.
boss_proportionsBoss h / DTall thin bosses warp during cooling.
rib_thicknessRib thicknessRibs / fins should be ≤ ~60% of wall thickness to avoid sink marks.

Additive Manufacturing

key: additiveConfigurations: dmls, sla, fdm

Layer-wise build processes (DMLS, SLA, FDM). Rules govern min feature size, overhang / self-support angle, and trapped-material risk.

Rules

min_wall_thicknessMin wall thicknessProcess-specific minimum (laser spot / extrusion width / cured-layer thickness).
overhang_angleOverhang / self-supportFaces below the self-support threshold need supports; reverse-facing faces (vs build direction) often FAIL.
trapped_volumeTrapped materialClosed cavities trap unfused powder (DMLS) or uncured resin (SLA). Each pocket needs a drain / escape hole.
min_feature_sizeMin feature sizeFeatures below the process resolution will not print cleanly.

Sheet Metal

key: sheet-metalConfigurations: mild-steel-1.5mm, aluminium-2.0mm, stainless-1.0mm

Punched / bent sheet stock. Rules govern uniform gauge, bend radii, and hole proportions.

Rules

solid_bodySolid bodySheet metal requires a single closed shell — there is no process route to produce an internal hollow from sheet stock.
applicabilityApplicability — sheet shellSheet metal requires a constant-thickness, plane-dominated part. FAILs prismatic / cast / printed 3D parts that aren't single-gauge shells.
uniform_thicknessUniform sheet gaugeSheet metal is constant-thickness. Departure from nominal gauge means the part isn't a single-sheet design.
bend_radiusMin inside bend radiusInside radius < k×sheet thickness cracks during forming.
hole_diameterMin hole diameterPunched holes need diameter ≥ sheet thickness; smaller holes break punches or require EDM.
hole_to_edgeHole-to-edge distanceHoles too close to an edge tear out during punching.

Adaptive meshing

By default, all DFM endpoints resolve their meshing knobs automatically. The backend reads the part’s analytic bounding-box diagonal (sub-second, no meshing required) and scales max_edge and curvature so triangle count stays roughly flat regardless of part size — small parts keep mm-scale features sharp, big parts don’t buy a 30-minute mesh time.

# Default: bbox-driven adaptive density
curl -X POST .../analyze -F "file=@part.step" ...

# Override either / both knobs (gmsh max edge mm, curvature elements/2π)
curl -X POST .../analyze -F "file=@part.step" \
  -F "max_edge=1.4" -F "curvature=12" ...
POST

/dfm/quick-info

Fast geometry-only pass. Sub-second on most parts. Metered under dfm.quick_info.

Request

curl -X POST https://digitalmetal.io/api/v1/dfm/quick-info \
  -H "X-API-Key: $DM_KEY" \
  -F "file=@part.step"

Response 200

{
  "geometry": {
    "volume_cm3": 124.50,
    "surface_area_cm2": 84.20,
    "bounding_box_mm": { "x": 48.30, "y": 32.10, "z": 21.60 }
  },
  "glb_url": "https://blob.vercel-storage.com/dfm/.../preview.glb",
  "stats": { "n_features": 320, "n_vertices": 38693, "n_triangles": 63960 }
}

POST

/dfm/analyze

Submit a full DFM analysis. Metered under dfm.analyze. Returns 202 with a job_id; poll GET /dfm/jobs/{id}, then read GET /dfm/jobs/{id}/result.

Form fields

  • file — .step / .stp / .gltf / .glb, max 25 MB
  • blob_url + filename — alternative to file; the backend fetches server-side
  • quote_id (optional) — attach this analysis to an existing quote row
  • max_edge (optional, default auto)
  • curvature (optional, default auto)
  • pull (optional, default cardinal) — cardinal | hemisphere | x,y,z
  • alpha_min (deg, default 0.5) — undercut threshold
  • alpha_warn (deg, default 3.0) — low-draft warning threshold

Response 202

{ "job_id": "ae91d057fa33", "status": "queued" }

GET

/dfm/jobs/{id}

Job status. Poll every 1–2 seconds.

{
  "job_id": "ae91d057fa33",
  "status": "queued" | "running" | "done" | "error",
  "error":  null | string,
  "filename": "part.step",
  "log": [ "...", "...", "..." ]
}

GET

/dfm/jobs/{id}/result

Full result: recognized features, every shipped module’s rule output, GLB URLs.

{
  "job_id": "ae91d057fa33",
  "status": "done",
  "features": {
    "roles_by_fid":   { "face_0007": 1, "face_0008": 4, ... },
    "role_names":     { "0": "Unclassified", "1": "Hole wall", ... },
    "features": [
      { "fid": "face_0007", "kind": "HOLE", "diameter_mm": 4.0,
        "depth_mm": 8.0, "related_fids": [...] },
      ...
    ],
    "manufacturability": {
      "pull_dir": [0, 0, 1], "alpha_min_deg": 0.5,
      "n_undercuts": 2, "n_low_draft": 4
    }
  },
  "modules": {
    "modules": [
      { "module_key": "cnc", "configurations": [
        { "configuration_key": "5-axis",
          "result": { "overall_severity": "warning",
                      "summary": { "pass": 7, "warning": 2, "fail": 0 },
                      "rules":   [ ... ] } } ] },
      ...
    ]
  },
  "artifacts": {
    "thickness_glb": "https://...",
    "clearance_glb": "https://...",
    "features_glb":  "https://...",
    "draft_glb":     "https://...",
    "features_json": "https://...",
    "modules_json":  "https://..."
  }
}

POST

/dfm/jobs/{id}/recompute

Re-run draft + recognition + modules with a new pull direction. Mesh is cached.

# pick a different pull
curl -X POST .../jobs/ae91d057fa33/recompute \
  -H "X-API-Key: $DM_KEY" \
  -F "pull=0,0,-1"

Custom rule modules

The rule engine is exposed as a declarative grammar — selectors pick a subset of recognized features, a metric is read from each, and a tier ladder maps the metric to pass / warning / fail. Author your own module spec and run it against any completed analysis, no redeploy required.

GET

/dfm/rule-schema

Catalog of selectors, metrics, comparison ops, RHS forms. Read this before authoring.

{
  "selector_sets": ["walls", "holes", "bosses", "fillets", "rounds",
                    "pockets", "slots", "fins", "all_features", ...],
  "metrics_per_feature": ["thickness.min", "thickness.mean", "draft.min",
                           "hole.diameter", "boss.height_over_diameter",
                           "axis_angle_to_pull", "area_mm2", ...],
  "metrics_whole_part":  ["part.thickness.cv", "part.undercut_count", ...],
  "comparison_ops": ["lt","le","gt","ge","eq","ne"],
  "severity_levels": ["pass","warning","fail"],
  "rhs_forms": [
    { "shape": "<number>", "doc": "literal" },
    { "shape": "{\"param\":\"<name>\"}", "doc": "configuration parameter" },
    { "shape": "{\"param\":\"<name>\",\"factor\":<x>}", "doc": "param × factor" }
  ]
}

GET

/dfm/modules

Every shipped module spec, suitable for cloning + editing into a custom module.


POST

/dfm/jobs/{id}/modules/eval

Evaluate a caller-supplied module spec against a completed job. Same return shape as a single module entry in GET /jobs/{id}/result.

Request body (application/json)

{
  "module": {
    "key": "house",
    "name": "House DFM rules",
    "configurations": {
      "default": { "name": "Default", "parameters": { "min_wall_mm": 1.5 } }
    },
    "rules": [{
      "key": "min_wall",
      "name": "Minimum wall thickness",
      "scope": "per_feature",
      "selector": { "set": "walls" },
      "metric":   "thickness.min",
      "tiers": [
        { "level": "fail",    "op": "lt", "rhs": { "param": "min_wall_mm", "factor": 0.7 } },
        { "level": "warning", "op": "lt", "rhs": { "param": "min_wall_mm" } },
        { "level": "pass" }
      ],
      "messages": {
        "fail":    "{fid}: {metric:.2f} mm < {threshold:.2f} mm (severe)",
        "warning": "{fid}: {metric:.2f} mm < {threshold:.2f} mm",
        "pass":    "all {n_inspected} walls ≥ {threshold:.2f} mm"
      }
    }]
  },
  "configuration_key": "default",
  "overrides": { "min_wall_mm": 2.0 }
}

End-to-end example

JOB=$(curl -sS -X POST https://digitalmetal.io/api/v1/dfm/analyze \
  -H "X-API-Key: $DM_KEY" -F "file=@part.step" | jq -r .job_id)

while [ "$(curl -sS https://digitalmetal.io/api/v1/dfm/jobs/$JOB \
            -H "X-API-Key: $DM_KEY" | jq -r .status)" != "done" ]; do
  sleep 2
done

curl -sS https://digitalmetal.io/api/v1/dfm/jobs/$JOB/result \
  -H "X-API-Key: $DM_KEY" \
  | jq '.modules.modules[] | { module: .module_key,
        verdicts: [.configurations[] | { conf: .configuration_key,
                                         severity: .result.overall_severity }] }'

Error codes

  • 401 — missing / revoked / malformed API key
  • 402 — metered quota exhausted on the free tier; upgrade to add overage
  • 404 — job_id not found (or expired from cache)
  • 413 — file exceeds 25 MB
  • 422 — unsupported extension / build-volume violation / invalid module spec
  • 504 — meshing exceeded 5-minute timeout (try a coarser max_edge override)

See also