Product setup

Build a complete product record from packaging and code attributes, then connect the supplier names, customer part numbers, documents, and lifecycle actions that keep the catalog useful.

Public API v1.39.1 supports a connected product setup flow and product code renaming. A workflow can resolve or create packaging, read the organization's product code attributes, create a product with typed assignments, maintain its supplier and customer-facing identities, rename its code while preserving connected history, and reconcile the saved catalog record before continuing.

Setup flow

  1. Call GET /me and confirm the matching product, product-family, and write-readiness capabilities.
  2. Resolve the product family, packaging, and code-attribute values the product needs.
  3. Create packaging when an exact reusable reference does not already exist.
  4. Preview and create the product with its complete attributeAssignments set.
  5. Rename the product code when needed, then add supplier relationships, aliases, and customer part numbers with the latest product updatedAt value.
  6. Follow the product detail's catalog.links to reconcile every saved relationship.
  7. Attach product documents or assign the current SDS when needed.

Endpoint map

JobEndpoints
Product familiesGET /product-families, GET /product-families/{id}, POST /product-families, PATCH /product-families/{id}
PackagingGET /reference/product-packaging, POST /product-packaging, GET /product-packaging/{id}
Code attributesGET /product-code-attributes, GET /product-code-attributes/{attributeId}/values
Products and assignmentsPOST /products, GET /products/{id}, PATCH /products/{id}, GET /products/{id}/attribute-assignments
Supplier relationshipsGET and POST /products/{id}/suppliers, DELETE /products/{id}/suppliers/{supplierId}
Product aliasesGET and POST /products/{id}/aliases, PATCH and DELETE /products/{id}/aliases/{aliasId}
Product-family aliasesGET and POST /product-families/{id}/aliases, PATCH and DELETE /product-families/{id}/aliases/{aliasId}
Customer part numbersGET and POST /products/{id}/customer-part-numbers, PATCH and DELETE /products/{id}/customer-part-numbers/{customerId}
Product lifecyclePOST /products/{id}/archive, POST /products/{id}/unarchive

Product setup uses products:read and products:write together with the acting person's current Product access. A supplier relationship or supplier-specific alias also requires suppliers:read. A customer part number or customer-specific alias also requires customers:read.

Resolve or create packaging

Search the active packaging directory by text, exact name, or unit of measure. Continue larger results with meta.nextCursor by itself.

curl "$SHELFCYCLE_API_BASE_URL/reference/product-packaging?name=55%20gallon%20drum&limit=25" \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY"

When the product needs a new packaging reference, preview the exact definition and then create it with the same body and Idempotency-Key.

curl "$SHELFCYCLE_API_BASE_URL/product-packaging?dryRun=true" \
  -X POST \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: catalog:55-gallon-steel-drum" \
  -d '{
    "name": "55 gallon steel drum",
    "unitOfMeasure": "GALLON",
    "shape": "cylindrical",
    "tareWeightLbs": "42",
    "capacity": "55",
    "material": "steel"
  }'

Execution returns the packaging id, saved physical details, archive state, updatedAt, and idempotencyStatus. Use GET /product-packaging/{id} for exact readback.

Resolve code attributes

List the available attribute definitions, then list the accepted values for each SELECT or MULTISELECT definition.

curl "$SHELFCYCLE_API_BASE_URL/product-code-attributes?limit=50" \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY"
curl "$SHELFCYCLE_API_BASE_URL/product-code-attributes/grade-attribute-id/values?limit=50" \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY"

Each definition reports its type as SELECT, MULTISELECT, FREE_TEXT, or BOOLEAN. Build each assignment with the matching value field:

[
  { "attributeId": "grade-attribute-id", "optionIds": ["technical-grade-option-id"] },
  { "attributeId": "market-attribute-id", "optionIds": ["industrial-option-id", "coatings-option-id"] },
  { "attributeId": "color-attribute-id", "textValue": "Clear" },
  { "attributeId": "kosher-attribute-id", "booleanValue": true }
]

Create a complete product

Include attributeAssignments in the product create body so the accepted product identity and its assignments are saved together.

curl "$SHELFCYCLE_API_BASE_URL/products?dryRun=true" \
  -X POST \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: source-catalog:ipa-55-drum" \
  -d '{
    "code": "IPA-55-DRUM",
    "externalId": "source-product-913",
    "productFamilyId": "product-family-id",
    "packagingId": "packaging-id",
    "packagingType": "FIXED",
    "supplierType": "VARIABLE",
    "qty": "55",
    "casNumber": "67-63-0",
    "unNumber": "1219",
    "properShippingName": "Isopropanol",
    "hazardClass": ["3"],
    "packingGroup": "II",
    "attributeAssignments": [
      { "attributeId": "grade-attribute-id", "optionIds": ["technical-grade-option-id"] },
      { "attributeId": "color-attribute-id", "textValue": "Clear" }
    ]
  }'

