Inventario

La API de inventario permite gestionar el stock de productos en múltiples ubicaciones. Cannahub utiliza el sistema de inventario multi-ubicación de Medusa para trackear stock por club y calcular totales en gramos para productos de flor.


El modelo de Inventario

ProductLocationStock

Representa el stock de una variante en una ubicación específica.

  • Name
    productId
    Type
    string
    Description

    ID del producto padre.

  • Name
    variantId
    Type
    string
    Description

    ID de la variante específica.

  • Name
    inventoryItemId
    Type
    string
    Description

    ID del item de inventario en Medusa.

  • Name
    locationId
    Type
    string
    Description

    ID de la ubicación de stock.

  • Name
    locationName
    Type
    string
    Description

    Nombre legible de la ubicación.

  • Name
    quantity
    Type
    number
    Description

    Unidades físicas en stock.

  • Name
    reservedQuantity
    Type
    number
    Description

    Unidades reservadas por pedidos pendientes.

  • Name
    availableQuantity
    Type
    number
    Description

    Unidades disponibles para venta (quantity - reserved).

  • Name
    incomingQuantity
    Type
    number
    Description

    Unidades en tránsito/esperadas.

Interface ProductLocationStock

interface ProductLocationStock {
  productId: string
  variantId: string
  inventoryItemId: string
  locationId: string
  locationName: string
  quantity: number
  reservedQuantity: number
  availableQuantity: number
  incomingQuantity: number
}

Ejemplo

{
  "productId": "prod_01HQ8BD5G",
  "variantId": "variant_01HQ_BD_7G",
  "inventoryItemId": "iitem_01HQ_BD_7G",
  "locationId": "sloc_high_up",
  "locationName": "High Up - Dispensario",
  "quantity": 50,
  "reservedQuantity": 5,
  "availableQuantity": 45,
  "incomingQuantity": 0
}

InventoryLevel

Modelo raw de Medusa para niveles de inventario.

  • Name
    id
    Type
    string
    Description

    ID único del nivel de inventario.

  • Name
    inventoryItemId
    Type
    string
    Description

    ID del item de inventario asociado.

  • Name
    locationId
    Type
    string
    Description

    ID de la ubicación.

  • Name
    stockedQuantity
    Type
    number
    Description

    Cantidad total en stock.

  • Name
    reservedQuantity
    Type
    number
    Description

    Cantidad reservada.

  • Name
    incomingQuantity
    Type
    number
    Description

    Cantidad entrante.

Interface InventoryLevel

interface InventoryLevel {
  id: string
  inventoryItemId: string
  locationId: string
  stockedQuantity: number
  reservedQuantity: number
  incomingQuantity: number
  availableQuantity: number
  location?: StockLocation
  metadata?: Record<string, unknown>
  createdAt?: string
  updatedAt?: string
}

Multiplicador de Inventario

Para productos de flor, el inventario se calcula multiplicando las unidades por el peso de la variante.

Lógica del Multiplicador

inventoryTotal = quantity × weight

Ejemplo

VarianteUnidadesPesoTotal (gramos)
Blue Dream 1g1001g100g
Blue Dream 3.5g503.5g175g
Blue Dream 7g207g140g
Total170-415g

Cálculo de inventario

function calculateInventoryTotal(
  variant: ProductVariant,
  inventoryQuantity: number
): number {
  // Obtener peso de metadata
  const weight = variant.metadata?.weight
    ?? variant.metadata?.quantity
    ?? 1

  // Multiplicar para obtener total en gramos
  return inventoryQuantity * weight
}

// Uso
const variant = {
  id: 'variant_bd_7g',
  name: 'Blue Dream 7g',
  metadata: { weight: 7 }
}

const total = calculateInventoryTotal(variant, 20)
// total = 140 (gramos)

GET/api/inventory

Listar inventario

Este endpoint lista los items de inventario con sus niveles por ubicación.

Parámetros de Consulta

  • Name
    location_id
    Type
    string
    Description

    Filtrar por ubicación de stock.

  • Name
    search
    Type
    string
    Description

    Filtrar por SKU o titulo.

  • Name
    limit
    Type
    number
    Description

    Límite de resultados (default: 20).

  • Name
    offset
    Type
    number
    Description

    Offset para paginación.

Request

