Skip to main content

Shopping Lists

This section documents the shopping list endpoints of the MiseOS API.

Shopping lists are generated from approved ingredient requests for a delivery date.
AI normalization merges ingredient name variants and synonyms across languages into a single normalized name. If AI normalization fails, the shopping list is still generated using the original ingredient names. Manual items can also be added to shopping lists after generation.

The diagrams below give a quick overview of how shopping lists are created and managed.

  • Generation workflow shows how approved ingredient requests are transformed into a shopping list, including AI normalization and fallback behavior.
  • Lifecycle shows the allowed shopping list states and when transitions happen.

Together, they help clarify both the process flow and state rules before diving into endpoint details.

Shopping list generation workflow
#

Shopping lists are generated from approved ingredient requests for a specific delivery date.

The workflow below illustrates how ingredient requests are aggregated, normalized using AI, and transformed into a shopping list.

flowchart TD
    A[Ingredient Requests Submitted]
    B[Requests Approved]
    C[Shopping List Generated]
    D[AI Ingredient Normalization]
    E[Shopping List Created - DRAFT]
    F[Items Marked as Ordered]
    G[Shopping List Finalized]

    A --> B
    B --> C
    C --> D
    D --> E
    E --> F
    F --> G

Shopping list lifecycle
#

stateDiagram-v2
    [*] --> DRAFT : list generated

    DRAFT --> FINALIZED : finalize

    DRAFT --> [*] : deleted
    FINALIZED --> [*]

Shopping lists are initially created in the DRAFT state.
While in draft, items can be added, removed, or updated.

Once all items are marked as ordered, the list can be finalized.
Finalized lists are immutable and represent completed purchase orders.

Shopping Lists Endpoints
#

MethodURLAuth
GET/shopping-listsHEAD_CHEF, SOUS_CHEF
GET/shopping-lists/{id}HEAD_CHEF, SOUS_CHEF
POST/shopping-listsHEAD_CHEF, SOUS_CHEF
POST/shopping-lists/{id}/finalizeHEAD_CHEF, SOUS_CHEF
PATCH/shopping-lists/{id}/delivery-dateHEAD_CHEF, SOUS_CHEF
DELETE/shopping-lists/{id}HEAD_CHEF, SOUS_CHEF
POST/shopping-lists/{id}/itemsHEAD_CHEF, SOUS_CHEF
PUT/shopping-lists/{id}/items/{itemId}HEAD_CHEF, SOUS_CHEF
DELETE/shopping-lists/{id}/items/{itemId}HEAD_CHEF, SOUS_CHEF
PATCH/shopping-lists/{id}/items/{itemId}/orderedHEAD_CHEF, SOUS_CHEF
PATCH/shopping-lists/{id}/items/orderedHEAD_CHEF, SOUS_CHEF

ShoppingList response object
#

The shopping list object has the following structure:

{
  "id": 1,
  "deliveryDate": "2026-04-01",
  "status": "DRAFT",
  "createdBy": { "id": 1, "firstName": "Gordon", "lastName": "Ramsay" },
  "itemCount": 3,
  "items": [
    {
      "id": 1,
      "ingredientName": "Løg",
      "quantity": 14.0,
      "unit": "KG",
      "supplier": "Inco",
      "notes": "Claire (løg: 7.0 KG) | Marco (onions: 7.0 KG)",
      "ordered": false,
      "createdAt": "2026-03-27 10:00",
      "updatedAt": null
    },
    {
      "id": 2,
      "ingredientName": "Smør",
      "quantity": 5.0,
      "unit": "KG",
      "supplier": "Arla",
      "notes": "Manual entry by: Gordon Ramsay",
      "ordered": false,
      "createdAt": "2026-03-27 10:05",
      "updatedAt": null
    },
    {
      "id": 3,
      "ingredientName": "Frisk Dild",
      "quantity": 10.0,
      "unit": "BUNCH",
      "supplier": "Grønttorvet",
      "notes": "Claire (Frisk Dild: 10.0 BUNCH)",
      "ordered": false,
      "createdAt": "2026-03-27 10:00",
      "updatedAt": null
    }
  ],
  "allOrdered": false,
  "normalized": true,
  "createdAt": "2026-03-27 10:00",
  "finalizedAt": null
}

Status values: DRAFT | FINALIZED

allOrdered indicates whether every item in the shopping list has been marked as ordered.

normalized = true: AI successfully normalized ingredient names across languages.

normalized = false: AI normalization failed and the system fell back to original ingredient names.

notes describes the origin of the ingredient entry.

Examples:

  • Aggregated ingredient requests
    Claire (løg: 7.0 KG) | Marco (onions: 7.0 KG)

  • Manual entries
    Manual entry by: Gordon Ramsay


GET /shopping-lists
#

Returns shopping lists with optional filters.

Query parameters

ParameterTypeDescription
statusStringFilter by DRAFT or FINALIZED
deliveryDateLocalDateFilter by yyyy-MM-dd

Example Request
#

curl -H "Authorization: Bearer <token>" \
"https://miseos.corral.dk/api/v1/shopping-lists?status=DRAFT&deliveryDate=2026-04-01"

Response 200 — array of shopping list objects.

Errors

StatusCause
400Invalid status or date format

GET /shopping-lists/{id}
#

Returns one shopping list by ID.

Example Request
#

