Migration Guide
This guide covers the breaking changes between API versions and how to update your integration. Pick the section that matches your upgrade path:
- Migrating from v1 to v2
- Migrating from v2 to v3 (recommended)
Migrating from v2 to v3
v3 is the recommended version for all new integrations. The key changes are: proper JSON request bodies, PATCH for updates, and granular nested collection management. Authentication stays the same.
Authentication
v3 uses the same Bearer token format as v1/v2. Your API token and header stay the same:
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://api.eclarion.com/v3/recipes
Reference endpoint changes
Reference data endpoints remain public (no authentication required), same as v1/v2. Two endpoints have been renamed and one is new:
| v2 | v3 | Notes |
|---|---|---|
/v2/microbiologyprecisions | /v3/microbiology_precisions | Renamed (underscores) |
/v2/microbiologymetricunits | /v3/microbiology_metric_units | Renamed (underscores) |
| — | /v3/measurement_units | New (replaces measurement_unit_id integers in line items) |
Request body format
v2 sends recipe data as a URL-encoded JSON string. v3 uses proper JSON:
Before (v2):
curl -X POST \
-H "Authorization: Bearer YOUR_TOKEN" \
-d 'recipe={"productname":{"en":"Fruit Yoghurt"}}' \
https://api.eclarion.com/v2/recipes
After (v3):
curl -X POST \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"recipe": {"productname": {"en": "Fruit Yoghurt"}}}' \
https://api.eclarion.com/v3/recipes
PATCH replaces PUT
Recipe updates use PATCH instead of PUT. Only send the fields you want to change:
Before (v2):
curl -X PUT \
-H "Authorization: Bearer YOUR_TOKEN" \
-d 'recipe={"productname":{"en":"Updated Name"},"ingredientdeclaration":{"en":"..."}}' \
https://api.eclarion.com/v2/recipes/123
After (v3):
curl -X PATCH \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"recipe": {"productname": {"en": "Updated Name"}}}' \
https://api.eclarion.com/v3/recipes/123
Response format
v3 responses are wrapped in a root key and paginated results include a meta object:
{
"recipes": [...],
"meta": {
"page": 1,
"items": 50,
"count": 234,
"pages": 5
}
}
Use the page and items query parameters to control pagination. See the List recipes endpoint for details.
Renamed fields
| v2 field | v3 field | Notes |
|---|---|---|
nutrients | nutritional_values | Nested collection renamed |
measurement_unit_id | measurement_unit | Now a reference string (e.g. kilogram) instead of integer ID |
Type changes
Several fields have changed type for consistency:
| Field | v2 type | v3 type |
|---|---|---|
claims | String | Object |
metal_detection | String | Object |
countries_of_origin | String | Array of strings |
New: recipe steps
v3 introduces steps — named production phases with their own ingredients. This is a completely new concept:
{
"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." }
}
]
}
}
Steps are optional. You can still use recipe-level line_items for simple recipes without production phases. See Steps vs. line items in the Recipes guide.
New: granular nested collection updates
In v2, updating nested collections replaces the entire array. In v3, you can update, add, and remove individual records in a single request.
Reference-based collections (nutritional_values, allergen_values, microbiological_values, physiochemical_values, organoleptic_values, preparation_instruction_values) — identified by reference:
| Action | How |
|---|---|
| Update existing | Use the reference: {"reference": "protein", "value": 7.5} |
| Add new | Same syntax: {"reference": "fiber", "value": 3.0} |
| Remove | reference + remove: {"reference": "fat", "remove": true} |
ID-based collections (line_items, packages, steps) — identified by id:
| Action | How |
|---|---|
| Update existing | Include the id: {"id": 456, "weight": 200} |
| Add new | Omit the id: {"ingredient_id": 123, "weight": 100} |
| Remove | id + remove: {"id": 456, "remove": true} |
{
"recipe": {
"nutritional_values": [
{ "reference": "protein", "value": 7.5 },
{ "reference": "fiber", "value": 2.1 },
{ "reference": "fat", "remove": true }
]
}
}
New fields in v3
| Field | Type | Notes |
|---|---|---|
code | String | Recipe code |
version | String | Recipe version |
created_at | ISO 8601 | Creation timestamp |
updated_at | ISO 8601 | Last update timestamp |
Summary of v2 to v3 changes
- Authentication:
Bearer TOKEN(same as v1/v2) - Request body: URL-encoded JSON string → proper JSON with
Content-Type: application/json - Updates: PUT (full replacement) → PATCH (partial updates)
nutrients→nutritional_values- Type changes:
nutrients_with_loss,metal_detection(boolean),claims(object),countries_of_origin(array) - Reference endpoints: two renamed with underscores, new
/v3/measurement_units - Responses wrapped in root keys with
metapagination object - New: recipe steps with nested line items
- New: granular nested collection updates (add/update/remove in one request)
measurement_unit_id→measurement_unit(reference string, e.g.kilogram)- New:
/v3/measurement_unitsreference endpoint - New:
code,version,created_at,updated_atfields
Migrating from v1 to v2
The biggest change from v1 to v2 is consistent multilingual field handling. v2 also adds several new recipe fields.
Multilingual fields: always an object
In v1, multilingual fields return a plain string for single-language accounts and a translations object for multi-language accounts. This means your code needs to handle both types:
v1 (single-language account):
{
"productname": "Chocolate Cake"
}
v1 (multi-language account):
{
"productname": {
"en": "Chocolate Cake",
"nl": "Chocoladetaart"
}
}
In v2, multilingual fields always return a translations object, regardless of the number of active languages:
v2 (always):
{
"productname": {
"en": "Chocolate Cake"
}
}
Action required: If your v1 code checks whether the field is a plain string or a translations object, you can simplify it. In v2, always expect an object.
Date fields became multilingual
Three date-related fields changed from plain strings to multilingual objects:
| Field | v1 | v2 |
|---|---|---|
best_before_date | "See lid" | {"en": "See lid"} |
use_by_date | "See lid" | {"en": "See lid"} |
date_of_freezing | "N/A" | {"en": "N/A"} |
Update your code to read and write these fields as translation objects.
Removed fields
Three boolean fields were removed and consolidated into the claims field:
| v1 field | v2 replacement |
|---|---|
estimated_sign | Use claims |
free_of_gmo | Use claims |
irradiated | Use claims |
New fields in v2
| Field | Type | Notes |
|---|---|---|
declaration | Boolean | Declaration enabled flag |
microbiology_information | Multilingual | Free-text microbiology info |
custom_fields | JSON object | Free-form key-value data |
data_block_*_id (12 fields) | Integer | Data block references for nutrition, packaging, physiochemical, microbiological, statements, shelf life, organoleptic, preparation, declaration, allergen, notes, product description |
These fields are additive — your existing code will continue to work without changes. Include them when you need the functionality.
Summary of v1 to v2 changes
- Multilingual fields always return a translations object (no more conditional string/object)
best_before_date,use_by_date,date_of_freezingchanged from string to multilingualestimated_sign,free_of_gmo,irradiatedremoved (useclaims)- New fields:
declaration,microbiology_information,custom_fields, 12xdata_block_*_id
Version comparison
| v1 (legacy) | v2 (stable) | v3 (recommended) | |
|---|---|---|---|
| Status | Legacy | Stable | Recommended |
| Auth header | Bearer TOKEN | Bearer TOKEN | Bearer TOKEN |
| Request format | URL-encoded JSON | URL-encoded JSON | JSON body |
| Update method | PUT | PUT | PATCH |
| Multilingual | Conditional | Always object | Always object |
| Pagination meta | No | No | Yes |
| Recipe steps | No | No | Yes |
| Nested updates | Full replacement | Full replacement | Granular (add/update/remove) |
| Public reference endpoints | No | No | Yes |
Next steps
- Getting Started — authentication and your first v3 API call
- Working with Recipes — create and manage recipes with nested data
- Multilingual Fields — how translations work across versions
- API v3 Reference — full interactive documentation
- API v2 Reference
- API v1 Reference