Conventions
Success envelope
Section titled “Success envelope”Every 200 wraps the result in data, with a small meta block:
{ "data": { /* the resource(s) */ }, "meta": { "requestId": "0f8c…", "apiVersion": "1.0" }}meta.requestId— also returned as theX-Request-Idresponse header. Quote it in support requests.meta.apiVersion— the REST API version (1.0).
Pagination
Section titled “Pagination”The list endpoint uses page-number pagination (not cursors):
page— 1-based page number (default1).pageSize— rows per page (up to1000).
and returns them inside data alongside the totals:
{ "data": { "items": [ /* … */ ], "totalCount": 128, "page": 2, "pageSize": 50, "totalPages": 3, "availableVendors": ["Holzwerk GmbH", "Acme Supplies"] }, "meta": { "requestId": "…", "apiVersion": "1.0" }}# page 1curl -s -H "Authorization: Bearer $ASMK_TOKEN" \ "https://assemblified.com/api/v1/raw-materials?pageSize=50"# page 2curl -s -H "Authorization: Bearer $ASMK_TOKEN" \ "https://assemblified.com/api/v1/raw-materials?pageSize=50&page=2"Filtering & repeated query parameters
Section titled “Filtering & repeated query parameters”The list endpoint accepts query filters — search, usageFilter, originFilter, sortBy, sortOrder — see the reference for values.
Two parameters are repeatable — pass them once per value:
vendorFilteron the list:?vendorFilter=Holzwerk%20GmbH&vendorFilter=Acme%20SupplieslocationIdson the inventory read:?locationIds=74001236152&locationIds=74001268920
{variantId} in a path is either a numeric Shopify variant id (e.g. 45067340286136) or a virtual-material id (VMAT-VAR-…). Full gid://shopify/… ids are not supported in paths.
Methods
Section titled “Methods”GET and PATCH are the only supported methods. Any other method returns 405 with an Allow header listing the methods valid for that path (e.g. Allow: GET, PATCH).
PATCH semantics
Section titled “PATCH semantics”Both PATCH endpoints accept virtual (VMAT-*) materials only — a Shopify-managed id returns 400 INVALID_INPUT before anything is dispatched. Send a JSON object body with Content-Type: application/json.
Field updates — PATCH /raw-materials/{variantId}
Section titled “Field updates — PATCH /raw-materials/{variantId}”The body is a flat partial object of the editable fields: productName, variantName, sku, unit, unitCost, unitPrice, inventoryQuantity, vendor, specifications, imageUrl, isNonEssentialMaterial, weightValue, weightUnitId. Unrecognized fields are ignored (deny-by-default allowlist); only the fields you send are changed.
Inventory updates — PATCH /raw-materials/{variantId}/inventory
Section titled “Inventory updates — PATCH /raw-materials/{variantId}/inventory”The body is a batch of per-location rows:
{ "updates": [ { "locationId": "74001236152", "absolute": 120 }, { "locationId": "74001268920", "delta": -5 } ]}- Each row carries exactly one of
delta(relative adjustment) orabsolute(target level) — both or neither is400 INVALID_INPUT. absoluteis converted to a delta server-side inside the transaction, making it the retry-safe form — prefer it for stock-takes.- Negative
deltas respect the shop’s allow negative inventory setting: by default, levels clamp at zero server-side — a past-zerodeltastill returnsok: truewith the applied change truncated (re-read the levels to confirm).absolutemust be>= 0either way. - A row may optionally repeat
variantId, but it must equal the path id; a mismatch is400 INVALID_INPUT.
The response is a per-row batch envelope — check each row’s ok, don’t assume all-or-nothing:
{ "data": { "results": [ { "index": 0, "ok": true, "id": "VMAT-VAR-7f3d2c1a-9b4e-4c8d-a2f6-5e1b0d9c8a7f" }, { "index": 1, "ok": false, "error": { "code": "INVALID_INPUT", "message": "…" } } ], "okCount": 1, "failedCount": 1, "skippedCount": 0 }, "meta": { "requestId": "…", "apiVersion": "1.0" }}When every row fails with the same error (for example, the material doesn’t exist), the API collapses the batch into a single request-level error instead — e.g. one 404 NOT_FOUND — so common failures surface with a proper status code.