Products

Products represent the items available for sale in your application. On this page, we'll dive into the different product endpoints you can use to manage products programmatically. We'll look at how to query, create, update, and delete products.

The product model

The product model includes detailed information about the product, including its variants, prices, and inventory details.

Properties

  • Name
    id
    Type
    string
    Description

    Unique identifier for the product.

  • Name
    name
    Type
    string
    Description

    The name of the product.

  • Name
    description
    Type
    string
    Description

    The description of the product.

  • Name
    imageUrl
    Type
    string
    Description

    URL of the product image.

  • Name
    price
    Type
    number
    Description

    The base price of the product.

  • Name
    discountPrice
    Type
    number
    Description

    The lowest price of the product.

  • Name
    currency
    Type
    string
    Description

    The currency code of the product price.

  • Name
    variants
    Type
    array
    Description

    List of product variants.

    • Name
      id
      Type
      string
      Description

      Unique identifier for the product variant.

    • Name
      name
      Type
      string
      Description

      The name of the variant.

    • Name
      inventoryQuantity
      Type
      number
      Description

      The inventory quantity of the variant.

    • Name
      inventoryTotal
      Type
      number
      Description

      The total inventory quantity of the variant in grams

    • Name
      price
      Type
      number
      Description

      The base price of the variant.

    • Name
      discountPrice
      Type
      number
      Description

      The discounted price of the variant.

    • Name
      discountPercentage
      Type
      number
      Description

      The discount percentage of the variant.

    • Name
      currencyCode
      Type
      string
      Description

      The currency code of the variant price.

    • Name
      priceListId
      Type
      string
      Description

      The price list identifier of the variant.

    • Name
      priceListName
      Type
      string
      Description

      The name of the price list.

    • Name
      weight
      Type
      number
      Description

      The weight of the variant.

    • Name
      prices
      Type
      array
      Description

      List of prices for the variant.

      • Name
        id
        Type
        string
        Description

        Unique identifier for the price.

      • Name
        price
        Type
        number
        Description

        The price amount.

      • Name
        priceList
        Type
        object
        Description

        The price list details.

        • Name
          id
          Type
          string
          Description

          Unique identifier for the price list.

        • Name
          name
          Type
          string
          Description

          The name of the price list.

      • Name
        currency
        Type
        string
        Description

        The currency code of the price.

      • Name
        createdAt
        Type
        string
        Description

        The timestamp when the price was created.

    • Name
      quantity
      Type
      number
      Description

      The quantity of the variant (grams in the fraction).

  • Name
    strain
    Type
    object
    Description

    The strain information of the product if it is flower

    • Name
      cannabinoids
      Type
      object
      Description

      The cannabinoids content of the strain.

      • Name
        thc
        Type
        number
        Description

        The THC content percentage of the strain.

      • Name
        cbg
        Type
        number
        Description

        The CBG content percentage of the strain.

      • Name
        cbd
        Type
        number
        Description

        The CBD content percentage of the strain.

    • Name
      terpenes
      Type
      array
      Description

      List of terpenes present in the strain.

    • Name
      flavors
      Type
      array
      Description

      List of flavors associated with the strain.

    • Name
      genetics
      Type
      object
      Description

      The genetics of the strain.

      • Name
        parents
        Type
        array
        Description

        List of parent strains.

      • Name
        children
        Type
        array
        Description

        List of child strains.

  • Name
    category
    Type
    string
    Description

    The category of the product.

  • Name
    isFlower
    Type
    boolean
    Description

    Whether the product is a flower.

  • Name
    inventoryQuantity
    Type
    number
    Description

    The total inventory quantity of the product.

Complete product example

