Product Management

Product endpoints let a customer integration list and inspect the products in one catalogue, including variants, offers, images, enrichment progress, and moderation state. An integration can also review Youzu's enrichment against the retained vendor listing, approve or reject individual enrichment groups, and configure automatic approval for a vendor's future enrichment changes.

Enriched attributes follow the catalogue's Category Attributes definitions. Preserve numeric and boolean values, including 0 and false, and use exact configured strings for choices. Missing evidence leaves attributes unset. Supplier-provided original attributes remain the facts submitted with the product.

Authenticate a product request

Product routes use the single-use bearer tokens described in Authentication. Use product:read for list, detail, near-space, vendor-listing, and vendor-setting reads. Use product:update to change an enhancement decision, approval state, or vendor setting. Generate a fresh token for every request. For the catalogue-free workflow, omit the catalogue ID when creating the token and never send client or catalogue IDs as product query parameters.

See Upload Product Data and Track Processing for pagination and processing readiness, and Check Moderation Results for moderation fields and completion.

Product routes

Method Path Purpose
GET /api/v1/product List and filter products
GET /api/v1/product/{id} Retrieve one product by ID
PUT /api/v1/product/offers/{offerId} Record whether the offer's vendor accepted enhanced variant data
GET, PATCH /api/v1/product/offers/{offerId} Compare retained vendor input with enrichment and manage field-group approvals
GET, PATCH /api/v1/product/vendors/{vendorId} Read or change automatic approval for future enrichment changes
POST /api/v1/product/search/near-space Find products in the same visual space or category
POST /api/v1/product/user-products/{productId} Save a product for the token-scoped user
DELETE /api/v1/product/user-products/{productId} Remove a product saved by the token-scoped user

List and filter products

curl "https://platform.youzu.ai/api/v1/product?page=1&limit=20&process_status=completed&in_stock=true&sort=updated_at&order=desc" \
  -H "Authorization: Bearer YOUR_TOKEN"

Query parameters use snake_case:

Parameter Description
page Page number, minimum 1; defaults to 1
limit Page size from 1 to 100; defaults to 10
search Search product names and SKUs; maximum 500 characters
category_id One category ID or comma-separated category IDs
brand_id Filter by a brand ID already returned in a product record
vendor_id One vendor UUID. A product matches when one live offer belongs to that vendor. With approval_status, the same live offer must satisfy both. A non-UUID, repeated, or comma-separated value is rejected.
is_published Filter by whether a live offer is published
in_stock Filter by whether a live offer is in stock
process_status pending, processing, completed, partial, or failed
moderation_status Result status such as approved, pending, or flagged, or violation severity high, medium, or low
approval_status pending, approved, or rejected. Matches when title, description, category, and attributes on that offer's vendor listing are all this value. A missing listing or group does not match.
custom_properties URL-encoded, non-array JSON object matched against supplier properties
price_min, price_max Non-negative inclusive offer-price range; when both are present, price_min must not exceed price_max
currency Case-insensitive three-letter currency used by price filtering and sorting; normalized to uppercase and defaults to EUR
sort created_at, updated_at, or price
order asc or desc

approval_status matches only when title, description, category, and attributes on that offer's vendor listing are all the requested value. It is separate from moderation_status and from vendorAcceptedEnhancement.

List responses include customer-facing product records and pagination metadata:

{
  "success": true,
  "data": [
    {
      "id": "b60d9e58-2607-4ad5-a069-8ce77b8ad41d",
      "name": {"en": "Ergonomic Office Chair"},
      "price": {"USD": 299.99},
      "inStock": true,
      "isPublished": true,
      "url": "https://shop.example/products/chair-001",
      "externalRef": "CLIENT:CATALOGUE:550e8400-e29b-41d4-a716-446655440000:PRODUCT:CHAIR-001",
      "sku": "CHAIR-001",
      "versionHash": "8f2b9f1e6dd54a03",
      "images": [],
      "variants": [],
      "offers": [],
      "processStatus": "processing",
      "processing": {
        "completedStages": 3,
        "totalStages": 5,
        "currentStage": "indexing",
        "stages": [
          {"key": "ingest", "done": true},
          {"key": "images", "done": true},
          {"key": "enrichment", "done": true},
          {"key": "indexing", "done": false},
          {"key": "dedup", "done": false}
        ]
      },
      "moderation": null
    }
  ],
  "meta": {
    "currentPage": 1,
    "pageSize": 20,
    "totalPages": 4,
    "totalItems": 72
  }
}

