Endpoint reference
The tables below are a quick scan of the routes and filters. For full request/response schemas, the interactive explorer is the richer view.
OpenAPI spec
Section titled “OpenAPI spec”The full machine-readable contract is served publicly (no auth, no shop data) at:
GET https://assemblified.com/api/v1/openapi.jsonPoint Postman, Insomnia, or any OpenAPI 3.1 client at that URL to generate a typed client or browse every parameter and response shape.
Endpoints
Section titled “Endpoints”| Method | Path | Scope | What it does |
|---|---|---|---|
GET | /api/v1/raw-materials | read | List raw materials (filters: search, usageFilter, originFilter, vendorFilter (repeatable), sortBy, sortOrder, page, pageSize) |
GET | /api/v1/raw-materials/{variantId} | read | One material; ?includeConnections=true adds the BOMs + sub-assemblies that use it |
PATCH | /api/v1/raw-materials/{variantId} | write | Edit fields — virtual (VMAT-*) materials only |
GET | /api/v1/raw-materials/{variantId}/inventory | read | Real per-location stock (?locationIds= repeatable) — live from Shopify for Shopify-linked materials |
PATCH | /api/v1/raw-materials/{variantId}/inventory | write | Set per-location stock — virtual (VMAT-*) materials only |
GET | /api/v1/bills-of-materials | read | List bills of materials (filters: search, statusFilter, sortBy, sortOrder, page, pageSize) |
GET | /api/v1/bills-of-materials/{id} | read | One BOM with its fully hydrated recipe — raw materials, recursive sub-assemblies, cost factors |
GET | /api/v1/bills-of-materials/{id}/buildable | read | The computed per-location max-buildable (?locationIds= repeatable) |
GET | /api/v1/order-bom-breakdown | read | BOM breakdowns for 1–50 orders in one call (?ids= repeatable and/or comma-separated) — partial success |
GET | /api/v1/order-bom-breakdown/{id} | read | One order’s BOM breakdown — what a warehouse must pack |
GET | /api/v1/openapi.json | public | The machine-readable spec — no auth required |
sortBy on the list accepts productName (default), variantName, or inventoryQuantity; an unrecognized value is ignored and the default productName ordering is used.
Shopify-managed materials are read-only in v1
Section titled “Shopify-managed materials are read-only in v1”Every GET works for both origins — Shopify-linked and virtual. The two PATCH endpoints accept only virtual materials (ids starting VMAT-). A PATCH against a numeric Shopify variant id returns 400 INVALID_INPUT with a “read-only in API v1” message, before anything is changed. This is deliberate: fields like name, sku, price, and cost on Shopify-linked materials are mirrored from Shopify, so a local API edit would be silently reverted by the next product update. Editing those belongs in the Shopify admin (or a future API version with true write-back).
Examples
Section titled “Examples”List raw materials
Section titled “List raw materials”curl -s -H "Authorization: Bearer $ASMK_TOKEN" \ "https://assemblified.com/api/v1/raw-materials?originFilter=virtual&usageFilter=unused&pageSize=25"{ "data": { "items": [ { "variantId": "VMAT-VAR-7f3d2c1a-9b4e-4c8d-a2f6-5e1b0d9c8a7f", "productId": "VMAT-PROD-2a9c8b7d-6e5f-4a3b-8c1d-0e9f8a7b6c5d", "inventoryItemId": "VMAT-INV-5c4d3e2f-1a0b-4c9d-8e7f-6a5b4c3d2e1f", "productName": "Oak board 20 mm", "variantName": "120 x 60 cm", "sku": "OAK-20-120", "vendor": "Holzwerk GmbH", "unit": "pcs", "unitCost": 12.5, "unitPrice": 0, "inventoryQuantity": 240, "isVirtual": true, "bomUsageCount": 0, "subAssemblyUsageCount": 0 /* … specifications, imageUrl, isNonEssentialMaterial, weightValue, weightUnitId, productType, productCategory … */ } ], "totalCount": 7, "page": 1, "pageSize": 25, "totalPages": 1, "availableVendors": ["Holzwerk GmbH", "Acme Supplies"] }, "meta": { "requestId": "…", "apiVersion": "1.2" }}Get one material (with connections)
Section titled “Get one material (with connections)”curl -s -H "Authorization: Bearer $ASMK_TOKEN" \ "https://assemblified.com/api/v1/raw-materials/VMAT-VAR-7f3d2c1a-9b4e-4c8d-a2f6-5e1b0d9c8a7f?includeConnections=true"Returns data.material (same fields as a list row, minus the usage counts) plus data.connections with boms and subAssemblies arrays — each entry names the BOM / sub-assembly, the quantity of this material it uses, and its location. Works for Shopify-linked materials too (numeric id in the path).
Update a virtual material
Section titled “Update a virtual material”curl -s -X PATCH \ -H "Authorization: Bearer $ASMK_TOKEN" -H "Content-Type: application/json" \ -d '{ "unitCost": 13.1, "vendor": "Holzwerk GmbH" }' \ "https://assemblified.com/api/v1/raw-materials/VMAT-VAR-7f3d2c1a-9b4e-4c8d-a2f6-5e1b0d9c8a7f"Returns data.material — the full updated row. Only the fields you send change; the editable field list is in Conventions.
Read per-location inventory
Section titled “Read per-location inventory”curl -s -H "Authorization: Bearer $ASMK_TOKEN" \ "https://assemblified.com/api/v1/raw-materials/VMAT-VAR-7f3d2c1a-9b4e-4c8d-a2f6-5e1b0d9c8a7f/inventory"{ "data": { "variantId": "VMAT-VAR-7f3d2c1a-9b4e-4c8d-a2f6-5e1b0d9c8a7f", "origin": "virtual", "totalAvailable": 240, "cachedQuantity": 240, "levels": [ { "locationId": "74001236152", "available": 190 }, { "locationId": "74001268920", "available": 50 } ] }, "meta": { "requestId": "…", "apiVersion": "1.2" }}For a Shopify-linked material (numeric id), origin is "shopify", the levels are a live Shopify read, and each level row additionally carries a locationName. Virtual materials return locationId + available only — no locationName.
Set per-location inventory (virtual only)
Section titled “Set per-location inventory (virtual only)”curl -s -X PATCH \ -H "Authorization: Bearer $ASMK_TOKEN" -H "Content-Type: application/json" \ -d '{ "updates": [ { "locationId": "74001236152", "absolute": 200 } ] }' \ "https://assemblified.com/api/v1/raw-materials/VMAT-VAR-7f3d2c1a-9b4e-4c8d-a2f6-5e1b0d9c8a7f/inventory"Returns the per-row batch envelope (results / okCount / failedCount / skippedCount). Each row carries exactly one of absolute (retry-safe — prefer it) or delta; full body semantics in Conventions. Every adjustment is recorded in the material’s adjustment history.
Bills of materials (read-only)
Section titled “Bills of materials (read-only)”The three bills-of-materials endpoints expose your BOMsBill of MaterialsA bill of materials tells Assemblified how to build one unit of a finished good. When a customer orders the finished-good variant, Assemblified deducts the right component quantities from inventory automatically.
Read more →
— finished goods and their recipes. They are read-only in this version.
List bills of materials
Section titled “List bills of materials”curl -s -H "Authorization: Bearer $ASMK_TOKEN" \ "https://assemblified.com/api/v1/bills-of-materials?statusFilter=active&pageSize=25"Returns data.items (the projected BOM rows) plus the pagination fields and a stats block (total, active, inactive, totalValue, avgCost, shopifyLinked) aggregated over the full BOM set.
Get one BOM (the hydrated recipe)
Section titled “Get one BOM (the hydrated recipe)”curl -s -H "Authorization: Bearer $ASMK_TOKEN" \ "https://assemblified.com/api/v1/bills-of-materials/BOM-1748441466223"{ "data": { "bomId": "BOM-1748441466223", "status": "active", "name": "Oak Table", "productId": "8613244518724", "variantId": "45217109934404", "sku": "TABLE-OAK", "price": 499, "totalRawMaterialCost": 96.5, "totalSubAssemblyCost": 41.0, "totalCost": 137.5, "directCostFactorCost": 26.7, "preAssembledQuantity": 3, "rawMaterialReferences": [ { "nodeId": "VMAT-VAR-…", "quantity": 4, "wastePercentage": 5, "node": { "productName": "Oak board 20 mm", "unit": "pcs", "unitCost": 12.5 } } ], "subAssemblyReferences": [ { "nodeId": "ASM-…", "quantity": 2, "node": { "assemblyName": "Table leg", "unitCost": 20.5, "rawMaterialReferences": [], "subAssemblyReferences": [] } } ], "costFactorReferences": [ { "costFactorId": "CF-…", "quantity": 0.5, "node": { "name": "Assembly labour", "kind": "labour", "unit": "hour", "unitCost": 40 } } ] }, "meta": { "requestId": "…", "apiVersion": "1.2" }}Sub-assemblies nest recursively — each subAssemblyReferences[].node carries its own rawMaterialReferences and subAssemblyReferences, so one call returns the complete tree.
Compute max-buildable
Section titled “Compute max-buildable”curl -s -H "Authorization: Bearer $ASMK_TOKEN" \ "https://assemblified.com/api/v1/bills-of-materials/BOM-1748441466223/buildable"{ "data": { "bomId": "BOM-1748441466223", "variantId": "45217109934404", "hasSellableSplit": false, "perLocation": [ { "locationId": "74001236152", "locationName": "Main warehouse", "maxBuildable": 24, "sellable": 24, "limitingComponents": [ { "nodeId": "VMAT-VAR-…", "name": "Oak board 20 mm — 120 x 60 cm", "origin": "virtual", "quantityPerUnit": 4, "wastePercentage": 5, "isNonEssential": false, "maxUnitsByThisComponent": 24, "availableAtLocation": 96 } ] } ], "totals": { "maxBuildable": 24, "sellable": 24 }, "globalFallback": false }, "meta": { "requestId": "…", "apiVersion": "1.2" }}This is the same number the admin UI’s Max Buildable column shows: the full component tree against live Shopify raw-material levels, virtual levels, and nested pre-assembled sub-assembly stock — waste included, non-essential components excluded. Notes:
sellablerespects sub-assemblies flagged only consume pre-assembled; it differs frommaxBuildableonly whenhasSellableSplitistrue.limitingComponentsis a top-level, indicative view of what constrains the build — themaxBuildablenumber is the authoritative full-tree result.- When the BOM’s variant has no per-location Shopify data,
totalsfalls back to a single global computation andglobalFallbackistrue. - This is the most expensive read on the surface (it reads live Shopify levels) — cache the result rather than polling it.
Order BOM breakdown (read-only)
Section titled “Order BOM breakdown (read-only)”The two order-bom-breakdown endpoints answer the warehouse question: given an order, what exactly needs to be packed? Each breakdown lists the order’s BOMBill of MaterialsA bill of materials tells Assemblified how to build one unit of a finished good. When a customer orders the finished-good variant, Assemblified deducts the right component quantities from inventory automatically.
Read more →
lines with their component materials — sub-assembliesSub-AssemblyA reusable assembly block that composes into bigger BOMs. Define it once, include it in any BOM. At execution time, Assemblified expands the sub-assembly into its own components recursively.
Read more →
expanded down to leaf raw materials, waste included, quantities already multiplied by the ordered quantity — plus a cross-order totalMaterials summary and the non-BOM lines under otherItems.
The boms and totalMaterials portions are the same shape as the assemblified.material_requirements_json order metafield (see Shopify extensions) — built by the same code — so anything already consuming the metafield can switch to this endpoint unchanged. Unlike the metafield, the endpoint works for every order (the metafield writeback is an opt-in setting) and always reflects the order’s current line items.
Get one order’s breakdown
Section titled “Get one order’s breakdown”curl -s -H "Authorization: Bearer $ASMK_TOKEN" \ "https://assemblified.com/api/v1/order-bom-breakdown/6234567890123"{ "data": { "orderId": "6234567890123", "orderName": "#1042", "generatedAt": "2026-08-03T12:00:00.000Z", "boms": [ { "bomId": "BOM-1748441466223", "bomName": "Oak Table", "bomImageUrl": null, "bomProductId": "8613244518724", "bomVariantId": "45217109934404", "bomSku": "TABLE-OAK", "orderQuantity": 2, "materials": [ { "productId": "VMAT-PROD-…", "variantId": "VMAT-VAR-…", "sku": "OAK-20-120", "title": "Oak board 20 mm - 120 x 60 cm", "productName": "Oak board 20 mm", "variantName": "120 x 60 cm", "specifications": null, "notes": null, "quantity": 8 } ] } ], "totalMaterials": [ { "variantId": "VMAT-VAR-…", "productId": "VMAT-PROD-…", "sku": "OAK-20-120", "title": "Oak board 20 mm - 120 x 60 cm", "productName": "Oak board 20 mm", "variantName": "120 x 60 cm", "specifications": null, "notes": null, "totalQuantity": 8, "imageUrl": null, "isVirtual": true, "unitCost": 12.5 } ], "otherItems": [ { "variantId": "45217109999999", "productId": "8613244599999", "sku": "GIFT-CARD", "title": "Gift card", "quantity": 1 } ] }, "meta": { "requestId": "…", "apiVersion": "1.2" }}The path id is a bare numeric Shopify order id (recommended). The full gid://shopify/Order/… form is accepted too, but must be percent-encoded when used in the path — its literal slashes would otherwise split the URL and return 404 (in the batch route’s ids query parameter it needs no encoding). materials[].quantity is already multiplied by orderQuantity (2 tables × 4 boards = 8); totalMaterials deduplicates across all BOM lines by variant. An order that contains no BOM products still returns 200 with boms: [] and its lines under otherItems — an unknown order id returns 404 NOT_FOUND.
Get breakdowns for up to 50 orders
Section titled “Get breakdowns for up to 50 orders”curl -s -H "Authorization: Bearer $ASMK_TOKEN" \ "https://assemblified.com/api/v1/order-bom-breakdown?ids=6234567890123,6234567890124&ids=6234567890125"{ "data": { "items": [ { "orderId": "6234567890123", "…": "…" } ], "notFound": ["6234567890125"] }, "meta": { "requestId": "…", "apiVersion": "1.2" }}ids is repeatable and/or comma-separated (1–50 per call; more returns 400 INVALID_INPUT). The batch is partial-success: ids that can’t be resolved land in notFound and the call still returns 200 with every resolvable order — one request, one rate-limit token, regardless of how many orders it carries.