API and tool reference

Connect your projects, start designs and read finished files through the API or a tool client.

Last updated: 5 September 2026

Connect

The API runs at https://make-api.unwrite.co. Start with the machine-readable reference, which you can read without a key.

curl --fail-with-body 'https://make-api.unwrite.co/v1/public/make'

The same surface is served as an OpenAPI 3.1 document at /v1/public/make/openapi.json, also without a key, for generating a client.

In Settings, API keys, create a named API key and choose its scopes. You can allow all your projects or selected projects; a key never opens another account’s projects. Copy the key when it appears, because you won’t see it again.

Send Authorization: Bearer YOUR_API_KEY with each authenticated request. Keep keys in your server’s secret store, outside browser code and source control.

Read a project

Replace the project ID and set UNWRITE_API_KEY in your environment. This request needs projects:read and returns a project object.

curl --fail-with-body \
  -H "Authorization: Bearer $UNWRITE_API_KEY" \
  'https://make-api.unwrite.co/v1/public/make/projects/PROJECT_ID'

Read a saved version with revisions:read, then list its files with exports:read. The responses contain revision and exports, respectively. Export records include the format, size, checksum and download URL. Send the same bearer key to that URL to download the file; it must belong to a revision this key opens.

Inspect an assembly

Read GET /v1/public/make/assemblies/{assemblyId} to get the current assembly revision ID and component IDs. With assemblies:read, send the following template to POST /v1/public/make/assemblies/{assemblyId}/inspect-pair.

{
  "assemblyRevisionId": "ASSEMBLY_REVISION_ID",
  "componentA": "FIRST_COMPONENT_ID",
  "componentB": "SECOND_COMPONENT_ID"
}

The inspection response reports the pair’s clearance in millimetres, overlapping volume in cubic millimetres and relationship, plus the source revision IDs and checksums. Read unknownCount before relying on a measurement; an unknown result doesn’t establish clearance. If the assembly changes during the check, a 409 asks you to read its latest revision and try again.

Tool clients can call make_inspect_assembly_pair with the same fields plus assemblyId. The check uses bounded geometry runtime capacity and never changes a design. A 429 or 503 means capacity isn’t available yet; wait before retrying.

Start a design and follow progress

POST /v1/public/make/designs needs designs:write and spends account credit. Send JSON with requirements (1 to 20,000 characters), and projectId. You can omit the project ID when the key covers exactly one project, or when it covers the whole account, in which case a new project is made for the design and named after what you asked for.

To make a project yourself, send {"name": "..."} to POST /v1/public/make/projects with designs:write and an Idempotency-Key; the answer carries the new project and its ID. List what a key already opens with GET /v1/public/make/projects, 50 to a page, sending the nextCursor back as before. Tool clients have the same two as make_create_project and make_list_projects.

The optional mode accepts unattended or step_by_step. Send Content-Type: application/json and an Idempotency-Key header with the request.

Set detail: true to choose Detail Mode before the run starts. It works for longer and in more detail, so it uses more credit, and like any design it’s charged for what it uses. Omit it or set it to false for an ordinary design; the MCP tool accepts the same option.

{
  "projectId": "PROJECT_ID",
  "requirements": "YOUR_DESIGN_DESCRIPTION",
  "mode": "unattended"
}

A 202 response contains run, including its status, phase and version. It acknowledges the request, not a finished design. Open the project in Make to follow the build, answer questions and download the result.

Repeating the same request with the same idempotency key returns the project’s current run without charging for another design. Treat this as retry recovery; the public API lets you follow progress with GET /v1/public/make/designs/{projectId} and projects:read. This returns the current run without spending credit. Poll every few seconds, stop when the run finishes, and open Make if it needs an answer or approval. Use GET /v1/public/make/usage with the same scope to check credit.

Follow and stop work

GET /v1/public/make/jobs/{jobId} with projects:read returns a job carrying its status (queued, running, succeeded, failed or cancelled), a progress sentence, the resultRevisionId to read files from once it succeeds, and failureCode with failureMessage when it did not. A design that is waiting for your approval or an answer shows that on GET /v1/public/make/designs/{projectId} instead, because the wait belongs to the design rather than to a build.

POST /v1/public/make/jobs/{jobId}/cancel with jobs:write stops a build that is still queued or running. POST /v1/public/make/designs/{projectId}/cancel with designs:write stops a design: it ends the run, releases the machine it was using and stops the build it had asked for. Neither needs an Idempotency-Key, because both are safe to send again: work that has already stopped or finished comes back as it stands.

