Overview & Creating a Product | Moloni ON API
Moloni ONGuidesAPI ReferenceExplorer
Guides

Overview & Creating a Product

Products are central to most API operations; they appear as line items on invoices, invoice-receipts, credit notes and other documents. This guide covers the product model and walks through creating products with various configurations.

Product types

Every product has a type that determines its behavior:

TypeValueDescription
Product1A physical or tangible item. Can track stock.
Service2An intangible service. Cannot track stock.
Other3Any other type. Cannot track stock.

Required fields

The ProductInsert input requires:

FieldTypeDescription
nameString!Product name
referenceString!Any sort of identifier you want to use (unique per company)
typeInt!1 = Product, 2 = Service, 3 = Other
productCategoryIdInt!Category the product belongs to
measurementUnitIdInt!Unit of measure (e.g. units, kg, hours)

Fetching prerequisites

Before creating a product, you need a category and a measurement unit.

Product categories

query {
  productCategories(companyId: 1, options: {
    search: { field: ALL, value: "Furniture" }
    pagination: { page: 1, qty: 10 }
  }) {
    data {
      productCategoryId
      name
    }
  }
}

Measurement units

query {
  measurementUnits(companyId: 1, options: {
    pagination: { page: 1, qty: 50 }
  }) {
    data {
      measurementUnitId
      name
      abbreviation
    }
  }
}

Creating a simple product

A minimal product with just the required fields:

mutation {
  productCreate(
    companyId: 1
    data: {
      name: "Office Chair"
      reference: "CHAIR-001"
      type: 1
      productCategoryId: 5
      measurementUnitId: 1
      price: 149.99
    }
  ) {
    errors { field msg }
    data {
      productId
      name
      reference
      price
    }
  }
}

Taxes

Products can have one or more taxes applied to them. Each tax has an ordering that determines the calculation sequence, and a cumulative flag that controls whether the tax is applied on top of the previously calculated tax.

Fetching available taxes

query {
  taxes(companyId: 1, options: {
    pagination: { page: 1, qty: 50 }
  }) {
    data {
      taxId
      name
      value
      type
      fiscalZone
    }
  }
}

Creating with taxes

mutation {
  productCreate(
    companyId: 1
    data: {
      name: "Office Chair"
      reference: "CHAIR-001"
      type: 1
      productCategoryId: 5
      measurementUnitId: 1
      price: 149.99
      taxes: [
        {
          taxId: 1
          value: 23
          ordering: 1
          cumulative: false
        }
      ]
    }
  ) {
    errors { field msg }
    data {
      productId
      taxes {
        taxId
        value
        ordering
        cumulative
      }
    }
  }
}

The ordering field determines the sequence in which taxes are applied. When cumulative is true, the tax is calculated on the price plus previously applied taxes rather than on the base price alone.

Stock management

Products of type: 1 can track stock. Enable it with hasStock: true.

Default warehouse

Set warehouseId to assign a default warehouse, and provide an initial stock level via warehouses:

mutation {
  productCreate(
    companyId: 1
    data: {
      name: "Office Chair"
      reference: "CHAIR-001"
      type: 1
      productCategoryId: 5
      measurementUnitId: 1
      price: 149.99
      hasStock: true
      warehouseId: 1
      warehouses: [
        {
          warehouseId: 1
          stock: 50
          minStock: 5
        }
      ]
    }
  ) {
    errors { field msg }
    data {
      productId
      hasStock
      stock
      warehouses {
        warehouseId
        stock
        minStock
      }
    }
  }
}

Multi-warehouse stock

A product can have stock in multiple warehouses:

warehouses: [
  { warehouseId: 1, stock: 50, minStock: 5 },
  { warehouseId: 2, stock: 30, minStock: 3 }
]

Price classes

Price classes let you define different prices for different customer groups (e.g. wholesale vs retail). Each entry maps a priceClassId to a specific price.

mutation {
  productCreate(
    companyId: 1
    data: {
      name: "Office Chair"
      reference: "CHAIR-001"
      type: 1
      productCategoryId: 5
      measurementUnitId: 1
      price: 149.99
      priceClasses: [
        { priceClassId: 1, value: 129.99 },
        { priceClassId: 2, value: 109.99 }
      ]
    }
  ) {
    errors { field msg }
    data {
      productId
      price
      priceClasses {
        priceClassId
        value
      }
    }
  }
}

Suppliers

Associate one or more suppliers with cost prices:

suppliers: [
  {
    supplierId: 10
    costPrice: 75.00
    reference: "SUP-CHAIR-001"
  }
]
FieldTypeDescription
supplierIdInt!The supplier ID
costPriceFloat!Purchase cost per unit
referenceStringThe supplier's identifier

Identifications (barcodes)

Products can have barcodes or other identifiers attached:

identifications: [
  { type: 2, text: "5601234567890", favorite: true }
]

The type field maps to a barcode format:

TypeFormat
1EAN8
2EAN13
3JAN13
4ISSN
5ISBN
6UPCA
7UPCE
8CODE39
9CODE93
10DATAMATRIX
11PDF417
12QRCODE

The favorite flag marks the primary barcode shown at a glance.

Optional fields

Beyond the examples above, ProductInsert supports:

FieldTypeDescription
summaryStringShort description
notesStringInternal notes
notesOnExportBooleanInclude notes when exporting documents
visibleIntVisibility flag (default: 1)
exemptionReasonStringTax exemption reason
imgUploadProduct image
customFields[ProductCustomField!]Dynamic custom field values

See the full ProductInsert reference for all options.

Querying products

Single product

query {
  product(companyId: 1, productId: 100) {
    data {
      productId
      name
      reference
      price
      type
      hasStock
      stock
      productCategory { name }
      measurementUnit { name abbreviation }
      taxes {
        taxId
        value
        ordering
      }
      warehouses {
        warehouseId
        stock
        minStock
      }
    }
  }
}

Listing products

query {
  products(companyId: 1, options: {
    search: { field: ALL, value: "Chair" }
    order: { field: NAME, sort: ASC }
    pagination: { page: 1, qty: 20 }
  }) {
    data {
      productId
      name
      reference
      price
      stock
    }
    options {
      pagination {
        page
        qty
        count
      }
    }
  }
}

See the pagination and filtering guides for more on list queries.

Updating a product

Use productUpdate to modify an existing product. Only the productId is required; include only the fields you want to change:

mutation {
  productUpdate(
    companyId: 1
    data: {
      productId: 100
      price: 159.99
      notes: "Price increased for 2025"
    }
  ) {
    errors { field msg }
    data {
      productId
      price
      notes
    }
  }
}

Next steps