Product Import Developer Guide - Fabric

Prerequisites

In order to upload products, you must already have created your category taxonomy. For information on creating categories in the Product Agent UI, see Creating Categories.

Step 1: Postman Environment Setup

This part of the guide walks you through setting up a Postman environment to interact with the Product Agent API.

Create a new environment

  1. Open Postman.
  2. Click Environments in the sidebar.
  3. Click + to create a new environment.Provide a name such as Product Agent - Prod.

Add the environment variables

Add the following variables to your environment.

access_token and the various defined IDs will be empty initially — they will be populated after making API calls.

Variable Description Example
baseUrl Base URL for the API https://commerceos.aiagents.fabric.inc/api
authUrl Auth URL for the API https://commerceos.aiagents.fabric.inc
access_token Token used for authenticated requests eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
attributeWorkflowId ID of the attribute workflow (used for status endpoints) a1462a91-f733-45fe-993b-5d0353f33ee3
artifactId ID of an uploaded catalog artifact (returned by the artifact upload) art_95c650ded4824dc79bb6df16427e4a10
workflowId ID of an optimization workflow (returned by the workflow create call) 9349773c-27fe-4d4f-a093-a5793dff8702
clientId Client ID provided by fabric. Used to create an access token svc_peyFD9BXPRrZymhjJtFuYr7L3Ai
clientSecret Client secret provided by fabric. Used to create an access token cw_GWve_9oI_aJKd-xcE7uuZJpr-WqfnRpDPznGNVI-fOc
domain Brand’s domain name (must be a .com value scoped to a brand you own) vessel.com

Step 2: Authentication

This part of the guide walks you through authenticating with the Product Agent API and storing your access token in Postman for future requests.

Create and set the access token

To authenticate, send a POST request to the token endpoint using your clientId and clientSecret. If you do not have these credentials, contact fabric support.

Tokens expire every 60 minutes.

curl --location '{{authUrl}}/platform/v1/auth/token' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
  "grant_type": "client_credentials",
  "client_id": "{{clientId}}",
  "client_secret": "{{clientSecret}}"
  }'

Example Response:

{
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdmNfcGV5SkQ5DlhQUnRadW1oakp0RnVZcjdMbEFvIiwidHlwZ56UIkjnZpY2UiLCJjb21wYW55X2lkIjoiNjhiNzI3NjhlYzc1NGVjNzA2MjhkNTM5Iiwic2NvcGVzIjpbXSwiYnJhbmRfaWRzIjpbImNvbnRhaW5lcnN0b3JlLmNvbSJdLCJqdGkiOiI5MGQ3YjQ0NC03MTQxLTRhMDUtYWM4MC03OGJjZTMxZGI5OGUiLCJpYXQiOjE3NzQ1NDM2NDEsImV4cCI6MTc3NDU0NzI0MX0.kc9RQSirU2vIVl5oRTmuEBoaF1tmAeYB3KH87KAHJpM",
    "token_type": "bearer",
    "expires_in": 3600,
    "scope": ""
}

To automatically save the access_token for future requests:

  1. Go to the Scripts tab in your Postman request.
  2. Select Post-response.
  3. Add the following script:pm.environment.set("access_token", pm.response.json().access_token);

Step 3 (Optional): Import Attribute Definitions

You can optionally import attribute definitions for enrichment and mapping workflows. For information on creating attributes in the Product Agent UI, see Creating Attributes.This endpoint accepts a CSV file and creates an import job you can monitor using the returned import_id. We will save this to our environment as attributeWorkflowId.

Expected CSV columns for each attribute definition

