Products

Catalog products represent physical products, services, fees, and digital goods. Product variants and bundles are the sellable objects used for automatic price resolution and inventory tracking.

Methods

POST/v1/products

Create a new catalog product. Simple products use default_variant; optioned products use options and variants.

GET/v1/products/{product_id}

Get a product by product_id.

PATCH/v1/products/{product_id}

Update parent product fields using sparse update semantics. Categories and variant inventory use dedicated endpoints.

GET/v1/products

List products with filtering, sorting, and cursor pagination.

PUT/v1/products/{product_id}/categories

Replace the full category list for a product. Send an empty array to clear all categories.

DELETE/v1/products/{product_id}

Permanently delete a product.

GET /v1/products/{product_id} and GET /v1/products accept optional fields, a comma-separated list of public response fields to include, such as product_id,name,status.

CreateProduct

Request Fields

namestringrequired

Product name, 1-255 characters.

product_typestringrequired

Product type: physical, service, fee, digital.

default_variantProductVariantInput

Required for simple products. Contains sellable-unit fields such as price, SKU, inventory policy, and initial quantity.

optionsProductOptionInput[]

Required with variants for optioned products.

variantsProductVariantInput[]

Required with options for optioned products.

descriptionstring

Product description.

categoriesstring[]

Category labels.

image_urlstring

URL to product image.

metadatamap<string, string>

Up to 50 key-value pairs.

statusstring

Initial product status: active or inactive. If omitted, Flint creates the product as active.

Example

Request
JSON
{
  "name": "Premium Widget",
  "product_type": "physical",
  "default_variant": {
    "unit_price_money": { "amount": 2500, "currency": "USD" },
    "sku": "WIDGET-001",
    "initial_quantity_available": 100,
    "enforce_inventory_limit": true
  }
}
Response
JSON
{
  "data": {
    "product_id": "prod_abc123",
    "name": "Premium Widget",
    "product_type": "physical",
    "status": "active",
    "default_variant": {
      "variant_id": "var_abc123",
      "unit_price_money": { "amount": 2500, "currency": "USD" },
      "sku": "WIDGET-001",
      "quantity_available": 100
    }
  }
}

UpdateProduct

Mutable fields: name, description, image_url, status, product_type, default_variant_id, metadata.

Sparse update semantics:

  • Omitted fields are unchanged.
  • Present fields replace the previous value.
  • description, image_url, and default_variant_id can be cleared with "".
  • metadata merges by key. Send {} to clear all metadata. Send "" as a metadata value to delete that key.
  • Requests with no mutable fields fail with NO_FIELDS_TO_UPDATE.

Use PUT /v1/products/{product_id}/categories to manage categories after creation, and POST /v1/products/{product_id}/variants/{variant_id}/inventory-adjustments to change inventory levels. PATCH /v1/products/{product_id} does not mutate either field.

ReplaceProductCategories

Request field:

  • categories (string[]) replaces the full set of categories. Send [] to clear all categories.

AdjustProductVariantInventory

Request fields:

  • quantity_delta (integer, required): positive to add stock, negative to remove stock. Cannot be 0.
  • change_reason (string, optional): manual_adjustment, order_fulfillment, order_cancellation, order_refund, received_stock, inventory_count, damage, theft, loss.
  • note (string, optional): free-form note stored with the inventory history entry.

ListProducts

Common filters:

  • query
  • product_type
  • status
  • category
  • only_in_stock
  • fields
  • created_after
  • created_before
  • updated_after
  • updated_before

Use GET /v1/catalog/by-sku/{sku} for exact SKU lookup across variants and bundles.

Enums

Product Type

physical, service, fee, digital

Product Status

active, inactive (reversible), deleted (permanent)

Sort Options

name, created_at, updated_at

Pagination

GET /v1/products returns next_page_token. Pass it back as page_token with the same sort settings on the next request.