Inspect data.status, checks, and duplicateCandidates. When the preview is ready, remove dryRun=true and submit the same body and key. The response returns the normalized accepted attributeAssignments set, including for a key that can write the product but cannot separately read it.

For an update, fetch the product first and send its latest updatedAt as If-Match. If attributeAssignments is present, it replaces the complete assignment set. Omit the field to preserve the current assignments, or send [] to clear them.

Rename a product code

API v1.39.1 lets an existing product take a new unique code while its connected sales, purchasing, inventory, pricing, cost, and accounting history stays attached to the same product.

Preview the rename with the current product timestamp:

curl "$SHELFCYCLE_API_BASE_URL/products/product-id?dryRun=true" \
  -X PATCH \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY" \
  -H "Content-Type: application/json" \
  -H 'If-Match: "2026-09-13T14:30:00.000Z"' \
  -d '{ "code": "IPA-55-DRUM-NEW" }'

When the preview is ready, remove dryRun=true and send the same update with the latest updatedAt. Product codes are unique across active and archived products; a matching code returns duplicate_product. Other structural product changes continue to follow their existing readiness checks.

The response returns the renamed product and its next updatedAt. Read GET /products/{id} to confirm the new code before continuing with another catalog change.

Add catalog relationships

Every relationship write starts from the latest owner updatedAt. Use the new timestamp returned by one relationship change before applying the next.

Connect a supplier

curl "$SHELFCYCLE_API_BASE_URL/products/product-id/suppliers" \
  -X POST \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "If-Match: 2026-09-10T14:30:00.000Z" \
  -d '{ "supplierId": "supplier-id" }'

An exact repeat reports changed: false. This relationship makes the supplier available for the product; Current Source selection remains under GET and PUT /products/{id}/current-cost-source.

Add the names each relationship uses

Aliases can be organization-wide or specific to one customer or supplier. Use the same alias endpoints for products and product families.

curl "$SHELFCYCLE_API_BASE_URL/products/product-id/aliases" \
  -X POST \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: source-catalog:product-id:ipa" \
  -H "If-Match: 2026-09-10T14:31:00.000Z" \
  -d '{
    "value": "IPA",
    "context": { "type": "global" }
  }'

Customer and supplier contexts use { "type": "customer", "customerId": "customer-id" } or { "type": "supplier", "supplierId": "supplier-id" }. An alias can change its display value through PATCH; changing its owner or context uses remove and create.

Add a customer part number

curl "$SHELFCYCLE_API_BASE_URL/products/product-id/customer-part-numbers" \
  -X POST \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "If-Match: 2026-09-10T14:32:00.000Z" \
  -d '{
    "customerId": "customer-id",
    "partNo": "CUST-IPA-55"
  }'

An exact repeat reports no change. Update the value through PATCH /products/{id}/customer-part-numbers/{customerId}.

Reconcile the saved catalog

GET /products/{id} returns a catalog block with the product family ID and external ID, packaging ID, archive state, and links to the relationship collections available to the current key.

{
  "catalog": {
    "family": { "id": "product-family-id", "externalId": "family-ipa" },
    "packaging": { "id": "packaging-id" },
    "archived": false,
    "links": {
      "attributeAssignments": { "available": true, "method": "GET", "path": "/api/v1/products/product-id/attribute-assignments" },
      "suppliers": { "available": true, "method": "GET", "path": "/api/v1/products/product-id/suppliers" },
      "aliases": { "available": true, "method": "GET", "path": "/api/v1/products/product-id/aliases" },
      "customerPartNumbers": { "available": true, "method": "GET", "path": "/api/v1/products/product-id/customer-part-numbers" }
    }
  }
}

Follow the available links and continue each collection with its returned cursor until hasMore is false. Supplier rows show whether a relationship is the selected Current Source. Assignment rows preserve the definition type and selected value identity. Alias and part-number rows retain their customer or supplier context.

Archive or restore a product

Preview the named action with dryRun=true, then apply it with the latest product timestamp in If-Match.

curl "$SHELFCYCLE_API_BASE_URL/products/product-id/archive?dryRun=true" \
  -X POST \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY"

The preview reports the current timestamp and any stock or inbound quantity that needs attention. Execution returns archived, changed, and the next updatedAt value. Restore uses the matching /unarchive endpoint. See Archive and restore for the shared lifecycle flow.

Documents and SDS

Use the Attached documents guide to upload and attach product files. Assign the current SDS through PUT /products/{id}/sds with the latest product updatedAt as If-Match.

Information

Keep each returned product timestamp with the completed step. It is the starting point for the next catalog relationship, document, or lifecycle change.