Column Required Description
attribute_name Yes Attribute display name
attribute_key No Machine-readable key
description Yes Attribute description
data_type No Attribute data type
scope No Attribute scope
enum_values No Allowed values, pipe-delimited, for example `S
allow_ai_content No Allow AI to generate values (true or false)
source No MERCHANT or GOLD_STANDARD
guideline_reason No Guidance for AI enrichment

Request

curl --location '{{baseUrl}}/v2/attributes/import' \
--header 'Authorization: {{access_token}}' \
--header 'domain: {{domain}}' \
--form 'file=@"/D:/Demo CSV files/attributes.csv"'

To automatically save the attributeWorkflowId for future requests:

  1. Go to the Scripts tab in your Postman request.
  2. Select Post-response.
  3. Add the following script:pm.environment.set("attributeWorkflowId", pm.response.json().import_id);

Example Response:

{
    "import_id": "e2716438-b763-4be8-82d2-36abe0cb92b1",
    "name": "attribute.csv",
    "status": "PENDING",
    "total_rows": 36,
    "processed_rows": 0,
    "created_count": 0,
    "updated_count": 0,
    "skipped_count": 0,
    "failed_count": 0,
    "input_filename": "attribute.csv",
    "input_file_url": null,
    "error_file_url": null,
    "error_message": null,
    "errors": [],
    "started_at": null,
    "completed_at": null,
    "created_at": "2026-03-12T18:04:03.879876Z"
}

Check the import status

After the upload succeeds, use the saved attributeWorkflowId to check workflow status with:

curl --location '{{baseUrl}}/v2/attributes/import/{{attributeWorkflowId}}' \
--header 'Authorization: {{access_token}}' \
--header 'domain: {{domain}}'

Example Response:

{
    "import_id": "e2716438-b763-4be8-82d2-36abe0cb92b1",
    "name": "attribute.csv",
    "status": "COMPLETED",
    "total_rows": 36,
    "processed_rows": 36,
    "created_count": 35,
    "updated_count": 0,
    "skipped_count": 0,
    "failed_count": 1,
    "input_filename": "attribute_test.csv",
    "input_file_url": "https://product-agent-data-prod-ue2.s3.amazonaws.com/attribute/vessel/e2716438-b763-4be8-82d2-36abe0cb92b1.csv...",
    "error_file_url": "https://product-agent-data-prod-ue2.s3.amazonaws.com/attribute/vessel/e2716438-b763-4be8-82d2-36abe0cb92b1_errors.csv?X-...",
    "error_message": null,
    "errors": [
        "Row 35: invalid data_type 'BOOLREAN'"
    ],
    "started_at": "2026-03-12T18:04:03.998437Z",
    "completed_at": "2026-03-12T18:04:04.724584Z",
    "created_at": "2026-03-12T18:04:03.879876Z"
}

In this example, an error was flagged in the upload. You can resolve and publish updates to just that row or re-upload the entire file. Rows with errors are skipped.Once completed, you can review the attribute definitions you uploaded in the UI.

  1. Log in to Product Agent.
  2. In the left nav, click Settings.The Settings menu is displayed.
  3. Click Taxonomy.The Categories tab is displayed by default.
  4. Click Attributes.

Here you can review your attribute definitions.

Step 4: Import Your Products

After the categories and attribute definitions have been imported, products can be uploaded. The product import flow consists of three sequential API calls:

  1. Artifact Upload. Submit the CSV to the artifacts endpoint and store the returned artifact_id.
  2. Optimize Workflow. Create a workflow that processes the artifact and store the returned workflow id.
  3. Get Workflow Status. Poll the workflow until it reaches a review gate or a terminal status.

Expected CSV columns

The catalog file must be UTF-8 encoded, comma-delimited, and include a header row. The following table lists the supported columns.

Column Required Description
title Yes Product display name as shown on the product detail page.
sku Yes Unique product identifier within the brand catalog.
description Yes Full product description from the product detail page.
category_id Conditional The brand’s category identifier. Each row must provide either category_id or breadcrumb.
breadcrumb Conditional Full category hierarchy, separated by >. Example: Mens > Clothing > Pants. Each row must provide either breadcrumb or category_id.
category No Free-text category label. Used as a hint when neither category_id nor breadcrumb resolves to a known category.
product_group_id No Identifier shared by product variants that belong to the same family (for example, multiple SKUs of the same shirt in different colors).
price No Numeric price value for the product.
currency No Currency code corresponding to price.
url No Canonical product detail page URL.
images No One or more image URLs, separated by the pipe character (`
attribute.<name> No Custom attribute value. Free-form string, single value per cell. The <name> segment must match an attribute key configured for the brand (for example, attribute.color, attribute.material).

Artifact Upload

Submit the CSV to the artifacts endpoint. The response includes an artifact_id that is stored as artifactId and referenced when creating the workflow.

curl --location '{{baseUrl}}/v2/optimize/artifacts' \
--header 'Authorization: Bearer {{access_token}}' \
--header 'domain: {{domain}}' \
--form 'file=@"/path/to/products.csv"' \
--form 'kind=csv'