{
  "id": "prod_01HQ8BLUE_DREAM",
  "name": "Blue Dream Indoor",
  "description": "Blue Dream es una cepa híbrida sativa-dominante que cruza Blueberry con Haze. Conocida por su equilibrio entre relajación cerebral y estimulación corporal, es ideal para aliviar dolor, depresión y náuseas mientras mantiene claridad mental.",
  "imageUrl": "https://storage.cannahub.tech/products/blue-dream-main.jpg",
  "price": 330000,
  "discountPrice": 320000,
  "currency": "ARS",
  "variants": [
    {
      "id": "variant_01HQ_BD_1G",
      "name": "Blue Dream 1g",
      "inventoryQuantity": 20,
      "inventoryTotal": 20,
      "weight": 1,
      "price": 350000,
      "discountPrice": null,
      "discountPercentage": 0,
      "currencyCode": "ARS",
      "priceListId": "plist_basic",
      "priceListName": "Básica",
      "quantity": 1,
      "prices": [
        {
          "id": "price_01HQ_BD_1G_BASIC",
          "price": 350000,
          "priceList": {
            "id": "plist_basic",
            "name": "Básica"
          },
          "currency": "ARS",
          "createdAt": "2024-03-15T10:00:00Z"
        },
        {
          "id": "price_01HQ_BD_1G_PREMIUM",
          "price": 332500,
          "priceList": {
            "id": "plist_premium",
            "name": "Premium"
          },
          "currency": "ARS",
          "createdAt": "2024-03-15T10:00:00Z"
        }
      ]
    },
    {
      "id": "variant_01HQ_BD_5G",
      "name": "Blue Dream 5g",
      "inventoryQuantity": 8,
      "inventoryTotal": 40,
      "weight": 5,
      "price": 1650000,
      "discountPrice": 1650000,
      "discountPercentage": 6,
      "currencyCode": "ARS",
      "priceListId": "plist_basic",
      "priceListName": "Básica",
      "quantity": 5,
      "prices": [
        {
          "id": "price_01HQ_BD_5G_BASIC",
          "price": 1650000,
          "priceList": {
            "id": "plist_basic",
            "name": "Básica"
          },
          "currency": "ARS",
          "createdAt": "2024-03-15T10:00:00Z"
        }
      ]
    }
  ],
  "strain": {
    "id": "strain_blue_dream",
    "name": "Blue Dream",
    "type": "hybrid",
    "cannabinoids": {
      "thc": 20,
      "cbg": 0.8,
      "cbd": 1.5
    },
    "terpenes": ["Mirceno", "Pineno", "Caryofileno", "Limoneno"],
    "flavors": ["Arándano", "Dulce", "Terroso"],
    "genetics": {
      "parents": ["Blueberry", "Haze"],
      "children": []
    },
    "effects": {
      "helpsWith": ["Dolor", "Depresión", "Náuseas"],
      "feelings": ["Eufórico", "Creativo", "Relajado"],
      "negatives": ["Boca seca", "Ojos rojos"]
    }
  },
  "category": "flores",
  "isFlower": true,
  "manageInventory": true,
  "inventoryQuantity": 60
}

Field Mapping Reference

Cannahub uses Medusa.js for product management. Here's how frontend types map to Medusa API:

Product ↔ Medusa Product Mapping

Cannahub FrontendMedusa APITypeNotes
idproduct.idstringMedusa product ID
nameproduct.titlestring-
descriptionproduct.descriptionstringFull description
imageUrlproduct.thumbnailstring (URL)Main product image
imagesproduct.images[]arrayAll product images
priceCalculated from variantsnumberLowest variant price
discountPriceCalculated from variantsnumberLowest discount price
currencyregion.currency_codestringFrom region (ARS, USD, etc.)
variantsproduct.variants[]arrayProduct fractions
strainproduct.metadata.strainobjectComplete strain info in metadata
categoryproduct.metadata.categorystringflores, extracciones, etc.
isFlowerproduct.metadata.isFlowerbooleanWhether product is flower
manageInventoryvariant.manage_inventorybooleanTrack stock or not
inventoryQuantitySum of variant quantitiesnumberTotal units across all variants

Variant ↔ Medusa Variant Mapping