GET
/api/inventory
curl -G https://app.cannahub.tech/api/inventory \
  -H "Authorization: Bearer {token}" \
  -d location_id=sloc_high_up

Response

{
  "items": [
    {
      "id": "iitem_01HQ_BD_7G",
      "sku": "BD-7G-001",
      "title": "Blue Dream 7g",
      "stockedQuantity": 50,
      "reservedQuantity": 5,
      "availableQuantity": 45,
      "locationLevels": [
        {
          "id": "ilevel_01HQ",
          "locationId": "sloc_high_up",
          "stockedQuantity": 50,
          "reservedQuantity": 5,
          "incomingQuantity": 0,
          "availableQuantity": 45
        }
      ]
    }
  ],
  "count": 45,
  "offset": 0,
  "limit": 20
}

GET/api/inventory/:id

Obtener inventario de variante

Obtiene el detalle de un item de inventario específico con todos sus niveles.

Request

GET
/api/inventory/:id
curl https://yourapp.com/api/inventory/iitem_01HQ_BD_7G \
  -H "Authorization: Bearer {admin_token}"

Response

{
  "item": {
    "id": "iitem_01HQ_BD_7G",
    "sku": "BD-7G-001",
    "title": "Blue Dream 7g",
    "requiresShipping": true,
    "stockedQuantity": 250,
    "reservedQuantity": 5,
    "availableQuantity": 245,
    "locationLevels": [
      {
        "id": "ilevel_01HQ",
        "locationId": "sloc_high_up",
        "stockedQuantity": 50,
        "reservedQuantity": 5,
        "incomingQuantity": 0,
        "availableQuantity": 45
      },
      {
        "id": "ilevel_02HQ",
        "locationId": "sloc_deposito",
        "stockedQuantity": 200,
        "reservedQuantity": 0,
        "incomingQuantity": 50,
        "availableQuantity": 200
      }
    ]
  }
}

POST/api/inventory/:id/levels/:locationId

Actualizar nivel de inventario

Actualiza el nivel de stock en una ubicación específica.

Body

  • Name
    stockedQuantity
    Type
    number
    Description

    Nueva cantidad en stock.

  • Name
    incomingQuantity
    Type
    number
    Description

    Cantidad entrante esperada.

Request

PATCH
/api/inventory/levels
curl -X PATCH https://app.cannahub.tech/api/inventory/levels \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "inventoryItemId": "iitem_01HQ_BD_7G",
    "locationId": "sloc_high_up",
    "stockedQuantity": 60
  }'

Response

{
  "level": {
    "id": "ilevel_01HQ",
    "locationId": "sloc_high_up",
    "stockedQuantity": 60,
    "reservedQuantity": 5,
    "incomingQuantity": 0,
    "availableQuantity": 55
  }
}

POST/api/inventory/adjust

Ajuste de inventario (incremento/decremento)

Para ajustes de inventario por ventas, devoluciones, o correcciones.

Reservaciones

Las reservaciones son creadas automáticamente cuando se agrega un item al carrito y se liberan si el carrito expira o se cancela.

Flujo de reservación

// 1. Cliente agrega item al carrito
// → Se crea reservación automática
// reserved_quantity += ordered_quantity

// 2. Cliente completa la orden
// → Se confirma la reservación
// stocked_quantity -= ordered_quantity
// reserved_quantity -= ordered_quantity

// 3. Si el carrito expira/cancela
// → Se libera la reservación
// reserved_quantity -= ordered_quantity

Stock Locations (Filiales)

Las ubicaciones de stock (filiales) son fundamentales para el inventario multi-ubicacion. Para documentacion completa sobre gestion de filiales, consulta la API de Filiales.


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

BFF Route: Producto con Inventario

Endpoint BFF que retorna un producto con inventario agregado de todas las ubicaciones en una sola llamada.

Beneficio

Reduce el problema N+1 de consultar inventario por cada variante.

Request

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

Response