What cancelling costs: whatever the work had already done stays charged, and whatever was set aside for work that never happened returns to your account when the run settles. Sending the same cancel twice charges nothing extra. A design refused before it started costs nothing at all.

Tool clients have the same three as make_get_job, make_cancel_job and make_cancel_design. If you close the connection on a call that was reaching for a machine, that reach is stopped too, on the same terms.

Change a parameter

Send this request template to POST /v1/public/make/revisions/{revisionId}/mutations with revisions:write, your bearer key, Content-Type: application/json and an Idempotency-Key. Replace the field values with the feature and parameter you want to change; value can be a number, string or boolean, according to the parameter.

{
  "mutation": {
    "type": "set_feature_parameter",
    "featureId": "FEATURE_ID",
    "parameter": "PARAMETER_NAME",
    "value": "PARAMETER_VALUE"
  }
}

The response contains the new revision. To inspect feature IDs and parameter names before editing, read the revision with both revisions:read and revisions:graph.

Retry safely

Every write needs an Idempotency-Key: 8 to 128 characters, starting with a letter or number, then letters, numbers, dots, colons, hyphens or underscores. Keep the same key, project and body when recovering from a dropped connection. Use a new key for each new operation, even if you use another API key on the same account.

  • Parameter changes return 201 on creation and 200 on retries with the saved revision.
  • Design starts and build retries return 202, including successful retries.
  • A 409 idempotency_key_in_use means the server can’t return a saved result yet. Check the project before starting another operation with a fresh key.

When a request fails before creating anything, the API releases its claim. Correct the problem before retrying; it doesn’t cache the refusal permanently.

A key is remembered for 35 days from the call that first used it. Inside that window, a replay returns what the first call produced: the saved revision for a parameter change, the project’s current run for a design start, the same build for a retry. After 35 days the key is forgotten and the same call would be treated as new work, so use a fresh key per operation and you will never reach that edge.

If two calls share one key at the same moment, only one does the work. The other is answered from the claim: 409 idempotency_key_in_use while the first is still going, or 409 build_already_running when a build retry races another retry of the same build. Read the current state before deciding what to do next.

Two design starts with different keys on one project never both pay. While the first is still between taking the credit and starting, the second gets 409 design_start_in_progress and its key is handed straight back. Once the first run is live, the second is answered with that run.

Credit and how it's charged

Prepaid credit is charged by usage. When a design starts we set aside the most it could use, which is US$7.04, or US$28.14 in Detail Mode because it works for longer. If your balance is lower than that, the design runs on what you have, as long as it's at least US$1.00. When the design ends you're charged for what it actually used and the rest goes straight back to your balance.

Usage is charged whether or not the design finished. A design that failed, or one you stopped, still did the work it's charged for, and it costs only that. If we can't tell what a design used, it's charged everything that was set aside for it. Some older top-ups also carry a separate generation capacity that earlier failed attempts used up without spending the money. If that runs out first, the API answers usage_capacity_exhausted, your money stays in your account, and we'll refund it if you ask.

Legacy credit keeps its original terms: US$0.50 for an ordinary design, returned in full if the run doesn't finish. It can't be bought any more. New top-ups are charged by usage, and buying more doesn't renew the old offer.

Read GET /v1/public/make/usage before starting work, and check Credit and usage for your balance, what each design cost and your daily spending limit. Reading progress doesn’t spend credit. Reuse the same idempotency key when retrying a start request so you don’t start and pay for another design.

Limits and recovery

Both the API key and the calling IP address have rate limits, so keys sharing an office connection also share its address limit. On 429, wait for the number of seconds in Retry-After before trying again.

A 402 usage_balance_required needs more credit. The daily spending and design retry limits lift at midnight UTC and include Retry-After. A refused design start doesn’t start work or charge for it.

A 402 usage_capacity_exhausted means your prepaid generation capacity is used up. Your remaining credit stays in your account. Waiting until midnight or automatically retrying won’t restore capacity.

A request body over 64 KB is refused with 413 body_too_large before anything is read out of it. Every route answers that way, and so does the tool endpoint.

REST errors contain error.code and error.message. Branch on the code; the message can change. The reference below lists the codes and statuses.

Tool clients

Use https://make-api.unwrite.co/v1/public/make/mcp with your bearer key. The endpoint accepts JSON-RPC POST requests for initialize, tools/list and tools/call. tools/list returns each tool’s argument schema.

