Custom Storefronts

API: parts, carts, and prices

Customer API endpoints and data shapes for part uploads, carts, and prices, in the order of the purchase flow

This page follows the first half of the purchase flow: upload a part, put it in a cart, and get a price. For the procedure with examples, refer to Upload and price.

Parts

Upload a part

PUT/part-revision/uploadGuest

Uploads one part file and starts the analysis.

ItemValue
Bodymultipart/form-data, one field file that contains the gzip-compressed file
Header X-FilenameMandatory. The original file name, URL-encoded.
Response 200A JSON string: the analysis ID
Limit1024 MB for each request. Also apply the store setting maximumFileSize.

Poll the analysis status

GET/part-revision/upload/status?analysisIds={id},{id}Guest

Returns an object: analysis ID → status. Refer to Wait for the analysis for the status values.

Read the analysis result

GET/part-revision/upload/{analysisId}/resultsGuest

Returns the analysis result as MessagePack (application/msgpack, with Content-Encoding: gzip). Only the uploader can read it.

You can read the result one time only. Keep the data that you need.

type AnalysisResult = {
  partRevisionId: string        // the ID for all later calls
  fileName: string
  versionNumber: number
  createdAt: string
  originalCadFileType: string
  availableAccessories: string[]

  width: number
  height: number
  length: number
  volume: number
  area: number
  convexHullVolume: number
  minBoundingBoxVolume: number
  shrinkWrapVolume: number
  minimumWallThickness: number | null
  watertight: boolean           // false: the part cannot be purchased instantly
  repaired: boolean             // the mesh was repaired automatically
  baseRotation: number[][]

  thumbnail: Uint8Array | null  // image bytes
  stl: Uint8Array               // gzip-compressed STL mesh, for a 3D viewer
  wallThickness: Uint8Array | null
  bending?: object | null       // sheet metal data
  machining?: object | null     // machining data
}

Dimensions are in the units of the file. You tell the API the units (units) when you add the part to a cart or an order.

Check manufacturability

POST/part-revision/manufacturability/validateGuest

Checks if parts are in the limits of a process and a material.

Request: an array of

{ partRevisionId: string; units: MeasurementUnit; processPricesId: string; materialId: number }

Response: an array of

type PartManufacturability = {
  partRevisionId: string
  limits: { withinBoundingBoxLimit: boolean; withinWallThicknessLimit: boolean }
  warnings: { hasOptimalWallThickness: boolean }
  values: { boundingBox: [number, number, number] | null; wallThicknessLimit: number | null; wallThicknessWarning: number | null }
}

type MeasurementUnit = 'MILLIMETERS' | 'CENTIMETERS' | 'METRES' | 'INCHES' | 'FEET'

Part data and files for carts and orders

These calls need a customer token.

GET/carts/{cartId}/items/{itemId}/part-revisionPart data (JSON) for a cart itemCustomer
GET/carts/{cartId}/items/{itemId}/part-revision/thumbnailPNG imageCustomer
GET/carts/{cartId}/items/{itemId}/part-revision/download/{type}A part fileCustomer
GET/requisition/{requisitionId}Part data (JSON) for an order lineCustomer
GET/requisition/{requisitionId}/thumbnailPNG imageCustomer
GET/requisition/{requisitionId}/download/{type}A part fileCustomer

{type} is ORIGINAL_CAD_FILE, CORE_GEOMETRY, THUMBNAIL, PDF_DESIGN_FILE, or WALL_THICKNESS_ANALYSIS.

Carts

A cart keeps the parts and their configuration on the server. A cart does not contain prices. Prices come from POST /pre-order. The cart uses the prefix /cart, and its items use /carts/{cartId}/items.

POST/cartCreate an empty cart. No body.Guest
POST/carts/{cartId}/itemsAdd an itemGuest
PUT/carts/{cartId}/items/{itemId}Replace the configuration of an itemGuest
DELETE/carts/{cartId}/items/{itemId}Remove an item. 204.Guest
PATCH/cart/{cartId}Change cart-level dataGuestPATCH/cart/{cartId}/claimMove a guest's parts and carts to the customer. 204.Customer
GET/cartList the open cartsCustomer
GET/cart/{cartId}Get one cart with its itemsCustomer
DELETE/cart/{cartId}Delete an open cart. 204.Customer
POST/carts/{cartId}/items/{itemId}/filesAttach a file (multipart/form-data, field file)Customer
GET/carts/{cartId}/items/{itemId}/files/{fileId}Download an attached fileCustomer
DELETE/carts/{cartId}/items/{itemId}/files/{fileId}Delete an attached fileCustomer

A guest can create carts and change items, but cannot read a cart back. Keep the cart ID and the items in your storefront until the visitor signs in.

Cart data shapes

type Cart = {
  cartId: string
  status: 'OPEN' | 'DELETED' | 'CONVERTED'
  items: CartItem[]
  customerId: number | null     // null: a guest cart that is not claimed
  currency: string | null
  notes: string | null
  affiliate: string | null
  discountId: number | null
  billingAddressId: number | null
  toAddressId: number | null
  shippingMethodId: number | null
  rateId: string | null
  createdAt: string
  lastUpdated: string
}

