Extras

View as Markdown

Bought-in parts shipped with products (designers) or with order lines (integrators).

The extra object

A bought-in part that ships with a product, for example a magnet, screw or battery. Extras belong to the account (designer or integrator) that created them and are reviewed by 3DAPI before use.

Attributes

  • idstring

    Unique identifier of the extra, prefixed with xtr_. Treat it as an opaque string: store it and compare it, never parse it.

  • objectstring

    Names the type of this object. Always extra.

  • namestring

    Display name of the part.

  • descriptionstring

    What the part is, as written by the seller.

  • reviewReview

    Review by 3DAPI. Anything that will be manufactured is reviewed before it can be sold or produced.

  • priceobject or null

    What one unit adds to a variant cost, under costs.extra_parts (designers), or to the production price of each printed unit of an order line that carries it (integrators, items[].extras). Set by 3DAPI when the extra is approved. While an edited extra is back in review this stays the last approved price. Null until the extra is approved, and null again after a rejection: rejecting clears the price even if the extra was approved before.

  • purchase_linksarray of object

    Where the production partner can buy the part. At least one.

  • imagesarray of object

    Photos of the part. At least one.

  • created_attimestamp

    Point in time, ISO 8601 in UTC.

  • updated_attimestamp

    When the extra last changed: created, edited, or approved or rejected by 3DAPI. Extras last changed before this was recorded show the latest of their creation and review decision instead.

The extra object
{
  ,
  ,
  ,
  ,
  ,
  ,
  ,
  ,
  ,
  
}

List extra parts

GET/v2/extras

Credentials
An API key with the extras:read scope (full access includes every scope). A signed-in dashboard session works too.
For
Designers and Integrators
Operation id
list_extras

Lists your extra parts, newest first, with their review status and price. For designers and integrators.

Query parameters

  • review_statusarray of ReviewStatus

    Only extras in these review states.

    Possible valuespendingapprovedrejected
  • searchstring

    Only extras whose name contains this text, ignoring case.

  • sortstring

    Order of results. A leading - means descending. name sorts alphabetically in a language-aware collation, which compares letters before spaces and case (test slicer comes after testing warnings); do not expect byte order.

    Possible values-created_atcreated_atname
    Defaults to "-created_at".
  • limitinteger

    How many results per page, from 1 to 100.

    Range 1 to 100. Defaults to 20.
  • cursorstring

    Where to continue from: the next_cursor of the previous page. Omit for the first page.

Returns200 OK

A page of extras.

Responses

  • 200

    A page of extras.

  • 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.

  • 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.

GET/v2/extras
curl "https://public-api.dapi.digital/v2/extras" \
  -H "x-api-key: $DAPI_API_KEY"