Cannahub FrontendMedusa APITypeNotes
idvariant.idstring-
namevariant.titlestringe.g. "Blue Dream 5g"
inventoryQuantityvariant.inventory_quantitynumberUnits available
inventoryTotalvariant.metadata.inventoryTotalnumberTotal grams (quantity × weight)
weightvariant.metadata.weightnumberGrams per unit
pricevariant.prices[].amountnumberPrice for current price list
discountPriceCalculatednumberIf discount applied
discountPercentagevariant.metadata.discount_percentagenumberDiscount % for this fraction
quantityvariant.metadata.quantitynumberSame as weight (for clarity)
prices[]variant.prices[]arrayAll prices across price lists

Complete Product Metadata Structure

{
  "metadata": {
    "strainId": "strain_blue_dream",
    "isFlower": true,
    "requireReprocann": true,
    "category": "flores",
    "subcategory": "hibridas",
    "thc": 20,
    "cbd": 1.5,
    "cbg": 0.8,
    "terpenes": ["mirceno", "pineno", "caryofileno", "limoneno"],
    "effects": ["energetic", "creative", "euphoric"],
    "cultivationType": "indoor",
    "harvestDate": "2024-03-01",
    "batchNumber": "BATCH-2024-03-001",
    "strain": {
      "id": "strain_blue_dream",
      "name": "Blue Dream",
      "altNames": "Azure Haze, BD",
      "type": "hybrid",
      "description": "Cepa híbrida equilibrada...",
      "cannabinoids": {
        "thc": 20,
        "cbd": 1.5,
        "cbg": 0.8
      },
      "terpenes": ["Mirceno", "Pineno", "Caryofileno"],
      "flavors": ["Arándano", "Dulce", "Terroso"],
      "genetics": {
        "parents": ["Blueberry", "Haze"],
        "children": []
      },
      "effects": {
        "helpsWith": ["Dolor", "Depresión", "Náuseas"],
        "feelings": ["Eufórico", "Creativo", "Relajado"],
        "negatives": ["Boca seca", "Ojos rojos"]
      },
      "growInformation": {
        "type": "indoor",
        "difficulty": "moderate",
        "height": "medium",
        "yield": "high",
        "floweringWeeks": "9-10",
        "notes": "Requiere podas regulares"
      }
    }
  }
}

Variant Metadata Structure

{
  "metadata": {
    "weight": 5,
    "unit": "grams",
    "inventoryTotal": 40,
    "quantity": 5,
    "packaging": "jar",
    "discount_percentage": 6
  }
}

GET/store/products

List all products

This endpoint allows you to retrieve a paginated list of all your products. By default, a maximum of ten products are shown per page.

Optional attributes

  • Name
    category
    Type
    string
    Description

    Filter products by category.

  • Name
    limit
    Type
    integer
    Description

    Limit the number of products returned.

  • Name
    page
    Type
    integer
    Description

    Start offset of products returned.

  • Name
    sort
    Type
    string
    Description

    Sort the list of products by specified attribute.

  • Name
    direction
    Type
    string
    Description

    Sort direction, can be 'asc' or 'desc'.

  • Name
    search
    Type
    string
    Description

    Search products by name or description.

Request

GET
/store/products
curl -G https://api.yourapp.com/store/products \
  -H "Authorization: Bearer {token}" \
  -d limit=10

Response

[
  {
    "id": "l7cGNIBKZiNJ6wqF",
    "name": "Product 1",
    "description": "This is product 1",
    "imageUrl": "https://assets.yourapp.com/products/product1.jpg",
    "price": 100,
    "discountPrice": 80,
    "currency": "USD",
    "variants": [
      {
        "id": "variant1",
        "name": "Variant 1",
        "inventoryQuantity": 50,
        "price": 100,
        "currencyCode": "USD",
        // ...
      }
    ],
    "strain": null,
    "category": "Category 1",
    "isFlower": true,
    "inventoryQuantity": 50
  }
]

