# Designs

Printable model files.

## Endpoints

-   [POST `/v2/products/{id}/designs`](https://public-api.dapi.digital/docs/reference/designs.md#add_design)
-   [GET `/v2/designs/{id}`](https://public-api.dapi.digital/docs/reference/designs.md#get_design)
-   [PATCH `/v2/designs/{id}`](https://public-api.dapi.digital/docs/reference/designs.md#update_design)
-   [DELETE `/v2/designs/{id}`](https://public-api.dapi.digital/docs/reference/designs.md#delete_design)

## The design object

A printable model: its files, the materials it can be printed in and how it is produced. Every design is reviewed by 3DAPI before it can be manufactured.

### Attributes

-   `id` (string): Unique identifier of the design, prefixed with `dsg_`. Treat it as an opaque string: store it and compare it, never parse it.
-   `object` (string): Names the type of this object. Always `design`.
-   `name` (string): Display name, usually the name of the uploaded file.
-   `owner` (object): The product or stock this design belongs to. A design always belongs to exactly one of them; check `object`.
    -   Shape **Reference** (default):
        
        Reference to a product. Pass the matching `expand[]` path to receive the full product instead.
        
        -   `id` (string): Unique identifier of the product, prefixed with `prd_`. Treat it as an opaque string: store it and compare it, never parse it.
        -   `object` (string): Names the type of this object. Always `product`.
        -   `name` (string): Display name of the product.
    -   Shape **Reference** (default):
        
        Reference to a stock. Pass the matching `expand[]` path to receive the full stock instead.
        
        -   `id` (string): Unique identifier of the stock, prefixed with `stk_`. Treat it as an opaque string: store it and compare it, never parse it.
        -   `object` (string): Names the type of this object. Always `stock`.
        -   `name` (string): Display name of the stock.
-   `review` (Review): Review by 3DAPI. Anything that will be manufactured is reviewed before it can be sold or produced.
    -   `status` (ReviewStatus):
        
        Where 3DAPI stands on reviewing this object:
        
        -   `pending`: waiting for review by 3DAPI. Nothing to do on your side.
        -   `approved`: reviewed and accepted.
        -   `rejected`: reviewed and refused. `reason` says why; fix it and save again to resubmit.
        
    -   `reason` (string or null): Why it was rejected. Null unless `status` is `rejected`.
    -   `reviewed_at` (timestamp or null): When the latest review decision was made. Null while pending.
-   `files` (array of DesignFile): The model files of this design. Usually exactly one.
    -   `name` (string): File name.
    -   `format` (string): File format.
        
        Possible values: `3mf`, `stl`.
        
    -   `size_bytes` (integer or null): File size in bytes. Null only when the size cannot be read from storage.
    -   `download_url` (string): Signed URL to download the file. Valid for 15 minutes; fetch the design again for a fresh one.
    -   `id` (string): Unique identifier of the design file, prefixed with `dfl_`. Treat it as an opaque string: store it and compare it, never parse it.
    -   `object` (string): Names the type of this object. Always `design_file`.
    -   `uploaded_at` (timestamp): When the file was uploaded.
    -   `production_version` (object or null): The copy 3DAPI adapted to the production printer (printer, process and filament settings written in). This is the file that gets printed. Null when your upload is printed as it is.
        -   `name` (string): File name.
        -   `format` (string): File format.
            
            Possible values: `3mf`, `stl`.
            
        -   `size_bytes` (integer or null): File size in bytes. Null only when the size cannot be read from storage.
        -   `download_url` (string): Signed URL to download the file. Valid for 15 minutes; fetch the design again for a fresh one.
        -   `prepared_at` (timestamp): When 3DAPI prepared this version.
-   `materials` (object): Which materials the design may be printed in. `mode` tells the two shapes apart.
    -   Shape **mode: single**:
        
        -   `mode` (string): The whole design prints in one material.
        -   `allowed` (array of MaterialReference): Materials buyers or variants may choose from. Each variant picks one.
            -   `id` (string or null): Unique identifier of a catalog material, prefixed with `mat_`. Null for a custom material.
            -   `object` (string): Names the type of this object. Always `material`.
            -   `name` (string): Display name, for example `PLA Black`. For custom materials, exactly what the seller typed.
            -   `custom` (boolean): True for a seller-specified material that is not in the catalog. Custom materials need manual review before production.
    -   Shape **mode: multicolor**:
        
        -   `mode` (string): The design prints in several materials at once.
        -   `combinations` (array of object): Saved material combinations a variant can use.
            -   `id` (string): Identifier of the combination, unique within the design.
            -   `name` (string): Display name listing the materials in slot order.
            -   `slots` (array of MaterialReference): One material per color slot, in the order the file uses them. Same shape as `MaterialReference` above.
-   `manufacturing` (object): How the design is produced.
    -   `colors` (integer): How many colors the print uses at once, 1 to 8.
        
        Range 1 to 8.
        
    -   `requires_ams` (boolean): Whether printing needs an automatic material system (several filaments loaded at once).
    -   `assembly_required` (boolean): Whether the printed parts must be assembled before shipping.
    -   `farm_instructions` (string or null): Notes for the production partner, for example assembly steps.
    -   `extra_parts_note` (string or null): Free-text description of extra parts, kept for reference. The extras that are priced and shipped are the ones in each variant `contents`.
-   `created_at` (timestamp): Point in time, ISO 8601 in UTC.
-   `updated_at` (timestamp): Point in time, ISO 8601 in UTC.

**The design object**

```json
{
  "id": "dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
  "object": "design",
  "name": "tracker.3mf",
  "owner": {
    "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
    "object": "product",
    "name": "PR Tracker"
  },
  "review": {
    "status": "approved",
    "reason": null,
    "reviewed_at": "2026-04-21T09:10:00Z"
  },
  "files": [
    {
      "id": "dfl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
      "object": "design_file",
      "name": "tracker.3mf",
      "format": "3mf",
      "size_bytes": 2758723,
      "download_url": "https://files.example.com/tracker.3mf?signature=abc",
      "uploaded_at": "2026-04-21T07:34:06Z",
      "production_version": {
        "name": "farm-settings-tracker.3mf",
        "format": "3mf",
        "size_bytes": 2761090,
        "download_url": "https://files.example.com/farm-settings-tracker.3mf?signature=def",
        "prepared_at": "2026-04-21T07:36:40Z"
      }
    }
  ],
  "materials": {
    "mode": "single",
    "allowed": [
      {
        "id": "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
        "object": "material",
        "name": "PLA Black",
        "custom": false
      },
      {
        "id": null,
        "object": "material",
        "name": "PETG Matte",
        "custom": true
      }
    ]
  },
  "manufacturing": {
    "colors": 1,
    "requires_ams": false,
    "assembly_required": true,
    "farm_instructions": null,
    "extra_parts_note": "Two M2 screws"
  },
  "created_at": "2026-04-21T07:34:06Z",
  "updated_at": "2026-04-21T09:10:00Z"
}
```

## Add a design to a product

POST `/v2/products/{id}/designs`

- **Credentials**: An API key with the `products:write` scope (full access includes every scope). A signed-in dashboard session works too.

- **For**: Designers

- **Operation id**: `add_design`

Adds a model file to a product, with the materials it may be printed in. The new design is pending review by 3DAPI, so the product is too until it is approved. Designers only.

### Path parameters

-   `id` (string, required): Unique identifier of the product, prefixed with `prd_`. Treat it as an opaque string: store it and compare it, never parse it.

### Headers

-   `Idempotency-Key` (string): Any unique string, for example a UUID. Sending the same request again with the same key within 24 hours returns the first response without doing anything twice. Reusing a key with a different body is refused with `idempotency_key_reused`. A refused request (4xx) frees the key so you can correct and resend it; a server error (5xx) is kept and returned again, because the write may already have happened: check the resource before retrying with a new key.

### Request body

-   `file` (object, required): The model file, uploaded first with purpose `design_file`.
    -   `upload` (string, required): An upload id from `POST /v2/uploads`, used before it expires. The file is checked when used: one that is not what its name and type say is refused with 422 `invalid_file` (the expected type in `details.expected`) and must be uploaded again.
-   `name` (string or null): Display name. The file name when omitted.
-   `materials` (object, required)
    -   Shape **mode: single**:
        
        -   `mode` (string, required): `single`: the design prints in one material, and each variant picks one from `allowed`.
        -   `allowed` (array of objects, required): Materials variants may print this design in; each variant picks one.
            -   Shape **string**:
                
                Unique identifier of the material, prefixed with `mat_`. Treat it as an opaque string: store it and compare it, never parse it.
                
            -   Shape **CustomMaterialInput** (expanded):
                
                -   `custom` (string, required): A filament not in the catalog, by name. Reviewed by 3DAPI.
    -   Shape **mode: multicolor**:
        
        -   `mode` (string, required): `multicolor`: the design prints in several color slots, and each variant picks one of the `combinations`.
        -   `combinations` (array of object, required): Color combinations variants may print this design in. Every combination has the same number of slots; each variant picks one by sending its slot materials in order.
            -   `name` (string or null): Display name. Built from the slot materials when omitted.
            -   `slots` (array of objects, required): One material per color slot, in the order the file uses them. Same shape as `MaterialInput` above.

### Returns 201 Created

The new design, pending review. Returns [the design object](https://public-api.dapi.digital/docs/reference/designs.md#design_object).

### Responses

-   `201`: The new design, pending review.
-   `400`: The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).
-   `401`: Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).
-   `403`: The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.
-   `404`: You have no product with this id (`product_not_found`).
-   `422`: `material_not_found`, `combination_slots_mismatch`, or the file upload cannot be used: `unknown_upload`, `upload_expired`, `upload_purpose_mismatch`, `upload_missing`, `upload_already_used`.
-   `429`: Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.

Errors use [the error object](https://public-api.dapi.digital/docs/guides/errors.md#error-object).

**Request**

```bash
curl -X POST "https://public-api.dapi.digital/v2/products/prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu/designs" \
  -H "x-api-key: $DAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "file": {
    "upload": "upl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
  },
  "name": "Tracker lid",
  "materials": {
    "mode": "single",
    "allowed": [
      "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
    ]
  }
}'
```

## Get a design

GET `/v2/designs/{id}`

- **Credentials**: An API key with the `products:read` or `stocks:read` scope (full access includes every scope). A signed-in dashboard session works too.

- **For**: Designers

- **Operation id**: `get_design`

Returns one of your designs: its files with download links, allowed materials, production settings and review status. Designers only.

### Path parameters

-   `id` (string, required): Unique identifier of the design, prefixed with `dsg_`. Treat it as an opaque string: store it and compare it, never parse it.

### Returns 200 OK

The design. Returns [the design object](https://public-api.dapi.digital/docs/reference/designs.md#design_object).

### Responses

-   `200`: The design.
-   `401`: Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).
-   `403`: The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.
-   `404`: You have no design with this id (`design_not_found`).
-   `429`: Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.

Errors use [the error object](https://public-api.dapi.digital/docs/guides/errors.md#error-object).

**Request**

```bash
curl "https://public-api.dapi.digital/v2/designs/dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu" \
  -H "x-api-key: $DAPI_API_KEY"
```

## Update a design

PATCH `/v2/designs/{id}`

- **Credentials**: An API key with the `products:write` scope (full access includes every scope). A signed-in dashboard session works too.

- **For**: Designers

- **Operation id**: `update_design`

Changes a design name or its allowed materials. Sends designs back to review by 3DAPI: a new design file; turning assembly on or off (every design); a newly added extra that is not approved yet (every design); and, per design, a change to its custom materials, single or multicolor mode, color count or multi-material (AMS) requirement. Nothing else does. Designers only.

### Path parameters

-   `id` (string, required): Unique identifier of the design, prefixed with `dsg_`. Treat it as an opaque string: store it and compare it, never parse it.

### Request body

-   `name` (string or null): Display name. The file name when omitted.
-   `materials` (object): Same shape as `DesignMaterialsInput` above.

### Returns 200 OK

The updated design. Returns [the design object](https://public-api.dapi.digital/docs/reference/designs.md#design_object).

### Responses

-   `200`: The updated design.
-   `400`: The request is invalid. `details.errors` lists each problem (`validation_failed`), or an `expand[]` path is unknown (`invalid_expand`).
-   `401`: Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).
-   `403`: The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.
-   `404`: You have no design with this id (`design_not_found`).
-   `409`: The design belongs to a stock (`design_belongs_to_stock`); stock designs are changed from the dashboard for now.
-   `422`: A material a variant still uses was removed (`material_in_use`), `material_not_found` or `combination_slots_mismatch`.
-   `429`: Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.

Errors use [the error object](https://public-api.dapi.digital/docs/guides/errors.md#error-object).

**Request**

```bash
curl -X PATCH "https://public-api.dapi.digital/v2/designs/dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu" \
  -H "x-api-key: $DAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "materials": {
    "mode": "single",
    "allowed": [
      "mat_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
      {
        "custom": "Silk white PLA"
      }
    ]
  }
}'
```

## Delete a design

DELETE `/v2/designs/{id}`

- **Credentials**: An API key with the `products:write` scope (full access includes every scope). A signed-in dashboard session works too.

- **For**: Designers

- **Operation id**: `delete_design`

Removes a design from its product. Not possible while a variant uses it: change or delete those variants first. Designers only.

### Path parameters

-   `id` (string, required): Unique identifier of the design, prefixed with `dsg_`. Treat it as an opaque string: store it and compare it, never parse it.

### Returns 200 OK

The design was deleted.

Response attributes (object):

-   `id` (string): The id of the deleted design.
-   `object` (string): Always `design`.
-   `deleted` (boolean): Always `true`: the deletion succeeded.

### Responses

-   `200`: The design was deleted.
-   `401`: Credentials are missing (`missing_credentials`), the API key is unknown or inactive (`invalid_api_key`), the session expired (`invalid_session`), or both a key and a session were sent (`ambiguous_credentials`).
-   `403`: The credentials are valid but not allowed to do this: a scope is missing (`missing_scope`), the endpoint is for another kind of account (`actor_not_allowed`) or another kind of credential (`credential_not_allowed`), the account may no longer use the API (`account_blacklisted`), or the account must change something first, in which case `details.fix_url` links to the dashboard page that fixes it.
-   `404`: You have no design with this id (`design_not_found`).
-   `409`: A variant still uses this design (`design_in_use`), or the design belongs to a stock (`design_belongs_to_stock`).
-   `429`: Too many requests (`rate_limited`): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the `Retry-After` header, then retry; `details` has `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`.

Errors use [the error object](https://public-api.dapi.digital/docs/guides/errors.md#error-object).

**Request**

```bash
curl -X DELETE "https://public-api.dapi.digital/v2/designs/dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu" \
  -H "x-api-key: $DAPI_API_KEY"
```

**Response 200**

```json
{
  "id": "dsg_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
  "object": "design",
  "deleted": true
}
```

---

3DAPI API Docs. This page on the web: https://public-api.dapi.digital/docs/reference/designs. Index of every page: https://public-api.dapi.digital/docs/llms.txt.
