Working with Recipes

Recipes are the core of Eclarion. A recipe contains everything about a food product: name, ingredients, nutritional values, allergens, packaging, and production steps — all in a single API resource. This guide walks you through the typical workflow of creating and managing recipes via the API.

For the full list of fields, types, and JSON examples per collection, see the API v3 Reference.

How recipes work

A recipe in Eclarion is a complete product specification. One GET request returns everything:

curl -H "Authorization: Bearer YOUR_AUTH_TOKEN" \
  https://api.eclarion.com/v3/recipes/123

The response includes all nested collections inline — nutritional values, allergens, microbiological values, physiochemical values, organoleptic values, preparation instructions, packages, steps with line items, and recipe-level line items. No separate requests needed.

Step 1: Look up your brand and category

Every recipe belongs to a brand and a category. You'll need their IDs before creating a recipe:

# Get your brands
curl -H "Authorization: Bearer YOUR_AUTH_TOKEN" \
  https://api.eclarion.com/v3/brands

# Get your categories
curl -H "Authorization: Bearer YOUR_AUTH_TOKEN" \
  https://api.eclarion.com/v3/categories

Categories organize your products (e.g. "Ingredients", "Recipes", "Finished Products"). Use the id values from these responses in your recipe.

Step 2: Create a minimal recipe

Start with the required fields: productname, ingredientdeclaration (both with your account's primary locale), brand_id, and category_id:

curl -X POST \
  -H "Authorization: Bearer YOUR_AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "recipe": {
      "productname": { "en": "Fruit Yoghurt", "nl": "Vruchtenyoghurt" },
      "ingredientdeclaration": { "en": "milk, sugar, strawberry 8%", "nl": "melk, suiker, aardbei 8%" },
      "brand_id": 42,
      "category_id": 1
    }
  }' \
  https://api.eclarion.com/v3/recipes

This returns the created recipe with its id. You can now add more data by updating it, or include everything in one POST.

Text fields like productname are multilingual — provide at least the primary locale, and add other languages as needed.

Step 3: Add nested collections

The real power of the API is sending everything in one request. You can include all nested collections when creating or updating a recipe.

Using reference strings

For nested collections, you use reference strings instead of internal IDs. The API resolves them automatically. Get the available references from the public endpoints:

CollectionReference endpoint
Nutritional values/v3/nutrients
Allergen values/v3/allergens
Microbiological values/v3/microbiologies, precisions, metric units
Organoleptic values/v3/organoleptic_properties
Preparation instructions/v3/preparation_instructions
Package levels/v3/package_levels
Measurement units (line items)/v3/measurement_units

These reference endpoints are public and don't require authentication.

Multilingual nested values

Some nested collection values support translations. These use the same locale-keyed object as recipe fields:

  • Organoleptic values: value is multilingual
  • Preparation instruction values: value is multilingual
  • Packages: name and description are multilingual
  • Steps: name and instructions are multilingual

For the full field details and JSON examples for each collection, see the Create a recipe endpoint in the API reference.

Recipe steps vs. recipe-level line items

Ingredients can be organized in two ways, and this is a common point of confusion:

  • Recipe-level line_items — ingredients directly on the recipe, without step structure. Use this for simple recipes without production phases.
  • Steps with line_items — ingredients organized into named production phases (e.g. "Mixing", "Baking", "Finishing"). Each step has a position, name, instructions, and its own ingredients.

Steps are new in v3 and are the recommended approach for recipes with a production process. You can use either approach, but not both on the same recipe.

"steps": [
  {
    "position": 1,
    "name": { "en": "Mixing" },
    "instructions": { "en": "Combine all dry ingredients." },
    "line_items": [
      { "ingredient_id": 150, "weight": 500, "measurement_unit": "gram" },
      { "ingredient_id": 151, "weight": 200 }
    ]
  },
  {
    "position": 2,
    "name": { "en": "Baking" },
    "instructions": { "en": "Bake at 180°C for 25 minutes." }
  }
]

Packaging levels

Recipes typically have up to three packaging levels that represent the supply chain hierarchy:

  1. consumer_unit — the retail product the consumer buys (e.g. "Fruit Yoghurt 750g")
  2. trade_unit — the case or tray for wholesale (e.g. "6x Fruit Yoghurt 750g")
  3. pallet — the full pallet for logistics (e.g. "54 trays on pallet")

Each level can have its own EAN code, dimensions, and weight. The number_of_products field indicates how many of the previous level fit inside (e.g. 6 consumer units per trade unit).

See the API reference for all available package fields (dimensions, weights, GS1 properties).

Updating a recipe

