Documentation

Update products

POST /api

The body is one JSON object or an array of objects. Content-Type: application/json. Requires an API key when keys are enabled.

Sending only the fields that changed is a delta: the existing unit is loaded and those items are saved. Sending a larger set of fields is a fuller update. There is no separate delta URL.

curl -sS -X POST \
  -H "X-Api-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"D8165":"01234567890123","D8255":"208","D8276":"BASE_UNIT_OR_EACH","D8258_1":"Test product","D8259_1":"en"}' \
  "https://{instance}.fmcgproducts.dk/api"

Import a file

POST /api/import

An .xls, .xlsx, or .xml file creates or updates products through this call. The original file name selects the import profile. Import an Excel or XML file has the request, the content types, and the response.

Create vs update

The product id is D8165 + . + D8255. D8255 is the ISO 3166-1 numeric target market code, sent as a string. Identifiers and fields has the rules, including leading zeros. If that unit does not exist, the API creates it. D8276 is then required. If it exists, listed fields are updated.

Keep or clear a value

Omit a field to leave the current value. Send "" or null to clear it:

{
  "D8165": "01234567890123",
  "D8255": "208",
  "D8256": ""
}

Grids

Repeating values use FieldId_n with a 1-based row index. If a grid currently has five rows and you send only _1, the remaining rows are cleared.

To clear a whole grid, either:

  • send blank/null on the first row for every field in that grid, or
  • add the grid name to the deleteGrids array (the name is on the field definition).

Request options

These keys are not product fields. They are stripped before save:

Key Meaning
test If true, validate and compute as usual but do not persist. The response includes "test": true.
customRequestId Echoed on success and on error so you can correlate batches.
deleteGrids JSON array of grid names. Each named grid is cleared (fields not also present in the body are set to null).
deleteNotUsedPreparationStateFields If true, unused PREPARED or UNPREPARED nutrient fields are removed when the product only has one of those states.

Validation without save

POST /api/validate

Same body as POST /api. The server sets test: true and never persists. Use this to validate one or more drafts together in memory for the duration of the request.

You can also send "test": true on POST /api; that is the same dry-run behaviour.

Numbers and decimals

Integer fields must be sent as JSON numbers without a fractional part. For measurement-style fields D8171, D8175, D8183, D8193, D8217 and D8280, a string such as "1.50" or "1,50" is converted to a number plus a matching …Decimals field.

Keyword and other coded values must be codes, not labels. Multi-value fields are JSON arrays of strings.

Dates

Date and date/time fields accept:

  • dd-MM-yyyy
  • dd-MM-yyyy HH:mm
  • yyyy-MM-dd'T'HH:mm:ss
  • yyyy-MM-dd'T'HH:mm:ss.SSS

Values are in UTC.

Response

A single-product success looks like a details payload: productId, unitType, fmcgProductsStatus, validationErrors, and related status fields. If save is rejected, result is Product not updated and the previous data is returned.

A batch request returns:

{
  "status": 200,
  "count": 2,
  "products": [ { } ]
}

If any item has input errors, HTTP 400 is returned for the batch. Related products are recalculated after a successful batch.

Optimistic locking: if another user saved the unit first, the API returns the translated locking error.

Readonly and computed fields in the body are ignored. Unknown field names return field-level errors.