Response
{
  "object": "list",
  "data": [
    {
      "id": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
      "object": "extra",
      "name": "Magnet 10mm",
      "description": "Neodymium magnet, 10mm diameter.",
      "review": {
        "status": "approved",
        "reason": null,
        "reviewed_at": "2026-07-22T10:05:00Z"
      },
      "price": {
        "amount": 50,
        "currency": "usd"
      },
      "purchase_links": [
        {
          "url": "https://example.com/magnet-10mm",
          "label": "Datasheet"
        }
      ],
      "images": [
        {
          "url": "https://cdn.example.com/magnet.png",
          "name": "magnet.png"
        }
      ],
      "created_at": "2026-07-22T09:56:30Z",
      "updated_at": "2026-07-22T10:05:00Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Create an extra part

POST/v2/extras

Credentials
An API key with the extras:write scope (full access includes every scope). A signed-in dashboard session works too.
For
Designers and Integrators
Operation id
create_extra

Adds an extra part and submits it for review by 3DAPI. It comes back with review.status: pending and no price; we set the price when we approve it, and only approved extras can be used: in variants (designers) or on order lines (integrators, items[].extras of create_integrator_order). Upload the photos first with purpose extra_image. For designers and integrators.

Headers

  • Idempotency-Keystring

    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

  • namestringRequired

    Display name of the part.

  • descriptionstringRequired

    What the part is, for the production partner and our review.

  • purchase_linksarray of objectRequired

    Where the production partner can buy the part. At least one.

  • imagesarray of objectsRequired

    Photos of the part, at least one. Replaces the whole list on update.

Returns201 Created

The extra, pending review. Returns the extra object.

Responses

  • 201

    The extra, pending review.

  • 400

    The request is invalid (validation_failed).

  • 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.

  • 422

    An upload is unknown, expired or of the wrong purpose (unknown_upload, upload_expired, upload_purpose_mismatch, upload_missing when the file was never sent, upload_already_used when the upload id was already used once), an image url is given (a new extra has no photos to keep, unknown_image), or the same upload is listed twice (duplicate_image).

  • 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.

POST/v2/extras
curl -X POST "https://public-api.dapi.digital/v2/extras" \
  -H "x-api-key: $DAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Magnet 10mm",
  "description": "Neodymium magnet, 10mm diameter.",
  "purchase_links": [
    {
      "url": "https://example.com/magnet-10mm",
      "label": "Store"
    }
  ],
  "images": [
    {
      "upload": "upl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
    }
  ]
}'
Response
{
  "id": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
  "object": "extra",
  "name": "Magnet 10mm",
  "description": "Neodymium magnet, 10mm diameter.",
  "review": {
    "status": "pending",
    "reason": null,
    "reviewed_at": null
  },
  "price": null,
  "purchase_links": [
    {
      "url": "https://example.com/magnet-10mm",
      "label": "Datasheet"
    }
  ],
  "images": [
    {
      "url": "https://cdn.example.com/magnet.png",
      "name": "magnet.png"
    }
  ],
  "created_at": "2026-07-22T09:56:30Z",
  "updated_at": "2026-07-22T10:05:00Z"
}

Get an extra part

GET/v2/extras/{id}

Credentials
An API key with the extras:read scope (full access includes every scope). A signed-in dashboard session works too.
For
Designers and Integrators
Operation id
get_extra

Returns one of your extra parts with its review status and price. For designers and integrators.

Path parameters

  • idstringRequired

    Unique identifier of the extra, prefixed with xtr_. Treat it as an opaque string: store it and compare it, never parse it.

Returns200 OK

The extra. Returns the extra object.

Responses

  • 200

    The extra.

  • 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 extra with this id (extra_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.

GET/v2/extras/{id}
curl "https://public-api.dapi.digital/v2/extras/xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu" \
  -H "x-api-key: $DAPI_API_KEY"
Response
{
  "id": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
  "object": "extra",
  "name": "Magnet 10mm",
  "description": "Neodymium magnet, 10mm diameter.",
  "review": {
    "status": "approved",
    "reason": null,
    "reviewed_at": "2026-07-22T10:05:00Z"
  },
  "price": {
    "amount": 50,
    "currency": "usd"
  },
  "purchase_links": [
    {
      "url": "https://example.com/magnet-10mm",
      "label": "Datasheet"
    }
  ],
  "images": [
    {
      "url": "https://cdn.example.com/magnet.png",
      "name": "magnet.png"
    }
  ],
  "created_at": "2026-07-22T09:56:30Z",
  "updated_at": "2026-07-22T10:05:00Z"
}

Update an extra part

PATCH/v2/extras/{id}

Credentials
An API key with the extras:write scope (full access includes every scope). A signed-in dashboard session works too.
For
Designers and Integrators
Operation id
update_extra

Changes the fields you send. Sending any field sends the extra back to review by 3DAPI (review.status: pending), even if the value is unchanged; a body with no fields changes nothing. Until it is approved again the extra keeps its last approved price. For designers, every product using it goes back to review too (pending), which takes it off your store until 3DAPI approves the extra again, exactly as an edit in the dashboard does. For integrators, new orders cannot use it until it is approved again; orders already placed keep it. For designers and integrators.

Path parameters

  • idstringRequired

    Unique identifier of the extra, prefixed with xtr_. Treat it as an opaque string: store it and compare it, never parse it.

Request body

  • namestring

    Display name of the part.

  • descriptionstring

    What the part is, for the production partner and our review.

  • purchase_linksarray of object

    Where the production partner can buy the part. At least one.

  • imagesarray of objects

    Photos of the part, at least one. Replaces the whole list on update.

Returns200 OK

The extra, pending review. Returns the extra object.

Responses

  • 200

    The extra, pending review.

  • 400

    The request is invalid (validation_failed).

  • 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 extra with this id (extra_not_found).

  • 422

    An upload is unknown, expired or of the wrong purpose (unknown_upload, upload_expired, upload_purpose_mismatch, upload_missing when the file was never sent, upload_already_used when the upload id was already used once), an image url is not one of the photos this extra has (unknown_image), or a photo is listed twice (duplicate_image).

  • 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.

PATCH/v2/extras/{id}
curl -X PATCH "https://public-api.dapi.digital/v2/extras/xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu" \
  -H "x-api-key: $DAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "description": "Neodymium magnet, 10mm diameter, N52 grade.",
  "images": [
    {
      "url": "https://cdn.example.com/magnet.png"
    },
    {
      "upload": "upl_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu"
    }
  ]
}'
Response
{
  "id": "xtr_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
  "object": "extra",
  "name": "Magnet 10mm",
  "description": "Neodymium magnet, 10mm diameter.",
  "review": {
    "status": "pending",
    "reason": null,
    "reviewed_at": null
  },
  "price": {
    "amount": 50,
    "currency": "usd"
  },
  "purchase_links": [
    {
      "url": "https://example.com/magnet-10mm",
      "label": "Datasheet"
    }
  ],
  "images": [
    {
      "url": "https://cdn.example.com/magnet.png",
      "name": "magnet.png"
    }
  ],
  "created_at": "2026-07-22T09:56:30Z",
  "updated_at": "2026-07-22T10:05:00Z"
}