curl -H "Authorization: Bearer <token>" \
https://miseos.corral.dk/api/v1/shopping-lists/1

Response 200 — shopping list object.

Errors

StatusCause
404Shopping list not found

POST /shopping-lists
#

Generates a shopping list from approved ingredient requests for a delivery date.

Ingredient names from the requests are aggregated and sent to the AI normalization service.
The targetLanguage determines the language used for the normalized ingredient names in the final shopping list.

For example:

  • løg
  • onions
  • cebolla

With targetLanguage = EN these may all be normalized to:

Onions

Supported languages
#

targetLanguage must be one of the supported values:

CodeLanguage
DADanish
ENEnglish
ESSpanish
ITItalian
PTPortuguese
FRFrench
DEGerman
PLPolish
NLDutch

If an unsupported value is provided, the system defaults to EN (English).

Request body

FieldTypeDescription
deliveryDateLocalDateDelivery date for the shopping list
targetLanguageStringLanguage used for AI ingredient normalization
{
  "deliveryDate": "2026-04-01",
  "targetLanguage": "DA"
}

Example Request
#

curl -X POST \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "deliveryDate": "2026-04-01",
    "targetLanguage": "DA"
  }' \
  https://miseos.corral.dk/api/v1/shopping-lists

Response 201 — generated shopping list object.

Errors

StatusCause
400Invalid payload (missing deliveryDate or targetLanguage)
409Shopping list already exists for that date
409No approved ingredient requests for that date

POST /shopping-lists/{id}/finalize
#

Finalizes a shopping list. All items must be marked as ordered.

Example Request
#

curl -X POST \
  -H "Authorization: Bearer <token>" \
  https://miseos.corral.dk/api/v1/shopping-lists/1/finalize

Response 200 — updated shopping list object with status: FINALIZED.

Errors

StatusCause
404Shopping list not found
409Not all items are ordered
409List is already finalized

PATCH /shopping-lists/{id}/delivery-date
#

Updates delivery date for a draft list.

Request body

{
  "deliveryDate": "2026-04-03"
}

Example Request
#

curl -X PATCH \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "deliveryDate": "2026-04-03"
  }' \
  https://miseos.corral.dk/api/v1/shopping-lists/1/delivery-date

Response 200 — updated shopping list object.

Errors

StatusCause
400Date is missing, invalid, or in the past
404Shopping list not found
409Another shopping list already exists for that date
409List is finalized

DELETE /shopping-lists/{id}
#

Deletes a draft shopping list.

Example Request
#

curl -X DELETE \
  -H "Authorization: Bearer <token>" \
  https://miseos.corral.dk/api/v1/shopping-lists/1

Response 204 — no content.

Errors

StatusCause
404Shopping list not found
409List is finalized

POST /shopping-lists/{id}/items
#

Adds a manual item to a draft shopping list.

Request body

{
  "ingredientName": "Smør",
  "quantity": 5.0,
  "unit": "KG",
  "supplier": "Arla"
}

The system automatically adds a note like: Manual entry by: <FirstName LastName>.

Example Request
#

curl -X POST \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "ingredientName": "Smør",
    "quantity": 5.0,
    "unit": "KG",
    "supplier": "Arla"
  }' \
  https://miseos.corral.dk/api/v1/shopping-lists/1/items

Response 201 — updated shopping list object.

Errors

StatusCause
400Invalid payload
404Shopping list not found
409List is finalized

PUT /shopping-lists/{id}/items/{itemId}
#

Updates a shopping list item (draft lists only).

Request body

{
  "quantity": 8.0,
  "unit": "KG",
  "supplier": "Ny Leverandør"
}

quantity and unit are required. supplier is optional.

Example Request
#

curl -X PUT \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "quantity": 8.0,
    "unit": "KG",
    "supplier": "Ny Leverandør"
  }' \
  https://miseos.corral.dk/api/v1/shopping-lists/1/items/2

Response 200 — updated shopping list object.

Errors

StatusCause
400Invalid payload
404Shopping list or item not found
409List is finalized

DELETE /shopping-lists/{id}/items/{itemId}
#

Removes an item from a draft list.

Example Request
#

curl -X DELETE \
  -H "Authorization: Bearer <token>" \
  https://miseos.corral.dk/api/v1/shopping-lists/1/items/2

Response 200 — updated shopping list object.

Errors

StatusCause
404Shopping list or item not found
409List is finalized

PATCH /shopping-lists/{id}/items/{itemId}/ordered
#

Marks one item as ordered.

Example Request
#

curl -X PATCH \
  -H "Authorization: Bearer <token>" \
  https://miseos.corral.dk/api/v1/shopping-lists/1/items/2/ordered

Response 200 — updated shopping list object.

Errors

StatusCause
404Shopping list or item not found
409Item is already marked as ordered

PATCH /shopping-lists/{id}/items/ordered
#

Marks all items in the shopping list as ordered.

This is a convenience endpoint used when the entire list has been ordered from suppliers. It performs the same function as marking each item individually but reduces the number of API calls needed.

Example Request
#

curl -X PATCH \
  -H "Authorization: Bearer <token>" \
  https://miseos.corral.dk/api/v1/shopping-lists/1/items/ordered

Response 200 — updated shopping list object with allOrdered: true.

Errors

StatusCause
404Shopping list not found
409One or more items are already marked as ordered