Use PATCH with partial data — only send the fields you want to change:

curl -X PATCH \
  -H "Authorization: Bearer YOUR_AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"recipe": {"code": "R-002", "netcontent": "750 g"}}' \
  https://api.eclarion.com/v3/recipes/123

Update, add, or remove nested records

Nested collections come in two types, each with their own way of identifying records:

Reference-based collections

nutritional_values, allergen_values, microbiological_values, physiochemical_values, organoleptic_values, preparation_instruction_values — identified by reference:

ActionHowExample
UpdateUse the reference{"reference": "protein", "value": 7.5}
AddSame syntax (auto-detected){"reference": "fiber", "value": 3.0}
Removereference + "remove": true{"reference": "fat", "remove": true}

ID-based collections

line_items, packages, steps — identified by id:

ActionHowExample
UpdateInclude the id{"id": 456, "weight": 200}
AddOmit the id{"ingredient_id": 123, "weight": 100}
Removeid + "remove": true{"id": 456, "remove": true}

You can mix all three operations in a single request. Records not mentioned are left untouched:

{
  "recipe": {
    "nutritional_values": [
      { "reference": "protein", "value": 7.5 },
      { "reference": "fiber", "value": 2.1 },
      { "reference": "fat", "remove": true }
    ]
  }
}

Deleting a recipe

Deletes a recipe:

curl -X DELETE \
  -H "Authorization: Bearer YOUR_AUTH_TOKEN" \
  https://api.eclarion.com/v3/recipes/123

Complete example

Here's a full recipe creation request with the most common fields. This gives you a good starting point to adapt:

{
  "recipe": {
    "productname": { "en": "Fruit Yoghurt", "nl": "Vruchtenyoghurt" },
    "labeldescription": { "en": "Yoghurt with strawberry", "nl": "Yoghurt met aardbei" },
    "ingredientdeclaration": { "en": "milk, sugar, strawberry 8%", "nl": "melk, suiker, aardbei 8%" },
    "code": "R-001",
    "category_id": 1,
    "brand_id": 42,
    "netcontent": "750 g",
    "usagestorageinstruction": { "en": "Keep refrigerated between 2°C and 7°C" },
    "best_before_date": { "en": "See lid" },
    "ean": "1234567890128",
    "minimum_lifespan_from_production": 21,
    "minimum_storage_temperature": 2,
    "maximum_storage_temperature": 7,
    "nutritional_values": [
      { "reference": "energykj", "value": 380 },
      { "reference": "energykcal", "value": 90 },
      { "reference": "fat", "value": 1.5 },
      { "reference": "protein", "value": 4.2 },
      { "reference": "carbohydrates", "value": 14.8 }
    ],
    "allergen_values": [
      { "reference": "milk", "value": "2" },
      { "reference": "gluten", "value": "0" }
    ],
    "microbiological_values": [
      {
        "reference": "salmonella",
        "precision": "absent_in",
        "value": 25,
        "metric_unit": "gram",
        "method": "ISO 6579"
      }
    ],
    "organoleptic_values": [
      { "reference": "color", "value": { "en": "Light pink with fruit pieces" } },
      { "reference": "taste", "value": { "en": "Fresh, mildly sweet" } }
    ],
    "steps": [
      {
        "position": 1,
        "name": { "en": "Mixing", "nl": "Mengen" },
        "line_items": [
          { "ingredient_id": 150, "weight": 650 },
          { "ingredient_id": 151, "weight": 80 },
          { "ingredient_id": 152, "weight": 60 }
        ]
      }
    ],
    "packages": [
      {
        "level": "consumer_unit",
        "name": { "en": "Fruit Yoghurt 750g" },
        "ean": "1234567890123",
        "number_of_products": 1,
        "net_weight": 750,
        "net_weight_unit": "gram",
        "gross_weight": 800,
        "gross_weight_unit": "gram"
      },
      {
        "level": "trade_unit",
        "name": { "en": "6x Fruit Yoghurt 750g" },
        "ean": "0987654321098",
        "number_of_products": 6,
        "net_weight": 4500,
        "net_weight_unit": "gram",
        "gross_weight": 4950,
        "gross_weight_unit": "gram"
      }
    ]
  }
}

Tips

  • Start minimal, then expand. Create a recipe with just the required fields, verify it works, then add nested collections.
  • Cache reference data. Allergens, nutrients, and other reference lists rarely change. Fetch them once and reuse.
  • Use PATCH, not PUT. Partial updates are efficient — only send what changed.
  • Reference-based collections don't need id. Use the reference field to identify records. For ID-based collections (line_items, packages, steps), include the id when updating.

Next steps