GET/api/products/:id

Retrieve a product

This endpoint allows you to retrieve a product by providing the product ID.

Request

GET
/api/products/:id
curl https://yourapp.com/api/products/:id \
  -H "Authorization: Bearer {token}"

Response

{
  "id": "l7cGNIBKZiNJ6wqF",
  "name": "Product 1",
  "description": "This is product 1",
  "imageUrl": "https://assets.yourapp.com/products/product1.jpg",
  "price": 100,
  "discountPrice": 80,
  "currency": "USD",
  "variants": [
    {
      "id": "variant1",
      "name": "Variant 1",
      "inventoryQuantity": 50,
      "price": 100,
      "currencyCode": "USD"
    }
  ],
  "strain": null,
  "category": "Category 1",
  "isFlower": true,
  "inventoryQuantity": 50
}

POST/api/products

Create a product

This endpoint allows you to create a new product in your application.

Required attributes

  • Name
    name
    Type
    string
    Description

    The name of the product.

  • Name
    description
    Type
    string
    Description

    The description of the product.

  • Name
    imageUrl
    Type
    string
    Description

    URL of the product image.

  • Name
    category
    Type
    string
    Description

    The category of the product.

Optional attributes

  • Name
    strain
    Type
    object
    Description

    The strain information of the product.

Request

POST
/api/products
curl -X POST https://yourapp.com/api/products \
  -H "Authorization: Bearer {token}" \
  -d '{
    "name": "Product 1",
    "description": "This is product 1",
    "imageUrl": "https://assets.yourapp.com/products/product1.jpg",
    "currency": "USD"
  }'

Response

{
  "id": "l7cGNIBKZiNJ6wqF",
  "name": "Product 1",
  "description": "This is product 1",
  "imageUrl": "https://assets.yourapp.com/products/product1.jpg",
  "currency": "USD",
  "variants": [],
  "strain": null,
  "category": "Category 1",
  "isFlower": true,
}

PATCH/api/products/:id

Update a product

This endpoint allows you to update an existing product in your application.

Optional attributes

  • Name
    name
    Type
    string
    Description

    The name of the product.

  • Name
    description
    Type
    string
    Description

    The description of the product.

  • Name
    imageUrl
    Type
    string
    Description

    URL of the product image.

  • Name
    category
    Type
    string
    Description

    The category of the product.

  • Name
    strain
    Type
    object
    Description

    The strain information of the product.

Request

PUT
/api/products/:id
curl -X PUT https://yourapp.com/api/products/:id \
  -H "Authorization: Bearer {token}" \
  -d '{
    "name": "Updated Product",
    "description": "This is an updated product",
    "imageUrl": "https://assets.yourapp.com/products/updatedproduct.jpg",
  }'

Response

{
  "id": "l7cGNIBKZiNJ6wqF",
  "name": "Updated Product",
  "description": "This is an updated product",
  "imageUrl": "https://assets.yourapp.com/products/updatedproduct.jpg",
  "currency": "USD",
  "variants": [],
  "strain": null,
  "category": "Category 1",
  "isFlower": true,
}

POST/api/products/:productId/variants

Create a product variant

This endpoint allows you to create a new product variant in your application.

Required attributes

  • Name
    name
    Type
    string
    Description

    The name of the variant.

  • Name
    inventoryQuantity
    Type
    number
    Description

    The inventory quantity of the variant.

  • Name
    currencyCode
    Type
    string
    Description

    The currency code of the variant price.

Optional attributes

  • Name
    weight
    Type
    number
    Description

    The weight of the variant.

Request

POST
/api/products/:productId/variants
curl -X POST https://yourapp.com/api/products/:productId/variants \
  -H "Authorization: Bearer {token}" \
  -d '{
    "name": "New Variant",
    "inventoryQuantity": 50,
    "currencyCode": "USD"
  }'

Response