{
  "product": {
    "id": "prod_01HQ8BD5G",
    "name": "Blue Dream Indoor",
    "variants": [
      {
        "id": "variant_bd_1g",
        "name": "Blue Dream 1g",
        "weight": 1,
        "price": 350
      },
      {
        "id": "variant_bd_7g",
        "name": "Blue Dream 7g",
        "weight": 7,
        "price": 2100
      }
    ]
  },
  "inventory": {
    "variant_bd_1g": {
      "quantity": 100,
      "available": 95,
      "reserved": 5,
      "totalGrams": 100,
      "byLocation": [
        {
          "locationId": "sloc_high_up",
          "locationName": "High Up",
          "quantity": 100,
          "available": 95
        }
      ]
    },
    "variant_bd_7g": {
      "quantity": 50,
      "available": 45,
      "reserved": 5,
      "totalGrams": 350,
      "byLocation": [
        {
          "locationId": "sloc_high_up",
          "locationName": "High Up",
          "quantity": 50,
          "available": 45
        }
      ]
    }
  },
  "totalInventoryGrams": 450
}

POST/api/inventory/batch-update

BFF Route: Actualización Batch

Permite actualizar inventario de múltiples variantes en una sola llamada. Esta ruta reduce N llamadas individuales a 1 sola llamada.

Problema que resuelve

Flujo actual (N calls):
Para 10 variantes:
1. POST /admin/inventory-items/:id1/location-levels/:loc
2. POST /admin/inventory-items/:id2/location-levels/:loc
3. POST /admin/inventory-items/:id3/location-levels/:loc
...10 llamadas secuenciales

Body

  • Name
    updates
    Type
    array
    Description

    Array de actualizaciones a aplicar.

    • Name
      variantId
      Type
      string
      Description

      ID de la variante a actualizar.

    • Name
      locationId
      Type
      string
      Description

      ID de la ubicación de stock.

    • Name
      quantity
      Type
      number
      Description

      Nueva cantidad en stock.

    • Name
      adjustment
      Type
      number
      Description

      Ajuste relativo (+/-) en lugar de cantidad absoluta.

    • Name
      reason
      Type
      string
      Description

      Razón del ajuste para auditoría.

  • Name
    validateStock
    Type
    boolean
    Description

    Si es true, valida que haya stock suficiente para decrementos.

Response

  • Name
    success
    Type
    boolean
    Description

    Si todas las actualizaciones fueron exitosas.

  • Name
    updated
    Type
    number
    Description

    Número de items actualizados exitosamente.

  • Name
    failed
    Type
    number
    Description

    Número de items que fallaron.

  • Name
    results
    Type
    array
    Description

    Detalle de cada actualización.

Request

POST
/api/inventory/batch-update
curl -X POST https://app.cannahub.tech/api/inventory/batch-update \
  -H "Authorization: Bearer {admin_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "updates": [
      {
        "variantId": "variant_bd_1g",
        "locationId": "sloc_high_up",
        "quantity": 150
      },
      {
        "variantId": "variant_bd_7g",
        "locationId": "sloc_high_up",
        "quantity": 75
      }
    ]
  }'

Response

{
  "success": true,
  "updated": 2,
  "failed": 0,
  "results": [
    {
      "variantId": "variant_bd_1g",
      "locationId": "sloc_high_up",
      "previousQuantity": 100,
      "newQuantity": 150,
      "totalGrams": 150,
      "status": "success"
    },
    {
      "variantId": "variant_bd_7g",
      "locationId": "sloc_high_up",
      "previousQuantity": 50,
      "newQuantity": 75,
      "totalGrams": 525,
      "status": "success"
    }
  ],
  "summary": {
    "totalUnitsUpdated": 75,
    "totalGramsUpdated": 525
  }
}

Response con error parcial

{
  "success": false,
  "updated": 1,
  "failed": 1,
  "results": [
    {
      "variantId": "variant_bd_1g",
      "status": "success",
      "previousQuantity": 100,
      "newQuantity": 150
    },
    {
      "variantId": "variant_invalid",
      "status": "error",
      "error": "Variant not found"
    }
  ]
}

Comparación de rendimiento

MétricaSin BFF (10 items)Con BFFMejora
Llamadas API101-90%
Latencia típica~2000ms~300ms-85%
Manejo de erroresManual por itemCentralizadoSimplificado
AuditoríaN registros separados1 batch logTrazabilidad

POST/api/stock-locations/setup

BFF Route: Stock Location Setup

Esta ruta crea una ubicación de stock completa con todo su fulfillment chain en una sola llamada. Normalmente esto requiere 7+ llamadas secuenciales a Medusa.

Problema que resuelve

