# Design Ønce > Manufacturability (DFM) review for CAD assemblies. Upload STEP files, run analysis, and work through the findings it raises. Every route below the sign-in page belongs to one account. There is no public catalogue to crawl and no anonymous read path: an agent sees what the user it is acting for can see, and nothing else. ## What the product does An engineer uploads a CAD assembly as a STEP file. Design Ønce reads the geometry, works out how each part would be made, and reports the features that will make it difficult or expensive to manufacture. The engineer works through those findings, changes the model, uploads the next revision, and compares the two. ## Object model - **Project** — one assembly, tracked over time. Owned by a user, shared with a tenant or with named people. - **Project version** — one revision of that assembly. Versions sit on branches, so a project is a history, not a single file. - **Part version** — one part inside one project version. - **DFM report** — the manufacturability analysis of a single part version. - **Item** — one thing to deal with on a project. An item raised by the analysis carries an insight type; an item raised by a person does not. Every change to an item is recorded in its own audit log. A part carries a manufacturing method, which analysis tries to determine and a person can correct. The method decides which findings apply, so a part with the wrong method reports the wrong problems. ## In-browser tools An open project page publishes `window.DesignOnce`, version 1: 14 tools that read the analysis. Call `DesignOnce.describe()` for the full JSON Schema of every input and output; it answers before a project is open. Distances are millimetres and angles radians, and each field name carries its unit. **Every tool is read-only.** Nothing here uploads a file, sets a manufacturing method, dismisses a finding or writes a comment. Those remain the person's to do. - `DesignOnce.getSession` — The open project: its active revision, its parts, and which bodies are loaded and visible in the renderer. Start here. - `DesignOnce.listRevisions` — Every revision of the open project, oldest first, with its revision number and part counts. - `DesignOnce.listParts` — Parts in the active revision, with their manufacturing method, material and placements. - `DesignOnce.getPart` — One part version in the active revision: its assembly path, its measured volume and surface area, and what each analysis job has done for it. The measurements come from the part's features report, so they are absent until that has succeeded. - `DesignOnce.listFindings` — Review rows for the open project. Returns both findings (generated, still untriaged) and comments (everything the To do list owns, including promoted findings and anything a person wrote) — read each row's "bucket". By default rows the user has dismissed, and rows hidden because their manufacturing method differs from the part's, are left out. - `DesignOnce.getFinding` — One review row in full, including its rich-text body. Resolves rows that listFindings hides by default. - `DesignOnce.getDfmReport` — One part's manufacturing analysis: the features it found, the problems it raised, the solutions it suggests, and the machining setups it generated. The feature list is parked rather than returned — read it with results.query. - `DesignOnce.listProblems` — The problems in one part's manufacturing analysis, each with the features it warns about and the solutions raised for it. Get the part's report first: it says how many problems there are and of what type. - `DesignOnce.listFeatures` — The geometry recognised on one part: holes, thin walls, sharp corners, mould pulls, sheet bends. Every measurement names its unit. The whole unfiltered set is parked, and the same handle comes back however the call was filtered. - `DesignOnce.getAssemblyReport` — How the parts of the open revision sit together: which bodies overlap, which overlap a workholding fixture, which bores share an axis, and which surfaces touch. Points are not all in one frame — each field says which. - `DesignOnce.getRevisionDiff` — What changed between the open revision and the one before it: parts added, removed and changed, each changed part's volume and surface counts, and the individual volumes of material gained or lost. A comparison still running is reported as such rather than as no change. - `DesignOnce.results.get` — A parked result set in full, by handle. Prefer results.query, which pages. - `DesignOnce.results.query` — A page of a parked result set, optionally filtered by field equality. - `DesignOnce.results.list` — Parked result sets for the open project, with whether each still describes current data. Two different things happen to long results, and they are not interchangeable. The list tools page: 50 rows by default, 200 at most, and the answer's `truncated` says whether more remain. Advance `offset` until it is false; nothing is parked and no handle is involved. A report tool instead parks its rows and returns a handle plus the totals. Read those back with `DesignOnce.results.query`. A handle stops answering once the report it describes has been replaced. A failed call throws an error carrying a `code`: - `noProject` — no project is open, so there is nothing to answer about - `sessionChanged` — the project or revision changed while the call was running - `aborted` — the caller's `AbortSignal` fired - `invalidArgs` — the arguments do not match the tool's input schema - `notFound` — the named thing is not in the revision that was answered for - `staleHandle` — the saved result set no longer describes current data - `expiredHandle` — the saved result set timed out or was dropped - `unavailable` — the answer cannot be established, so none is given rather than a wrong one - `internal` — anything unanticipated `unavailable` is the one to read carefully: it means the tool declined to guess, not that the answer is nothing. ## Analysis is asynchronous Nothing here answers in one request. Analysis runs as batch jobs in these flows — `dfm`, `features`, `assembly`, `part-diff`, `classification`. A job is started for a set of targets and then polled. Each target's row reports `NotFound` (never started, or not visible to this account), `Generating` (still running), `Finished` (the run ended — successfully or not). Those are the HTTP rows the app itself polls. `Finished` among them does not mean it worked: the row also carries `success`, and a report can only be fetched when that is true. The tools do not pass those fields through. `DesignOnce.getPart` answers with one state per job instead: - `notStarted` — nothing has asked for it - `generating` — running - `stale` — running far longer than it should, so something has gone wrong that was never reported - `succeeded` — finished, and its report can be read - `failed` — finished without a report, and asking again will not change that - `refused` — it will not start, and asking again cannot help Only `succeeded` has a report behind it. An agent that treats an upload as complete when the request returns will read an empty report and conclude the assembly is clean. Wait for the job rows. `classification` has no start route: it is queued behind the features job, part by part, and its result is the part's own manufacturing method rather than a report. ## Setting up a part Two settings decide which findings appear and what they say, and both default to something an agent should not trust: - **Manufacturing method.** Auto-classification leaves most parts unset. The picker offers **Milling**, **Injection Molding**, **Sheet Metal Bending**, **Off-The-Shelf**, **Other**. A part left unset reports almost nothing. - **Material.** Defaults to Mild steel on every part, including parts set to injection molding. The choices are **Mild steel**, **Stainless steel**, **Alloy steel**, **Aluminium**, **Plastics**. ## Authentication Sign-in is WorkOS AuthKit, OpenID Connect. Where this deployment has a WorkOS client, https://app.designonce.ai/agents.json carries its OpenID discovery document as `authorization.discovery`; a deployment without one omits the field rather than naming a URL that does not answer. Every API request carries `Authorization: Bearer ` and an `X-UTC-Offset` header giving the caller's UTC offset in minutes; timestamps come back resolved against it. There is no API key and no client-credentials grant. An agent cannot hold its own credentials — it acts inside a signed-in user's session, which is why the in-browser tools above are the surface to use rather than the HTTP API. ## Access refusals A refusal carries a JSON body `{ "denial_code": ... }`. Read the code before the status. - `needs_demo` (402) — The account has no demo booking and no manual grant. - `subscription_required` (402) — The tenant requires billing and has no usable subscription. - `demo_project_revision_locked` (403) — Revision uploads to demo projects are refused. Everything else on a demo project works. An account behind the gate does not see its non-demo projects refused: they read as absent. An empty project list means "not entitled", not "no projects". Do not create a replacement project on that reading. ## Routes - `/` — Project list, and the entry points for creating one. - `/projects/{projectId}` — One project. - `/projects/{projectId}/summary` — What changed between two revisions. - `/projects/{projectId}/analysis` — Findings across the assembly. - `/projects/{projectId}/analysis/{itemId}` — One finding. - `/projects/{projectId}/part/{partId}` — One part and its report. - `/projects/{projectId}/versions` — Revision history. - `/projects/{projectId}/sharing` — Who can reach the project. - `/projects/{projectId}/settings` — Project settings. - `/settings` — Account settings. ## Machine-readable declaration - https://app.designonce.ai/agents.txt - https://app.designonce.ai/agents.json - https://app.designonce.ai/skills/design-once/SKILL.md