{
  "id": "variantId",
  "name": "New Variant",
  "inventoryQuantity": 50,
  "currencyCode": "USD",
  "weight": 10
}


GET/api/products/:id/with-inventory

BFF Route: Product with Inventory

Esta ruta BFF resuelve el problema N+1 al obtener un producto con todos sus datos de inventario. En lugar de hacer múltiples llamadas (1 para el producto + N para cada variante), esta ruta agrega toda la información en una sola llamada.

Problema que resuelve

Flujo actual (N+1 calls):
1. GET /admin/products/:id              → Producto base
2. GET /admin/stock-locations           → Ubicaciones disponibles
3. GET /admin/inventory-items?variant_id=v1  → Inventario variante 1
4. GET /admin/inventory-items?variant_id=v2  → Inventario variante 2
5. GET /admin/inventory-items?variant_id=v3  → Inventario variante 3
...N más por cada variante

Ejemplo: Producto con 5 variantes = 7+ llamadas API

Request

  • Name
    id
    Type
    string
    Description

    El ID del producto.

  • Name
    locationId
    Type
    string
    Description

    Filtrar inventario por ubicación específica. Si no se proporciona, retorna inventario de todas las ubicaciones.

Response

  • Name
    product
    Type
    Product
    Description

    El producto completo con strain, categoría y metadatos.

  • Name
    inventory
    Type
    object
    Description

    Mapa de inventario por variante.

    • Name
      [variantId]
      Type
      VariantInventory
      Description

      Datos de inventario para cada variante.

      • Name
        quantity
        Type
        number
        Description

        Cantidad total de unidades.

      • Name
        available
        Type
        number
        Description

        Cantidad disponible para venta.

      • Name
        reserved
        Type
        number
        Description

        Cantidad reservada en carritos/pedidos.

      • Name
        totalGrams
        Type
        number
        Description

        Total en gramos (quantity × weight).

      • Name
        byLocation
        Type
        array
        Description

        Desglose por ubicación.

        • Name
          locationId
          Type
          string
          Description

          ID de la ubicación.

        • Name
          locationName
          Type
          string
          Description

          Nombre de la ubicación.

        • Name
          quantity
          Type
          number
          Description

          Cantidad en esta ubicación.

        • Name
          available
          Type
          number
          Description

          Disponible en esta ubicación.

  • Name
    totalInventoryGrams
    Type
    number
    Description

    Total de gramos en inventario sumando todas las variantes.

  • Name
    totalInventoryUnits
    Type
    number
    Description

    Total de unidades en inventario.

  • Name
    lowStockVariants
    Type
    array
    Description

    Lista de variantes con stock bajo (menos de 5 unidades).

Request

GET
/api/products/:id/with-inventory
curl https://api.cannahub.tech/api/products/prod_01HQ8BLUE_DREAM/with-inventory \
  -H "Authorization: Bearer {token}"

Response

{
  "product": {
    "id": "prod_01HQ8BLUE_DREAM",
    "name": "Blue Dream Indoor",
    "description": "Cepa híbrida sativa-dominante...",
    "imageUrl": "https://storage.cannahub.tech/products/blue-dream.jpg",
    "price": 330000,
    "currency": "ARS",
    "variants": [
      {
        "id": "variant_01HQ_BD_1G",
        "name": "Blue Dream 1g",
        "weight": 1,
        "price": 350000
      },
      {
        "id": "variant_01HQ_BD_5G",
        "name": "Blue Dream 5g",
        "weight": 5,
        "price": 1650000
      }
    ],
    "strain": {
      "name": "Blue Dream",
      "type": "hybrid",
      "cannabinoids": { "thc": 20, "cbd": 1.5 }
    },
    "isFlower": true
  },
  "inventory": {
    "variant_01HQ_BD_1G": {
      "quantity": 20,
      "available": 18,
      "reserved": 2,
      "totalGrams": 20,
      "byLocation": [
        {
          "locationId": "sloc_dispensary",
          "locationName": "Dispensario Principal",
          "quantity": 15,
          "available": 13
        },
        {
          "locationId": "sloc_warehouse",
          "locationName": "Depósito",
          "quantity": 5,
          "available": 5
        }
      ]
    },
    "variant_01HQ_BD_5G": {
      "quantity": 8,
      "available": 8,
      "reserved": 0,
      "totalGrams": 40,
      "byLocation": [
        {
          "locationId": "sloc_dispensary",
          "locationName": "Dispensario Principal",
          "quantity": 8,
          "available": 8
        }
      ]
    }
  },
  "totalInventoryGrams": 60,
  "totalInventoryUnits": 28,
  "lowStockVariants": []
}