Flujo actual (7+ calls secuenciales):
1. POST /admin/stock-locations           → Crear ubicación
2. POST /admin/fulfillment-sets          → Crear fulfillment set
3. POST /admin/service-zones             → Crear service zone
4. POST /admin/geo-zones                 → Crear geo zone
5. POST /admin/shipping-options          → Crear opción de envío
6. POST /admin/shipping-options          → Crear opción de pickup
7. POST /admin/stock-locations/:id/sales-channels
                                         → Asociar sales channel

Request Body

  • Name
    name
    Type
    string
    Description

    Nombre de la ubicación.

  • Name
    address
    Type
    object
    Description

    Dirección física de la ubicación.

    • Name
      address_1
      Type
      string
      Description

      Dirección principal.

    • Name
      city
      Type
      string
      Description

      Ciudad.

    • Name
      province
      Type
      string
      Description

      Provincia/Estado.

    • Name
      postal_code
      Type
      string
      Description

      Código postal.

    • Name
      country_code
      Type
      string
      Description

      Código de país (ej: "AR").

  • Name
    enableShipping
    Type
    boolean
    Description

    Habilitar envíos desde esta ubicación (default: false).

  • Name
    enablePickup
    Type
    boolean
    Description

    Habilitar retiro en esta ubicación (default: true).

  • Name
    salesChannelId
    Type
    string
    Description

    ID del canal de ventas a asociar.

  • Name
    metadata
    Type
    object
    Description

    Datos adicionales (club, tipo, horarios, etc).

Response

  • Name
    stockLocation
    Type
    StockLocation
    Description

    La ubicación creada.

  • Name
    fulfillmentSet
    Type
    FulfillmentSet
    Description

    El fulfillment set asociado.

  • Name
    serviceZone
    Type
    ServiceZone
    Description

    La service zone creada.

  • Name
    shippingOptions
    Type
    array
    Description

    Las opciones de envío configuradas.

Request

POST
/api/stock-locations/setup
curl -X POST https://app.cannahub.tech/api/stock-locations/setup \
  -H "Authorization: Bearer {admin_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "High Up - Dispensario Palermo",
    "address": {
      "address_1": "Av. Santa Fe 3200",
      "city": "Buenos Aires",
      "province": "CABA",
      "postal_code": "C1425",
      "country_code": "AR"
    },
    "enableShipping": false,
    "enablePickup": true,
    "salesChannelId": "sc_01KBCZXJCPVWX8REW8880R45SW",
    "metadata": {
      "club": "high-up",
      "type": "dispensary",
      "schedule": {
        "monday": "10:00-20:00",
        "tuesday": "10:00-20:00",
        "wednesday": "10:00-20:00",
        "thursday": "10:00-20:00",
        "friday": "10:00-20:00",
        "saturday": "12:00-18:00",
        "sunday": "closed"
      }
    }
  }'

Response

{
  "stockLocation": {
    "id": "sloc_01KDPALERMO",
    "name": "High Up - Dispensario Palermo",
    "address": {
      "address_1": "Av. Santa Fe 3200",
      "city": "Buenos Aires",
      "province": "CABA",
      "postal_code": "C1425",
      "country_code": "AR"
    },
    "metadata": {
      "club": "high-up",
      "type": "dispensary"
    }
  },
  "fulfillmentSet": {
    "id": "fset_01KDPALERMO",
    "name": "High Up - Dispensario Palermo Fulfillment",
    "type": "pickup"
  },
  "serviceZone": {
    "id": "szone_01KDPALERMO",
    "name": "High Up Palermo Zone",
    "geo_zones": [
      {
        "id": "gzone_01KDPALERMO",
        "type": "country",
        "country_code": "AR"
      }
    ]
  },
  "shippingOptions": [
    {
      "id": "so_01KDPALERMO_PICKUP",
      "name": "Retiro en Dispensario Palermo",
      "price_type": "flat",
      "amount": 0,
      "is_return": false,
      "metadata": {
        "type": "pickup",
        "locationId": "sloc_01KDPALERMO"
      }
    }
  ],
  "summary": {
    "totalApiCalls": 7,
    "savedCalls": 6,
    "executionTimeMs": 450
  }
}

Flujo interno del BFF

Loading diagram...

Comparación de rendimiento