To automatically save the artifactId for future requests:

  1. Go to the Scripts tab in your Postman request.
  2. Select Post-response.
  3. Add the following script:pm.environment.set("artifactId", pm.response.json().artifact_id);

Example Response:

{
  "artifact_id": "art_95c650ded4824dc79bb6df16427e4a10",
  "kind": "csv",
  "source": "upload",
  "bytes": 45678,
  "sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
  "content_type": "text/csv",
  "uri": "s3://product-agent-data-prod-ue2/optimize-artifacts/svc_yourClientId/art_95c650ded4824dc79bb6df16427e4a10/products.csv",
  "original_filename": "products.csv",
  "brand_id": "691df5949676c8e0b1d7b6b3",
  "created_at": "2026-05-15T10:30:00Z"
}

Optimize Workflow

Create an optimization workflow that processes the uploaded artifact. Store the returned workflow id as workflowId.

curl --location '{{baseUrl}}/v2/optimize/workflows' \
--header 'Authorization: Bearer {{access_token}}' \
--header 'domain: {{domain}}' \
--header 'Content-Type: application/json' \
--data '{
  "input": {
    "artifact": { "artifact_id": "{{artifactId}}" }
  },
  "name": "Spring 2026 catalog enrichment",
  "origin": "API"
}'

To automatically save the workflowId for future requests:

  1. Go to the Scripts tab in your Postman request.
  2. Select Post-response.
  3. Add the following script:pm.environment.set("workflowId", pm.response.json().id);

Example Response:

{
  "id": "9349773c-27fe-4d4f-a093-a5793dff8702",
  "brand_id": "691df5949676c8e0b1d7b6b3",
  "type": "OPTIMIZE",
  "origin": "API",
  "workflow_type": null,
  "status": "PENDING",
  "current_step": null,
  "current_hitl_gate": null,
  "name": "Spring 2026 catalog enrichment",
  "tags": [],
  "started_at": null,
  "completed_at": null,
  "last_error": null,
  "retry_count": 0,
  "created_at": "2026-05-15T10:35:00Z",
  "updated_at": "2026-05-15T10:35:00Z"
}

Get Workflow Status

Poll the workflow until status reports COMPLETED. A status of REVIEW_PENDING indicates that the workflow is paused at a human-in-the-loop gate; the current_hitl_gate field identifies the active gate.

Review gates are approved in the CommerceOS web application, not through the API. When polling returns status: REVIEW_PENDING, direct the assigned reviewer to CommerceOS to evaluate and approve the gate. Once the reviewer acts in the UI, the workflow resumes automatically and subsequent polls reflect the new state.

curl --location '{{baseUrl}}/v2/optimize/workflows/{{workflowId}}' \
--header 'Authorization: Bearer {{access_token}}' \
--header 'domain: {{domain}}'

Example Response:

{
  "id": "9349773c-27fe-4d4f-a093-a5793dff8702",
  "brand_id": "691df5949676c8e0b1d7b6b3",
  "type": "OPTIMIZE",
  "origin": "API",
  "workflow_type": null,
  "status": "REVIEW_PENDING",
  "current_step": "TAXONOMY_MAP",
  "current_hitl_gate": "TAXONOMY_REVIEW",
  "name": "Spring 2026 catalog enrichment",
  "tags": [],
  "started_at": "2026-05-15T10:35:12Z",
  "completed_at": null,
  "last_error": null,
  "retry_count": 0,
  "progress": {
    "current_phase": "taxonomy",
    "current_step": "TAXONOMY_MAP",
    "current_gate": "TAXONOMY_REVIEW",
    "phases": {
      "validation": { "status": "COMPLETED" },
      "taxonomy":   { "status": "REVIEW_PENDING" },
      "enrichment": { "status": "NOT_STARTED" },
      "publish":    { "status": "NOT_STARTED" }
    }
  },
  "created_at": "2026-05-15T10:35:00Z",
  "updated_at": "2026-05-15T10:42:08Z"
}

The status value progresses through PENDING, then RUNNING or IN_PROGRESS, then REVIEW_PENDING when paused at a human-in-the-loop gate, and ultimately COMPLETED. FAILED and CANCELLED are terminal alternatives.