Flujo interno del BFF

Loading diagram...

Comparación de rendimiento

MétricaSin BFFCon BFFMejora
Llamadas API (5 variantes)7+1-86%
Latencia típica~700ms~200ms-71%
Cálculo de gramosClienteServidorCentralizado
Detección stock bajoManualAutomáticoIncluido

GET/api/products/with-inventory

BFF Route: Products with Inventory (List)

Versión paginada que lista productos con su inventario agregado. Ideal para dashboards de administración y vistas de catálogo con stock.

Query Parameters

  • Name
    locationId
    Type
    string
    Description

    Filtrar por ubicación específica.

  • Name
    limit
    Type
    number
    Description

    Número de productos por página (default: 20).

  • Name
    offset
    Type
    number
    Description

    Offset para paginación.

  • Name
    category
    Type
    string
    Description

    Filtrar por categoría.

  • Name
    lowStock
    Type
    boolean
    Description

    Si es true, solo retorna productos con stock bajo.

  • Name
    outOfStock
    Type
    boolean
    Description

    Si es true, solo retorna productos sin stock.

Request

GET
/api/products/with-inventory
curl "https://api.cannahub.tech/api/products/with-inventory?limit=20&lowStock=true" \
  -H "Authorization: Bearer {token}"

Response

{
  "products": [
    {
      "id": "prod_01HQ8BLUE_DREAM",
      "name": "Blue Dream Indoor",
      "imageUrl": "https://storage.cannahub.tech/...",
      "totalInventoryGrams": 60,
      "totalInventoryUnits": 28,
      "variants": [
        {
          "id": "variant_01HQ_BD_1G",
          "name": "1g",
          "quantity": 20,
          "available": 18
        }
      ],
      "isLowStock": false,
      "isOutOfStock": false
    }
  ],
  "count": 45,
  "limit": 20,
  "offset": 0,
  "summary": {
    "totalProducts": 45,
    "lowStockCount": 5,
    "outOfStockCount": 2,
    "totalInventoryGrams": 15000
  }
}

PATCH/api/products/:productId/variants/:variantId

Update a product variant

This endpoint allows you to update an existing product variant in your application.

Optional attributes

Request

PUT
/api/products/:productId/variants/:variantId
curl -X PUT https://yourapp.com/api/products/:productId/variants/:variantId \
  -H "Authorization: Bearer {token}" \
  -d '{
    "name": "Updated Variant",
    "inventoryQuantity": 100,
    "weight": 10
  }'

Response

{
  "id": "variantId",
  "name": "Updated Variant",
  "inventoryQuantity": 100,
  "weight": 10,
  "price": 100,
  "discountPrice": 80,
  "currencyCode": "USD",
  "priceListId": "priceListId",
  "priceListName": "Price List",
  "prices": [
    {
      "id": "priceId",
      "price": 100,
      "priceList": {
        "id": "priceListId",
        "name": "Price List"
      },
      "currency": "USD",
      "createdAt": "2023-05-13T12:00:00Z"
    },
    {
      "id": "priceId",
      "price": 80,
      "priceList": {
        "id": "priceListId",
        "name": "Price List"
      },
      "currency": "USD",
      "createdAt": "2023-05-14T12:00:00Z"
    }
  ]
}