MétricaSin BFFCon BFFMejora
Llamadas API7+1-86%
Latencia típica~1400ms~450ms-68%
Complejidad clienteAlta (secuencial)Baja (1 request)Simplificado
Rollback en errorManualAutomáticoTransaccional

Mapeo Medusa

La API BFF transforma las respuestas de Medusa de snake_case a camelCase:

UI (camelCase)Medusa (snake_case)
inventoryItemIdinventory_item_id
locationIdlocation_id
stockedQuantitystocked_quantity
reservedQuantityreserved_quantity
incomingQuantityincoming_quantity
availableQuantityavailable_quantity
requiresShippingrequires_shipping
originCountryorigin_country
hsCodehs_code
midCodemid_code
createdAtcreated_at
updatedAtupdated_at
locationLevelslocation_levels

Diagrama de Flujo de Inventario

Loading diagram...

Origen de Cosecha (Seed-to-Sale)

Al editar stock, los modales permiten seleccionar una cosecha como origen para trazabilidad. Esto habilita el flujo completo de seed-to-sale.

Funcionalidad

  • Selector de Cosecha: Combobox que filtra cosechas por la genética del producto
  • Solo STOCKED: Solo muestra cosechas con estado STOCKED (listas para dispensar)
  • Peso Disponible: Muestra los gramos disponibles de cada cosecha
  • Warning de Exceso: Alerta si el stock a agregar excede el peso disponible de la cosecha

Modales Actualizados

  1. EditProductStockModal - Stock por ubicación
  2. EditStockDialog - Stock multi-ubicación con acordeón
  3. EditStockModal - Nivel de inventario individual

HarvestsCombobox Props

type Props = {
  value: string | undefined
  setValue: (value: string | undefined) => void
  strainId?: string  // Filtra por genética
  setHarvest?: (harvest: Harvest | undefined) => void
  disabled?: boolean
  placeholder?: string
}

StockUpdateWithSource Type

// src/api/custom/stock.ts
type StockUpdateWithSource = {
  inventoryItemId: string
  locationId: string
  quantity: number
  sourceHarvestId?: string // Trazabilidad seed-to-sale
}

Uso en Modal

// Estado para selección de cosecha
const [selectedHarvestId, setSelectedHarvestId] =
  useState<string | undefined>()
const [selectedHarvest, setSelectedHarvest] =
  useState<Harvest | undefined>()

// Warning cuando stock > disponible
const harvestWarning = useMemo(() => {
  if (!selectedHarvest) return null

  const available = selectedHarvest.trimmedWeight || 0
  const stockChange = newQty - originalQty

  if (stockChange > available) {
    return { change: stockChange, available }
  }
  return null
}, [selectedHarvest, formValues])

Flujo de Trazabilidad

Loading diagram...

Traducciones Agregadas

ClaveESENDE
sourceHarvestCosecha origenSource HarvestQuell-Ernte
selectHarvestSeleccionar cosecha a descontarSelect harvest to discount fromErnte zum Abzug auswählen
availableFromHarvestDisponible de cosechaAvailable from harvestVerfügbar von Ernte
harvestExceededWarningLa cantidad supera el peso disponibleStock quantity exceeds harvest availableBestandsmenge übersteigt verfügbares Erntegewicht

GET/api/inventory/levels

Niveles de inventario

Obtiene los niveles de inventario para un item específico en todas las ubicaciones.

Parámetros de Consulta

  • Name
    inventory_item_id
    Type
    string
    Description

    ID del item de inventario del cual obtener niveles.

Request

GET
/api/inventory/levels
curl -G https://app.cannahub.tech/api/inventory/levels \
  -H "Authorization: Bearer {token}" \
  -d inventory_item_id=iitem_01HQ_BD_7G

Response

{
  "levels": [
    {
      "id": "ilevel_01HQ",
      "inventoryItemId": "iitem_01HQ_BD_7G",
      "locationId": "sloc_high_up",
      "stockedQuantity": 50,
      "reservedQuantity": 5,
      "incomingQuantity": 0,
      "availableQuantity": 45
    },
    {
      "id": "ilevel_02HQ",
      "inventoryItemId": "iitem_01HQ_BD_7G",
      "locationId": "sloc_deposito",
      "stockedQuantity": 200,
      "reservedQuantity": 0,
      "incomingQuantity": 50,
      "availableQuantity": 200
    }
  ]
}