After starting a design, call make_get_design with its projectId to follow progress. The server supports Streamable HTTP without sessions. Send the protocol version returned by initialize in the MCP-Protocol-Version header on later requests.

{"jsonrpc":"2.0","id":1,"method":"tools/list"}

tools/list needs no key, so a client can read the argument shapes while it is still being set up. Every tools/call needs the key and its scope.

Write tools take idempotencyKey as an argument. Successful calls include result.structuredContent; failures carry the operation code in error.data.code. HTTP rate limits still apply, so check the HTTP status before reading the JSON-RPC body.

Set UNWRITE_API_KEY in your environment, then add the server to your client of choice. For opencode, in opencode.json:

{
  "mcp": {
    "unwrite-make": {
      "type": "remote",
      "url": "https://make-api.unwrite.co/v1/public/make/mcp",
      "headers": { "Authorization": "Bearer {env:UNWRITE_API_KEY}" },
      "oauth": false,
      "enabled": true
    }
  }
}

For Claude Code, in .mcp.json at the root of your project:

{
  "mcpServers": {
    "unwrite-make": {
      "type": "http",
      "url": "https://make-api.unwrite.co/v1/public/make/mcp",
      "headers": { "Authorization": "Bearer ${UNWRITE_API_KEY}" }
    }
  }
}

For Cursor, in .cursor/mcp.json. Paste the key into the header value; Cursor reads this file as written rather than expanding environment variables in it, so keep the file out of source control.

{
  "mcpServers": {
    "unwrite-make": {
      "url": "https://make-api.unwrite.co/v1/public/make/mcp",
      "headers": { "Authorization": "Bearer YOUR_API_KEY" }
    }
  }
}

Standard parts

GET /v1/make/standard-parts accepts q for search, limit up to 200 and from for pagination. A valid key returns full records and the matching count, while anonymous searches return a smaller selection.

Revoke or replace a key

Revoke a key in Settings, API keys to stop its next request. To rotate a key without interrupting your integration, create a replacement, switch your integration across and then revoke the old key.

Operations

  • GET /v1/public/make

    No operation scope · This description. No key needed.

  • GET /v1/public/make/projects

    projects:read · Every part this key opens, most recently worked on first, 50 to a page. Send the nextCursor you were given back as the before parameter for the page after that. Start here: everything else needs a part named.

  • POST /v1/public/make/projects

    designs:write · Make a new part to work on. Send {"name": "..."} and an Idempotency-Key header. Answers 201 the first time and 200 with the same part if you send it again. Needs a key issued over the whole account.

  • GET /v1/public/make/openapi.json

    No operation scope · This same surface as an OpenAPI 3.1 document. No key needed.

  • GET /v1/public/make/projects/{projectId}

    projects:read · One of the parts this key opens.

  • GET /v1/public/make/revisions/{revisionId}

    revisions:read · One saved version of a design, with the requirements it was built to. The feature graph rides along only for a key that also carries revisions:graph.

  • POST /v1/public/make/revisions/{revisionId}/mutations

    revisions:write · Change one parameter and save the result as a new version. Needs an Idempotency-Key header. Answers 201 the first time and 200 with the same version if you send it again.

  • GET /v1/public/make/revisions/{revisionId}/exports

    exports:read · The finished files for a version, which is where the geometry lives.

  • GET /v1/public/make/revisions/{revisionId}/exports/{exportId}/content

    exports:read · Download a finished file using the same API key. The file must belong to this revision.

  • GET /v1/public/make/projects/{projectId}/assemblies

    assemblies:read · The assemblies saved against one part, newest first, 50 to a page. Send the nextCursor you were given back as the before parameter for the page after that.

  • GET /v1/public/make/assemblies/{assemblyId}

    assemblies:read · One assembly and its parts list.

  • POST /v1/public/make/assemblies/{assemblyId}/revisions

    assemblies:write · Save a new revision of an assembly from a complete definition, sent as {"assembly": ...}. The solver runs again. Needs an Idempotency-Key header, and answers 201 the first time and 200 with the same revision if you send it again. The tool list carries the full assembly schema.

  • POST /v1/public/make/assemblies/{assemblyId}/inspect-pair

    assemblies:read · Measure exact clearance or overlapping volume between two assembly parts at a specified assembly revision. Uses bounded geometry runtime capacity without changing the design.

  • GET /v1/public/make/jobs/{jobId}

    projects:read · How one build is going: queued, running, succeeded, failed or cancelled, what that means in words, and the version it produced.

  • POST /v1/public/make/jobs/{jobId}/retry

    jobs:write · Run a build again after it failed or was stopped. Needs an Idempotency-Key header.

  • POST /v1/public/make/jobs/{jobId}/cancel

    jobs:write · Stop a build that is still queued or running. Send it again as often as you like: a build that has already stopped or finished comes back as it stands.

  • POST /v1/public/make/designs/{projectId}/cancel

    designs:write · Stop a design that is still going. On current credit the work already done stays charged and anything set aside for work that never happened goes back. On legacy credit a run that doesn't finish is refunded in full. Send it again as often as you like.

  • POST /v1/public/make/designs

    designs:write · Start a new design from a written description, on the part you name in projectId. You can leave projectId out if this key opens exactly one part. This is the one call that costs money: it sets aside the most the design could use, charges what it did use when it ends, and returns the rest. It is also available as a tool.

  • GET /v1/public/make/designs/{projectId}

    projects:read · Read the project's current design progress and questions without spending credit.

  • GET /v1/public/make/usage

    projects:read · What is left of the credit on this account, in money, and the most the next design can cost.

  • GET /v1/make/standard-parts

    No operation scope · Search the standard parts library. `q` searches, `limit` sizes the page and `from` moves through it. Any working key gets the full page, a count and paging past the first page; without one you get a shorter page and no count.

  • POST /v1/public/make/mcp

    No operation scope · The same operations as tools, over JSON-RPC, for a client that speaks the Model Context Protocol. Point your client here and send the key as a bearer token.