Response bodies use camelCase. Depending on the source data and processing state, a product can also include its description, discounted price, brand, categories, identifiers, dimensions, weight, colour, custom properties, variant axes, AI-extracted and translated attributes, public image metadata, timestamps, and nullable current moderation result.

Each entry in variants can include variantValues, a dynamic map of configuration axes to values, for example {"color":"black","capacity":"256gb"}. Newly inferred keys and string values are lowercased, and keys normally use lower_snake_case. Treat the map as extensible and preserve unknown keys rather than relying on a fixed list of axes.

Customer slugs in product responses

Product details, product lists, product search, and Youzu Lens results include customer slug metadata directly in each product. Existing categories and attributes remain available.

categoryDetails lists the assigned categories with id, name, parentId, the existing slug identifier, and optional category_slug. attribute_slugs groups matched attribute metadata by assigned category ID and attribute key:

{
  "categoryDetails": [
    {"id": "11111111-1111-4111-8111-111111111111", "name": "Running shoes", "parentId": null, "slug": "running-shoes", "category_slug": "RUNNING_SHOES"}
  ],
  "attribute_slugs": {
    "11111111-1111-4111-8111-111111111111": {
      "color": {"attribute_slug": "COLOR", "attribute_value_slug": "BLK"},
      "spike_count": {"attribute_value_slug": "ZERO"},
      "waterproof": {"attribute_value_slug": "NO"},
      "material": {"attribute_slug": "MATERIAL"}
    }
  }
}

Each variant also has its own attribute_slugs, matched against that variant's enhanced values. The product-level map uses the same representative variant as the product's attributes. Products assigned to multiple categories resolve each category independently. A product with no assigned categories uses general for mappings from the general defaults.

Unset slugs and unmatched value mappings are omitted. A product or variant with no matches has an empty map. Free-text attributes can return a key slug but never a value slug. Slug edits are read from current configuration on subsequent requests; they do not change product facts. Configure them as described in Category Attributes.

Understand variants and offers

A variant contains Youzu's consolidated, enhanced name, description, and attributes. Its offers represent the vendor-specific records that contributed to that variant. One variant can therefore contain offers from multiple vendors.

Offers with a merchant include its UUID as vendorId; the vendor display name is absent for unnamed merchants. Vendor-supplied values remain available on the offer as originalName, originalDescription, and read-only originalAttributes, so an integration can show them alongside the enhanced variant values.

vendorAcceptedEnhancement records the vendor's decision about the enhanced variant data:

  • null: no decision has been recorded.
  • true: the vendor accepted the enhanced data.
  • false: the vendor explicitly declined the enhanced data.

This field is metadata only. Changing it does not apply, regenerate, or overwrite any product, variant, or offer data.

Record vendor acceptance

Use the offer ID returned inside a variant and a fresh product:update token. The request body accepts only the required vendorAcceptedEnhancement boolean:

curl -X PUT "https://platform.youzu.ai/api/v1/product/offers/5bf718f6-75c1-4145-b88b-2a0949226d68" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"vendorAcceptedEnhancement":true}'

The response contains the updated offer:

{
  "success": true,
  "data": {
    "id": "5bf718f6-75c1-4145-b88b-2a0949226d68",
    "variantId": "9820d390-3207-4015-888c-58d546b8b40f",
    "vendorId": "bd0a6fe7-9288-4654-8770-fb3e9f670231",
    "originalName": {"en": "Office chair black"},
    "originalDescription": {"en": "Adjustable office chair"},
    "originalAttributes": {"colour": "black"},
    "vendorAcceptedEnhancement": true,
    "price": {"USD": 299.99},
    "inStock": true,
    "isPublished": true,
    "sku": "CHAIR-001-BLACK",
    "url": "https://shop.example/products/chair-001-black",
    "externalRef": "VENDOR-CHAIR-001-BLACK",
    "versionHash": "3f41982425e33cf1"
  }
}