POST/api/inventory/levels

Crear nivel de inventario

Crea un nuevo nivel de inventario para un item en una ubicación específica.

Body

  • Name
    inventory_item_id
    Type
    string
    Description

    ID del item de inventario.

  • Name
    location_id
    Type
    string
    Description

    ID de la ubicación de stock.

  • Name
    stocked_quantity
    Type
    number
    Description

    Cantidad inicial en stock (default: 0).

Request

POST
/api/inventory/levels
curl -X POST https://app.cannahub.tech/api/inventory/levels \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "inventory_item_id": "iitem_01HQ_BD_7G",
    "location_id": "sloc_high_up",
    "stocked_quantity": 100
  }'

Response

{
  "level": {
    "id": "ilevel_03HQ",
    "inventoryItemId": "iitem_01HQ_BD_7G",
    "locationId": "sloc_high_up",
    "stockedQuantity": 100,
    "reservedQuantity": 0,
    "incomingQuantity": 0,
    "availableQuantity": 100
  }
}

PATCH/api/inventory/levels

Actualizar nivel de inventario

Actualiza un nivel de inventario existente.

Body

  • Name
    inventory_item_id
    Type
    string
    Description

    ID del item de inventario.

  • Name
    location_id
    Type
    string
    Description

    ID de la ubicación de stock.

  • Name
    stocked_quantity
    Type
    number
    Description

    Nueva cantidad en stock.

  • Name
    incoming_quantity
    Type
    number
    Description

    Nueva cantidad entrante esperada.

Request

PATCH
/api/inventory/levels
curl -X PATCH https://app.cannahub.tech/api/inventory/levels \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "inventory_item_id": "iitem_01HQ_BD_7G",
    "location_id": "sloc_high_up",
    "stocked_quantity": 60,
    "incoming_quantity": 0
  }'

Response

{
  "level": {
    "id": "ilevel_01HQ",
    "inventoryItemId": "iitem_01HQ_BD_7G",
    "locationId": "sloc_high_up",
    "stockedQuantity": 60,
    "reservedQuantity": 5,
    "incomingQuantity": 0,
    "availableQuantity": 55
  }
}

DELETE/api/inventory/levels

Eliminar nivel de inventario

Elimina un nivel de inventario de una ubicación. Esto desvincula el item de inventario de dicha ubicación.

Parámetros de Consulta

  • Name
    inventory_item_id
    Type
    string
    Description

    ID del item de inventario.

  • Name
    location_id
    Type
    string
    Description

    ID de la ubicación de stock a desvincular.

Request

DELETE
/api/inventory/levels
curl -X DELETE "https://app.cannahub.tech/api/inventory/levels?inventory_item_id=iitem_01HQ_BD_7G&location_id=sloc_high_up" \
  -H "Authorization: Bearer {token}"

Response

{
  "success": true,
  "deleted": {
    "inventoryItemId": "iitem_01HQ_BD_7G",
    "locationId": "sloc_high_up"
  }
}

GET/api/inventory/locations

Listar ubicaciones de stock

Lista todas las ubicaciones de stock (filiales) disponibles.

Parámetros de Consulta

  • Name
    include_sales_channels
    Type
    boolean
    Description

    Si es true, incluye los canales de venta asociados a cada ubicación.

Request

GET
/api/inventory/locations
curl -G https://app.cannahub.tech/api/inventory/locations \
  -H "Authorization: Bearer {token}" \
  -d include_sales_channels=true

Response

{
  "stock_locations": [
    {
      "id": "sloc_high_up",
      "name": "High Up - Dispensario",
      "address": {
        "address_1": "Av. Santa Fe 3200",
        "city": "Buenos Aires",
        "province": "CABA",
        "country_code": "AR"
      },
      "metadata": {
        "club": "high-up",
        "type": "dispensary"
      },
      "sales_channels": [
        {
          "id": "sc_01KBCZXJCPVWX8REW8880R45SW",
          "name": "High Up Store"
        }
      ]
    },
    {
      "id": "sloc_deposito",
      "name": "Depósito Central",
      "address": {
        "address_1": "Calle Industria 500",
        "city": "Buenos Aires",
        "province": "CABA",
        "country_code": "AR"
      },
      "metadata": {
        "type": "warehouse"
      },
      "sales_channels": []
    }
  ]
}