Scopes

  • projects:read: Read the parts this key opens.
  • revisions:read: Read saved versions and the requirements behind them.
  • revisions:graph: Read the internal shape description on a version as well. Ask for this only if you need it.
  • revisions:write: Change a parameter and save a new version.
  • exports:read: List the finished files for a version.
  • assemblies:read: Read assemblies and their parts lists.
  • assemblies:write: Save a new revision of an assembly you already own.
  • jobs:write: Run a build again, or stop one that is still going.
  • designs:write: Start a new design, or stop one. This is the one scope that spends money, so leave it off any key that only needs to read.

Available tools

  • make_inspect_assembly_pair: Measure the exact clearance or overlapping volume between two parts of an owned assembly. Read the assembly first and send its current revision ID. This check is subject to build-service limits and does not change the design.
  • make_list_assemblies: List the saved assemblies of one part, newest first, 50 to a page. Send the nextCursor you were given as `before` to read the page after that. Name the part unless this key opens exactly one.
  • make_get_assembly: Read one saved assembly at its latest revision: the parts and where each one sits, the mate diagnostics, the bill of materials and the robot descriptions.
  • make_revise_assembly: Save a new revision of an owned assembly from a complete assembly definition. The solver runs again and the answer carries the new revision. Send the same idempotency key again to get the first revision back rather than saving a second one.
  • make_get_design: Read a project's current design status, progress and questions after starting a design. This doesn't spend credit. Name the project unless this key opens exactly one.
  • make_list_projects: List projects accessible to this API key, most recently updated first, 50 per page. Pass nextCursor as before to fetch the next page. Use this to find a projectId when needed.
  • make_create_project: Create a project and return its ID. Reuse the same idempotencyKey to return the original project instead of creating another. Requires an account-wide API key.
  • make_get_project: Read a project. Supply projectId unless the API key is restricted to one project.
  • make_get_revision: Read one immutable project revision. Geometry comes from exports; use make_list_exports for STEP, STL, 3MF and GLB.
  • make_list_exports: List generated exports for an immutable project revision.
  • make_get_assembly_bom: Read the grouped bill of materials for an owned assembly.
  • make_retry_job: Retry a failed or cancelled build job with an idempotency key.
  • make_get_job: Read a build job's status, explanation and resulting revision, when available.
  • make_cancel_job: Stop a build that is still queued or running. Safe to send again: a build that has already stopped or finished comes back as it stands.
  • make_cancel_design: Cancel an active design. With current credit, the work already done stays charged and anything set aside for work that never happened goes back to the balance. With legacy credit, an unfinished run is refunded in full. Repeating this call is safe. Supply projectId unless the API key is restricted to one project.
  • make_start_design: Start a paid design from a written description in a project. Supply projectId unless the API key is restricted to one project. Reuse idempotencyKey to return the original run instead of starting another.
  • make_get_usage: Check the account’s remaining credit before starting paid work.
  • make_download_export: Fetch one finished file for a revision, as base64 bytes you can save. Use make_list_exports first to find the file. A file larger than 8 MB is fetched from its download URL instead.
  • make_search_standard_parts: Search the standard parts library: fasteners, bearings and the rest, with the dimensions a design has to be built around.
  • make_set_feature_parameter: Save a new version of a design with one parameter changed. Send the same idempotency key again to get the first result back rather than making a second version.

