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:
| Collection | Reference 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:
valueis multilingual - Preparation instruction values:
valueis multilingual - Packages:
nameanddescriptionare multilingual - Steps:
nameandinstructionsare 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:
consumer_unit— the retail product the consumer buys (e.g. "Fruit Yoghurt 750g")trade_unit— the case or tray for wholesale (e.g. "6x Fruit Yoghurt 750g")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:
| Action | How | Example |
|---|---|---|
| Update | Use the reference | {"reference": "protein", "value": 7.5} |
| Add | Same syntax (auto-detected) | {"reference": "fiber", "value": 3.0} |
| Remove | reference + "remove": true | {"reference": "fat", "remove": true} |
ID-based collections
line_items, packages, steps — identified by id:
| Action | How | Example |
|---|---|---|
| Update | Include the id | {"id": 456, "weight": 200} |
| Add | Omit the id | {"ingredient_id": 123, "weight": 100} |
| Remove | id + "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 thereferencefield to identify records. For ID-based collections (line_items,packages,steps), include theidwhen updating.
Next steps
- Multilingual Fields — how translations work for all text fields
- Migration Guide — upgrade from v1/v2 to v3
- API v3 Reference — full interactive documentation with all fields and types