type CartItem = {
  id: number                    // omit when you create an item
  partRevisionId: string
  name: string | null
  quantity: number
  units: MeasurementUnit | null
  processPricesId: string | null
  materialId: number | null
  colorId: number | null
  infillId: number | null
  precisionPricesId: number | null   // NOTE: "precisionId" in pre-order and order lines
  leadTimeId: string | null
  postProcessingIds: number[] | null
  useOriginalOrientation: boolean | null
  files: { id: number; fileName: string | null; contentType: string | null; size: number | null }[] | null
}

Change cart-level data

PATCH/cart/{cartId}Guest

Accepts currency, affiliate, notes, discountId, billingAddressId, shippingMethodId, toAddressId, and rateId.

  • A field that you omit does not change.
  • A field with null is cleared.
  • For a guest cart, the address and rate fields give 409 CART_NOT_CLAIMED.

Claim a guest cart

PATCH/cart/{cartId}/claimCustomer

Takes { "anonymousSessionToken": "<guest token>" }. For the procedure and the status codes, refer to Claim the guest cart.

Prices

Get a price

POST/pre-orderGuest

Calculates the price for a set of lines. Stores no data. For an example request and how to use the response, refer to Get a price.

Request

POST /order uses the same body. Refer to Orders.

type CreateOrder = {
  currency: string                                 // one of acceptedCurrencies
  requisitions: Record<string, CreateRequisition>  // key: a UUID that you select for each line
  shipping: CreateShipping | null
  shippingId: null
  billingAddressId: number | null
  discountId: number | null
  operatorNote: string | null                      // a note to the manufacturer
  affiliate: string | null
  cartId: string | null                            // POST /order only
  intent?: 'CHECKOUT'                              // POST /order only
}

type CreateRequisition = {
  sequence: number              // position of the line, from 0
  partRevisionId: string
  units: MeasurementUnit
  quantity: number
  processPricesId: string
  materialId: number
  infillId: number | null
  precisionId: number | null
  colorId: number | null
  leadTimeId: string | null
  postProcessingIds: number[]
  comments: { conversationType: 'REQUISITION_TAGGED'; message: string }[]
  constraints?: { constraintType: 'FIXED_ORIENTATION' }[]
  name: string | null
}

type CreateShipping =
  | { shippingMode: 'SELF_COLLECTION'; shippingMethodId: number; contactName?: string; contactPhoneNumber?: string }
  | { shippingMode: 'FIXED_PRICE'; shippingMethodId: number; rateId: string; toAddressId: number }
  | { shippingMode: 'CARRIER_ACCOUNT'; shippingMethodId: number; rateId: string; toAddressId: number }
  | { shippingMode: 'CUSTOMER_ACCOUNT'; shippingMethodId: number; toAddressId: number; accountNumber: string }

A guest cannot send shipping or billingAddressId (addresses need a customer).

Response

type PreOrder = {
  quote: Quote
  purchasability: Purchasability
  constraints: Record<string, object[]>
}

type Quote = {
  currency: string
  price: number                 // total
  subtotal: number
  shipping: number
  discount: number
  topUp: number | null          // amount added to reach the minimum order amount
  tax: {
    totalPrice: number
    totalPercentage: number
    components: { shortName: string; longName: string; percentage: number; amount: number }[]
    isTaxAppliedToShipping: boolean
    isTaxExempted: boolean
    isTaxReverseCharged: boolean
  }
  lineItems: { name: string; price: number }[]
  requisitions: Record<string, RequisitionQuote | null>   // null: no instant price for this line
  expectedDispatchDate: string | null
}

type RequisitionQuote = {
  partRevisionId: string
  quantity: number
  price: number
  unitPrice: number
  manufacturingPrice: number
  postProcessingPrice: number
  postProcessingOptions: { id: number; price: number }[]
}

type Purchasability = {
  canBePurchased: boolean       // false: offer "request a quote", not payment
  isPriceReviewRequired: boolean
  withinMaximumOrderValue: boolean
  withinBoundingBoxLimit: boolean
  withinWallThicknessLimit: boolean
  isMaterialPurchasable: boolean
  isPostProcessingPurchasable: boolean
  parts: Record<string, PartManufacturability>
}

PartManufacturability is described in Check manufacturability.

400 with an empty body: the request has no lines.

Get bulk prices

POST/pre-order/bulk-quote?currency={currency}Guest

Returns prices at the bulk quantities of the process, for one part configuration.

Request: a CreateRequisition without sequence, quantity, comments, and name.

type BulkQuote = { quantity: number; price: number; unitPrice: number; savingsPerUnit: number }

Validate a discount code

POST/discount/validateCustomer

Request: { "discountCode": "SPRING" }. Response 200: the discount (discountId, code, percentage, …). Response 204: the code is not valid.

Last updated on

On this page