Error reference

  • MODEL_SPEND_CONTEXT_REQUIRED (503): We've paused this run because we couldn't check its allowance. The problem is on our side, and your design is saved.
  • MODEL_SPEND_HISTORY_UNPRICED (409): We've paused this run because some earlier work has no recorded cost, so we can't confirm the remaining allowance. Your design is saved; you can review it while this cost issue remains unresolved.
  • MODEL_SPEND_LIMIT_REACHED (429): This run has paused because the next step would exceed the available allowance. Your design is saved. Check your usage before continuing.
  • MODEL_SPEND_QUOTE_EXCEEDED (503): We've paused this run because its cost exceeded the confirmed limit. Your design is saved. Check your usage before deciding whether to continue.
  • MODEL_SPEND_QUOTE_UNAVAILABLE (503): We've paused this run because we couldn't confirm the cost of its next step. The problem is on our side, and your design is saved.
  • assembly_inspection_cancelled (409): You cancelled this check.
  • assembly_inspection_invalid (502): We couldn't verify the check for these parts.
  • assembly_inspection_unavailable (503): We couldn't check these parts just now. Try again shortly.
  • assembly_not_found (404): No assembly of that name is open to this key.
  • assembly_revision_changed (409): The assembly has changed. Read its latest revision and try again.
  • body_too_large (413): That request body is larger than the 64 KB this API accepts. Send a smaller one.
  • design_retry_limit_reached (429): A lot of designs have stopped short on this account today, so we've paused new ones until midnight UTC. None of them cost you anything. If something on our side is breaking them, tell us and we'll put it right.
  • design_start_in_progress (409): Another call is already starting a design for this part. Read the project shortly and send this call again if it is still needed.
  • export_not_found (404): No export of that name is open to this key.
  • export_too_large (413): That file is too large to send inside a tool answer. Fetch it from the download URL on the file instead.
  • idempotency_key_in_use (409): That idempotency key is already in use for a different change. Use a fresh one.
  • idempotency_key_required (400): Send an Idempotency-Key header of 8 to 128 letters, numbers, dots, colons, hyphens or underscores, so a repeated call does the work once.
  • invalid_arguments (400): One of the values sent isn't the right shape. Check them and retry.
  • invalid_assembly_pair (400): Choose two different parts from this assembly.
  • invalid_mutation (400): Describe the change as one bounded parameter change and retry.
  • job_not_found (404): No build of that name is open to this key.
  • key_expired (401): That API key has passed its expiry date, so create a new one to carry on.
  • key_invalid (401): That API key isn't one we recognise, so check it, or create a new one.
  • key_revoked (401): That API key was revoked, so create a new one to carry on.
  • mutation_refused (422): That change can't be made to this design.
  • project_creation_denied (403): This key was issued for named parts, so it cannot make new ones. Use a key that covers the whole account.
  • project_not_found (404): No project of that name is open to this key.
  • request_cancelled (409): You stopped this call before it began, so nothing was started and nothing was charged.
  • revision_not_found (404): No revision of that name is open to this key.
  • unauthenticated (401): Send your API key as an Authorization header, in the form Bearer followed by the key.
  • unsupported_media_type (415): Send the body as JSON, with a Content-Type header of application/json.
  • usage_balance_required (402): There isn't enough credit on this account to start a design. It needs at least US$1.00, so add credit to carry on.
  • usage_capacity_exhausted (402): Your prepaid generation capacity is used up. Your remaining credit is still in your account.
  • usage_daily_cap_reached (429): This account has reached the daily spending limit set on it. It lifts at midnight UTC.
  • scope_denied (403): The key works but was not given the scope this call needs.
  • rate_limited (429): Too many calls too quickly. Wait as long as the Retry-After header says.
  • daily_job_limit_reached (429): The day's builds are used up. Wait as long as the Retry-After header says.
  • build_already_running (409): This project is already building. Wait for that build to finish.
  • revision_not_confirmed (409): Nobody has confirmed this version of the design, so there is nothing to build.

Read the Make terms of service