Send false to record an explicit decline. The undecided null state is returned only before a decision; it cannot be submitted to clear or reset one. The token determines the tenant and catalogue, and the request cannot change the offer's vendor, original values, or enhanced variant data.

Review enrichment by field group

Use the vendor-listing resource when a vendor needs to compare its latest submitted listing with Youzu's enriched data and make separate decisions for title, description, category, and attributes. The resource is keyed by the offer ID returned inside a product variant.

curl "https://platform.youzu.ai/api/v1/product/offers/5bf718f6-75c1-4145-b88b-2a0949226d68" \
  -H "Authorization: Bearer YOUR_PRODUCT_READ_TOKEN"

The response retains the original vendor values in source_data, using the same four groups as approvals, and reports the current decision for every group:

{
  "success": true,
  "data": {
    "offer_id": "5bf718f6-75c1-4145-b88b-2a0949226d68",
    "vendor_id": "bd0a6fe7-9288-4654-8770-fb3e9f670231",
    "source_data": {
      "title": {"en": "Office chair black"},
      "description": {"en": "Adjustable office chair"},
      "category": ["Office furniture"],
      "attributes": {"colour": "black"}
    },
    "approvals": {
      "title": "pending",
      "description": "approved",
      "category": "pending",
      "attributes": "rejected"
    },
    "created_at": "2026-09-20T10:00:00.000Z",
    "updated_at": "2026-09-20T10:05:00.000Z"
  }
}

Each group is pending, approved, or rejected. Update one or more groups with a fresh product:update token. Omitted groups keep their current state; the request must contain at least one group and does not accept unknown group names.

curl -X PATCH "https://platform.youzu.ai/api/v1/product/offers/5bf718f6-75c1-4145-b88b-2a0949226d68" \
  -H "Authorization: Bearer YOUR_PRODUCT_UPDATE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "approvals": {
      "title": "approved",
      "attributes": "rejected"
    }
  }'

source_data.title and source_data.description are keyed by language tag, source_data.category contains the submitted category names, and source_data.attributes contains the submitted custom properties. Operational ingestion fields such as SKU, price, availability, URLs, and images are not part of this approval comparison.

Approval state follows the enriched data. When AI processing changes a group, that group returns to pending; decisions for unchanged groups remain intact. A category change applies to every active offer on the product. Title, description, and attribute changes apply to every active offer on the affected variant. Re-ingestion refreshes source_data and preserves decisions when the merchant and enriched output are unchanged.

Reassigning the offer to another merchant, or attaching one to an offer that had none, resets all four groups to pending, even if the new merchant has automatic approval enabled. That merchant's settings apply to subsequent enrichment changes. Renaming a merchant or attaching its external reference without changing its UUID preserves decisions. See Merchant Identity for the ingestion rules.

The vendor-listing decision is finer-grained than vendorAcceptedEnhancement on the offer. Updating either resource does not apply, regenerate, publish, or overwrite product data.

Offers created before this feature receive retained source and approval data on their next ingestion. Until then, GET /api/v1/product/offers/{offerId} returns 404.

Automatically approve future enrichment changes

An offer without a merchant has no vendorId. Attach a merchant through ingestion first; this resets its approval groups to pending.

For an offer with a merchant, use its vendorId to read the vendor setting with a product:read token:

curl "https://platform.youzu.ai/api/v1/product/vendors/bd0a6fe7-9288-4654-8770-fb3e9f670231" \
  -H "Authorization: Bearer YOUR_PRODUCT_READ_TOKEN"

The merchant response includes its UUID id, nullable name, and nullable external_ref. A reference-only merchant has a null name; a name-only merchant has a null reference. External references are unique within the catalogue, but names may be shared by merchants with different references.

Enable automatic approval for selected groups with a fresh product:update token. Omitted groups keep their current setting:

curl -X PATCH "https://platform.youzu.ai/api/v1/product/vendors/bd0a6fe7-9288-4654-8770-fb3e9f670231" \
  -H "Authorization: Bearer YOUR_PRODUCT_UPDATE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "auto_approve_enrichment": {
      "title": true,
      "description": false,
      "category": true,
      "attributes": false
    }
  }'

Each value in auto_approve_enrichment controls the matching approval group. When a group is true, a future change to that enriched output becomes approved instead of pending. Set all four values to true to approve every future title, description, category, and attribute change automatically; set all four to false to disable automatic approval.

PATCH requests merge the supplied values, so changing title alone does not alter the other three settings. The configuration affects only future enrichment changes; updating it does not rewrite existing approval decisions. The setting is catalogue-scoped, like the vendor returned by the token-scoped product API.

Search by visual space

POST /api/v1/product/search/near-space finds products in the same visual space or category as one query image. Generate a single-use product:read token and provide exactly one of these inputs:

  • file: a decodable JPEG, PNG, or WebP multipart upload no larger than 10 MiB.
  • image_url: a public HTTP or HTTPS URL without embedded credentials. Every redirect must remain on public network addresses, and the downloaded image must be a decodable JPEG, PNG, or WebP no larger than 10 MiB.
  • detectionId: the UUID returned for an object by /api/v1/object-detection; Youzu safely retrieves the cached source image and crops its validated bounding box.

For an image URL:

curl -X POST "https://platform.youzu.ai/api/v1/product/search/near-space?matchLevel=similar" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "image_url": "https://images.example.com/rooms/office.webp",
    "limit": 20
  }'

For a direct upload, send file and optional limit fields as multipart/form-data. For a cached detection, send detectionId and optional limit in JSON. limit defaults to 20 and accepts 1 through 100; the optional matchLevel query parameter is exact, similar, or broad.

The response uses the same { success, data, meta } product-list envelope shown above. A 400 indicates an invalid input or unsafe URL, 404 means a cached detection is no longer available, 413 indicates an image over 10 MiB, and 502 indicates that the remote image or product service was unavailable.

Save products for a user

Saved-product routes use the user carried by the bearer token. Supply a stable userReference when creating each token so the saved list belongs to the intended customer. Generate a separate single-use token for every operation:

  • Use product:create for POST /api/v1/product/user-products/{productId}. The product must exist in the token-scoped catalogue. Its optional JSON body can include generation_id to associate the saved item with a non-deleted Youzu generation owned by the same token-scoped user. Success returns 201 and {"message":"Product saved successfully"}.
  • Use product:delete for DELETE /api/v1/product/user-products/{productId}. Catalogue scope is stored with the saved item, so removal remains available even if the upstream product was later deleted. Success returns 204 with no response body.

The productId is a product identifier from a catalogue-scoped product response. Never reuse the read token that returned the product for a save or delete request.

Understand processing state

processStatus is the product-level summary: pending and processing are nonterminal; completed, partial, and failed are terminal. partial means some selected work failed while usable product output remains.

The processing object is authoritative for stage-level progress. It returns only stages in the catalogue's compiled, selected pipeline graph. Records use the stable keys ingest, image_acquisition, image_indexing, semantic_text_indexing, ai_processing, identity_enrichment, dedup_indexing, and deduplication, and every record includes status, required, and attemptCount. A returned stage can be skipped when runtime processing determines that no work is needed.

processing.errors contains customer-safe { code, stage, retryable } entries. Internal provider text and traces are omitted. See Upload Product Data and Track Processing for the complete status and stage table.

Moderation runs separately and appears in moderation; it is not a product-processing stage. Each applicable stageResults entry is nonterminal at pending or processing and terminal at approved, flagged, error, or skipped. The aggregate moderation.status is a summary, so inspect stage results to determine completion.

The API returns real pipeline state. It does not fabricate stock counts, generic product status, AI feature lists, or an always-completed processing value.

Handle errors

Customer-facing product routes preserve supported upstream 4xx statuses with safe messages. Upstream server and transport failures return 502 without exposing the upstream response body. A 401 generally means the token is missing, expired, outside the required product scope, or has already been consumed.