POST/api/inventory/locations

Crear ubicación de stock

Crea una nueva ubicación de stock (filial).

Body

  • Name
    name
    Type
    string
    Description

    Nombre de la ubicación.

  • Name
    address
    Type
    object
    Description

    Dirección física de la ubicación.

    • Name
      address_1
      Type
      string
      Description

      Dirección principal.

    • Name
      city
      Type
      string
      Description

      Ciudad.

    • Name
      province
      Type
      string
      Description

      Provincia/Estado.

    • Name
      postal_code
      Type
      string
      Description

      Código postal.

    • Name
      country_code
      Type
      string
      Description

      Código de país (ej: "AR").

  • Name
    metadata
    Type
    object
    Description

    Datos adicionales (club, tipo, horarios, etc).

Request

POST
/api/inventory/locations
curl -X POST https://app.cannahub.tech/api/inventory/locations \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Nuevo Dispensario Belgrano",
    "address": {
      "address_1": "Av. Cabildo 1500",
      "city": "Buenos Aires",
      "province": "CABA",
      "postal_code": "C1426",
      "country_code": "AR"
    },
    "metadata": {
      "club": "high-up",
      "type": "dispensary"
    }
  }'

Response

{
  "stock_location": {
    "id": "sloc_01KDBELGRANO",
    "name": "Nuevo Dispensario Belgrano",
    "address": {
      "address_1": "Av. Cabildo 1500",
      "city": "Buenos Aires",
      "province": "CABA",
      "postal_code": "C1426",
      "country_code": "AR"
    },
    "metadata": {
      "club": "high-up",
      "type": "dispensary"
    },
    "created_at": "2026-05-09T10:00:00.000Z",
    "updated_at": "2026-05-09T10:00:00.000Z"
  }
}

PATCH/api/inventory/stock

Actualizar stock

Actualiza el stock de un item en una ubicación específica. Si no existe un nivel de inventario para esa combinación, lo crea automáticamente.

Body

  • Name
    inventory_item_id
    Type
    string
    Description

    ID del item de inventario.

  • Name
    location_id
    Type
    string
    Description

    ID de la ubicación de stock.

  • Name
    stocked_quantity
    Type
    number
    Description

    Nueva cantidad total en stock.

Request

PATCH
/api/inventory/stock
curl -X PATCH https://app.cannahub.tech/api/inventory/stock \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "inventory_item_id": "iitem_01HQ_BD_7G",
    "location_id": "sloc_high_up",
    "stocked_quantity": 75
  }'

Response

{
  "level": {
    "id": "ilevel_01HQ",
    "inventoryItemId": "iitem_01HQ_BD_7G",
    "locationId": "sloc_high_up",
    "stockedQuantity": 75,
    "reservedQuantity": 5,
    "incomingQuantity": 0,
    "availableQuantity": 70
  },
  "created": false
}

Response (nivel creado)

{
  "level": {
    "id": "ilevel_04HQ",
    "inventoryItemId": "iitem_01HQ_BD_7G",
    "locationId": "sloc_nuevo",
    "stockedQuantity": 75,
    "reservedQuantity": 0,
    "incomingQuantity": 0,
    "availableQuantity": 75
  },
  "created": true
}

POST/api/inventory/enable-for-variant

Habilitar inventario para variante

Habilita la gestión de inventario para una variante de producto. Este endpoint ejecuta un proceso de tres pasos:

  1. Actualiza el flag manage_inventory de la variante a true
  2. Crea un inventory item asociado a la variante
  3. Vincula el inventory item con la variante

Body

  • Name
    variant_id
    Type
    string
    Description

    ID de la variante de producto para la cual habilitar inventario.

Request

POST
/api/inventory/enable-for-variant
curl -X POST https://app.cannahub.tech/api/inventory/enable-for-variant \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "variant_id": "variant_bd_7g"
  }'

Response

{
  "success": true,
  "variant_id": "variant_bd_7g",
  "inventory_item": {
    "id": "iitem_01NEW_BD_7G",
    "sku": null,
    "requires_shipping": true,
    "created_at": "2026-05-09T10:00:00.000Z"
  },
  "steps": {
    "variant_updated": true,
    "inventory_item_created": true,
    "inventory_item_linked": true
  }
}

Próximos Pasos

Was this page helpful?