NutraSoft food manufacturing ERP software
Esc
  • GuideGetting startedThree steps to your first call and your first order, then the two shapes every response takes.
  • GuideAuthentication and keysBearer keys, test and live environments, rotation without an outage, and what each scope grants.
  • GuideTest modeA second NutraSoft account of your own, seeded with realistic data, that you can safely break.
  • PageErrorsEvery error code the API returns, what it means, what to do about it and whether to retry.
  • PageConventionsThe rules every operation follows: money, time, paging, sorting, nulls, PATCH and identifiers.
Menu

Products

One resource, one id space, two families of product with different fields. The largest comprehension trap in the API.
Updated

In short

  • Every product has kind: material for raw materials, packaging and miscellaneous, recipe for recipes, sub-recipes and finished goods.
  • On a single product exactly one of material and recipe is populated; the other is null.
  • The list leaves allergens, tag_ids, material and recipe null. Fetch one product to read them.
  • Products are read-only in v1, and allergens holds only what was declared, never what a recipe's components carry.

Products are one resource with one id space, but they hold two different kinds of thing that carry different fields. Getting this wrong produces bugs in your system rather than errors from ours.

The two families

product_type has six values that fall into two families. Every product also carries kind, either material or recipe, so you never have to hold that mapping yourself. The raw product_type is returned too, because the values mean different things to you even inside one family.

Familykindproduct_type values
MaterialmaterialRAW_MATERIAL, PACKAGING, MISCELLANEOUS
ReciperecipeRECIPE, SUB_RECIPE, FINISHED_GOOD

GET /products/{product_id} returns two nullable objects, material and recipe. Exactly one is populated, decided by kind; the other is null. The table below is generated from the published schemas.

Fields each product family carries
FieldMaterialRecipe
product_typeRAW_MATERIAL, PACKAGING, MISCELLANEOUSRECIPE, SUB_RECIPE, FINISHED_GOOD
kindmaterialrecipe
material.banned_country_idsYesNo, material is null
material.food_idThe nutrition source. Fetch with GET /foods/{food_id}.YesNo, material is null
material.substitute_product_idsYesNo, material is null
material.supplier_idsYesNo, material is null
recipe.byproduct_product_idsNo, recipe is nullYes
recipe.ingredients_enThe generated ingredient declaration.No, recipe is nullYes
recipe.ingredients_frNo, recipe is nullYes
recipe.portion_in_gramsNo, recipe is nullYes
recipe.qc_checkpoint_idsNo, recipe is nullYes
recipe.recipe_grams_yieldNo, recipe is nullYes
Allergensallergens[].allergen_idYesYes
Tagstag_idsYesYes
Units of measureunit_of_measure_idYesYes
Product lotslinked by product_idYesYes

Material fields

  • material.food_id: the nutrition source. Fetch it with GET /foods/{food_id}.
  • material.supplier_ids: the suppliers you buy it from.
  • material.substitute_product_ids: products that can replace it.
  • material.banned_country_ids: countries it must not come from.

Recipe fields

  • recipe.qc_checkpoint_ids: the quality checkpoints the recipe carries.
  • recipe.byproduct_product_ids: products made alongside it.
  • recipe.ingredients_en and recipe.ingredients_fr: the generated ingredient declaration.
  • recipe.portion_in_grams and recipe.recipe_grams_yield.

Allergens

allergens lists the allergens recorded against a product, each with its allergen_id and a source. In v1 every entry is declared: recorded directly against this product. v1 does not roll allergens up from a recipe's components, so a finished good's list shows only what was declared on it. Do not present it as a complete allergen statement.

List versus detail

GET /products leaves allergens, tag_ids, material and recipe null: here null means not loaded, not none. Fetch a single product to read them.

Costs

cost and operations_cost are two separate figures and are never summed for you. Both need cost:read; without it they read null, which means redacted, not zero. Never put a redacted cost into arithmetic.

What v1 does not do

  • No bill of materials. v1 publishes no component list or recipe lines for a product.
  • No product writes. Products are read-only over the API: create and edit them in NutraSoft. Allergens, foods, tags and units of measure do have write operations.
  • Deleted products disappear. A deleted product is no longer listed or retrievable, and polling with updated_since does not report the deletion. See Lots and incremental sync.