Ordeliya Docs

Products

Manage your menu — products, categories, option groups, variants, and pricing.

Overview

The Products API lets you manage your entire menu programmatically. Products support multi-language translations, multiple variants with per-currency pricing, option groups (modifiers/extras), ingredient tracking, and cross-selling.


Products

POST /products

Create a new product with variants, translations, pricing, and category assignments.

Auth: Bearer Token (write:products, Manager role or above)

curl -X POST https://api.ordeliya.com/products \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..." \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "margherita-pizza",
    "translations": [
      {
        "locale": "da-DK",
        "name": "Margherita Pizza",
        "shortDesc": "Klassisk tomat og mozzarella",
        "longDesc": "Frisk mozzarella, San Marzano tomater, frisk basilikum"
      },
      {
        "locale": "en-GB",
        "name": "Margherita Pizza",
        "shortDesc": "Classic tomato and mozzarella"
      }
    ],
    "categoryIds": ["cat_p1z2a3"],
    "variants": [
      {
        "translations": [{ "locale": "da-DK", "name": "Normal" }],
        "prices": [
          { "currency": "DKK", "priceMinor": 8900 },
          { "currency": "EUR", "priceMinor": 1199 }
        ],
        "isDefault": true
      },
      {
        "translations": [{ "locale": "da-DK", "name": "Familiestørrelse" }],
        "prices": [
          { "currency": "DKK", "priceMinor": 14900 },
          { "currency": "EUR", "priceMinor": 1999 }
        ]
      }
    ],
    "ingredients": [
      { "ingredientId": "ing_moz1", "rule": "REQUIRED" },
      { "ingredientId": "ing_bas1", "rule": "REMOVABLE" }
    ],
    "isActive": true,
    "isFeatured": false,
    "sortOrder": 0
  }'
FieldTypeRequiredDescription
slugstringyesURL-safe identifier (2-100 chars)
translationsarrayyesAt least one translation with locale and name
categoryIdsstring[]yesCategory IDs to assign the product to
variantsarrayyesAt least one variant with translations and prices
variants[].pricesarrayyesPer-currency pricing in minor units
variants[].prices[].priceMinorintegeryesPrice in minor units (8900 = 89.00 DKK)
variants[].prices[].comparePriceMinorintegernoOriginal price for strikethrough display
variants[].prices[].costMinorintegernoCost price for margin calculation
variants[].optionLinksarraynoLink option groups to this variant
ingredientsarraynoIngredient associations with rules
tagIdsstring[]noTag IDs for metadata
crossSellTargetIdsstring[]noProduct IDs to suggest as cross-sell
isActivebooleannoDefault true
isFeaturedbooleannoDefault false
isPickupOnlybooleannoDefault false
sortOrderintegernoDisplay order (ascending)

Response 201 Created

{
  "success": true,
  "data": {
    "id": "prod_8kx2m4n7",
    "storeId": "store_r4k7",
    "slug": "margherita-pizza",
    "isActive": true,
    "isFeatured": false,
    "sortOrder": 0,
    "translations": [
      {
        "locale": "da-DK",
        "name": "Margherita Pizza",
        "shortDesc": "Klassisk tomat og mozzarella",
        "longDesc": "Frisk mozzarella, San Marzano tomater, frisk basilikum"
      }
    ],
    "variants": [
      {
        "id": "var_a1b2c3",
        "sku": null,
        "isDefault": true,
        "isActive": true,
        "translations": [{ "locale": "da-DK", "name": "Normal" }],
        "prices": [
          { "currency": "DKK", "priceMinor": 8900, "comparePriceMinor": null, "costMinor": null },
          { "currency": "EUR", "priceMinor": 1199, "comparePriceMinor": null, "costMinor": null }
        ]
      },
      {
        "id": "var_d4e5f6",
        "sku": null,
        "isDefault": false,
        "isActive": true,
        "translations": [{ "locale": "da-DK", "name": "Familiestørrelse" }],
        "prices": [
          { "currency": "DKK", "priceMinor": 14900, "comparePriceMinor": null, "costMinor": null }
        ]
      }
    ],
    "categories": [
      { "categoryId": "cat_p1z2a3", "category": { "id": "cat_p1z2a3", "name": "Pizzas" } }
    ],
    "baseIngredients": [
      { "ingredientId": "ing_moz1", "rule": "REQUIRED", "ingredient": { "name": "Mozzarella" } },
      { "ingredientId": "ing_bas1", "rule": "REMOVABLE", "ingredient": { "name": "Basilikum" } }
    ],
    "createdAt": "2026-03-15T10:00:00.000Z"
  }
}

GET /products

List all products with filtering, search, and pagination.

Auth: Bearer Token or API Key (read:products)

curl "https://api.ordeliya.com/products?page=1&pageSize=20&search=pizza&isActive=true" \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..."
ParameterTypeDefaultDescription
pageinteger1Page number
pageSizeinteger10Items per page (max 100)
searchstringSearch in product names (case-insensitive)
localestringenLocale for translations
isActivebooleanFilter by active status
categoryIdstringFilter by category
tagIdstringFilter by tag
isFeaturedbooleanFilter featured products
includeDetailsbooleanfalseInclude ingredients, cross-sell in response

Response 200 OK

{
  "success": true,
  "data": [
    {
      "id": "prod_8kx2m4n7",
      "slug": "margherita-pizza",
      "isActive": true,
      "isFeatured": false,
      "sortOrder": 0,
      "translations": [{ "locale": "da-DK", "name": "Margherita Pizza" }],
      "variants": [
        {
          "id": "var_a1b2c3",
          "isDefault": true,
          "prices": [{ "currency": "DKK", "priceMinor": 8900 }]
        }
      ],
      "categories": [
        { "categoryId": "cat_p1z2a3", "category": { "name": "Pizzas" } }
      ]
    }
  ],
  "meta": {
    "total": 47,
    "page": 1,
    "pageSize": 20,
    "totalPages": 3
  }
}

GET /products/:id

Get full product detail including all variants, prices, ingredients, and cross-sell products.

Auth: Bearer Token or API Key (read:products)

curl https://api.ordeliya.com/products/prod_8kx2m4n7 \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..."

Returns the full product object (same shape as the POST response).


PATCH /products/:id

Update a product. Only provided fields are modified. Array fields (translations, variants, ingredients) use replace-all semantics — the old records are replaced entirely with the new array.

Auth: Bearer Token (write:products, Manager role or above)

curl -X PATCH https://api.ordeliya.com/products/prod_8kx2m4n7 \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..." \
  -H "Content-Type: application/json" \
  -d '{
    "isActive": false,
    "isFeatured": true
  }'

Response 200 OK — Full product object with updated fields.


DELETE /products/:id

Permanently delete a product and all its variants, prices, and associations.

Auth: Bearer Token (write:products, Admin or Owner role)

curl -X DELETE https://api.ordeliya.com/products/prod_8kx2m4n7 \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..."

Response 200 OK

{
  "success": true,
  "data": {
    "message": "Product deleted successfully"
  }
}

PATCH /products/bulk

Perform bulk operations on multiple products at once.

Auth: Bearer Token (write:products, Manager role or above)

curl -X PATCH https://api.ordeliya.com/products/bulk \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..." \
  -H "Content-Type: application/json" \
  -d '{
    "ids": ["prod_8kx2m4n7", "prod_3jn8v5q2", "prod_x9y0z1"],
    "action": "deactivate"
  }'
ActionDescription
activateSet all products to active
deactivateSet all products to inactive
move_categoryMove all to a new category (requires categoryId)
deletePermanently delete all

Response 200 OK

{
  "success": true,
  "data": {
    "message": "3 products deactivated",
    "count": 3
  }
}

PATCH /products/reorder

Update the display order of multiple products in a single request.

Auth: Bearer Token (write:products, Manager role or above)

curl -X PATCH https://api.ordeliya.com/products/reorder \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..." \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      { "id": "prod_8kx2m4n7", "sortOrder": 0 },
      { "id": "prod_3jn8v5q2", "sortOrder": 1 },
      { "id": "prod_x9y0z1", "sortOrder": 2 }
    ]
  }'

Response 200 OK

{
  "success": true,
  "data": {
    "message": "Products reordered successfully",
    "count": 3
  }
}

Categories

Categories organize your menu. Products can belong to multiple categories.

POST /categories

curl -X POST https://api.ordeliya.com/categories \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Pizzas",
    "slug": "pizzas",
    "parentId": null,
    "sortOrder": 0,
    "isActive": true
  }'

Response 201 Created

{
  "success": true,
  "data": {
    "id": "cat_p1z2a3",
    "storeId": "store_r4k7",
    "name": "Pizzas",
    "slug": "pizzas",
    "parentId": null,
    "sortOrder": 0,
    "isActive": true,
    "createdAt": "2026-03-15T10:00:00.000Z"
  }
}

GET /categories

List all categories for the current store, including nested hierarchy.

curl https://api.ordeliya.com/categories \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..."

Response 200 OK

{
  "success": true,
  "data": [
    {
      "id": "cat_p1z2a3",
      "name": "Pizzas",
      "slug": "pizzas",
      "parentId": null,
      "sortOrder": 0,
      "isActive": true,
      "productCount": 12
    },
    {
      "id": "cat_s1d2e3",
      "name": "Sides",
      "slug": "sides",
      "parentId": null,
      "sortOrder": 1,
      "isActive": true,
      "productCount": 8
    }
  ]
}

PATCH /categories/:id

curl -X PATCH https://api.ordeliya.com/categories/cat_p1z2a3 \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..." \
  -H "Content-Type: application/json" \
  -d '{ "name": "Premium Pizzas", "sortOrder": 0 }'

DELETE /categories/:id

curl -X DELETE https://api.ordeliya.com/categories/cat_p1z2a3 \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..."

Response 204 No Content

PATCH /categories/reorder

Reorder categories and optionally change parent assignments.

curl -X PATCH https://api.ordeliya.com/categories/reorder \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..." \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      { "id": "cat_p1z2a3", "sortOrder": 0, "parentId": null },
      { "id": "cat_s1d2e3", "sortOrder": 1, "parentId": null }
    ]
  }'

Option Groups

Option groups define modifiers and extras (e.g., "Size", "Toppings", "Extra Cheese"). They are linked to product variants.

POST /option-groups

curl -X POST https://api.ordeliya.com/option-groups \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Extra Toppings",
    "isRequired": false,
    "allowMultiple": true,
    "choices": [
      {
        "name": "Extra Cheese",
        "prices": [{ "currency": "DKK", "priceMinor": 1500 }]
      },
      {
        "name": "Pepperoni",
        "prices": [{ "currency": "DKK", "priceMinor": 2000 }]
      },
      {
        "name": "Mushrooms",
        "prices": [{ "currency": "DKK", "priceMinor": 1500 }]
      }
    ]
  }'
FieldTypeRequiredDescription
namestringyesGroup name (e.g., "Size", "Toppings")
isRequiredbooleannoMust the customer select at least one? Default false
allowMultiplebooleannoCan the customer select more than one? Default false
choicesarrayyesAvailable options within this group
choices[].namestringyesChoice label (e.g., "Extra Cheese")
choices[].pricesarrayyesPrice delta per currency in minor units

Response 201 Created

{
  "success": true,
  "data": {
    "id": "og_t1o2p3",
    "storeId": "store_r4k7",
    "name": "Extra Toppings",
    "isRequired": false,
    "allowMultiple": true,
    "choices": [
      {
        "id": "oc_c1h2e3",
        "name": "Extra Cheese",
        "prices": [{ "currency": "DKK", "priceMinor": 1500 }]
      },
      {
        "id": "oc_p4e5p6",
        "name": "Pepperoni",
        "prices": [{ "currency": "DKK", "priceMinor": 2000 }]
      },
      {
        "id": "oc_m7u8s9",
        "name": "Mushrooms",
        "prices": [{ "currency": "DKK", "priceMinor": 1500 }]
      }
    ]
  }
}

GET /option-groups

List all option groups for the current store.

curl https://api.ordeliya.com/option-groups \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..."

PATCH /option-groups/:id

Update an option group.

curl -X PATCH https://api.ordeliya.com/option-groups/og_t1o2p3 \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..." \
  -H "Content-Type: application/json" \
  -d '{ "isRequired": true }'

DELETE /option-groups/:id

curl -X DELETE https://api.ordeliya.com/option-groups/og_t1o2p3 \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..."

POST /option-groups/:id/choices

Add a new choice to an existing option group.

curl -X POST https://api.ordeliya.com/option-groups/og_t1o2p3/choices \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Jalapeños",
    "prices": [{ "currency": "DKK", "priceMinor": 1000 }]
  }'

PATCH /option-groups/:groupId/choices/:choiceId

Update a specific choice within an option group.

curl -X PATCH https://api.ordeliya.com/option-groups/og_t1o2p3/choices/oc_c1h2e3 \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Extra Mozzarella",
    "prices": [{ "currency": "DKK", "priceMinor": 1800 }]
  }'

DELETE /option-groups/:groupId/choices/:choiceId

Remove a choice from an option group.

curl -X DELETE https://api.ordeliya.com/option-groups/og_t1o2p3/choices/oc_c1h2e3 \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..."

Ingredients

Ingredients track pantry items and allergens. Link them to products with rules (REQUIRED, REMOVABLE).

POST /ingredients

curl -X POST https://api.ordeliya.com/ingredients \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Mozzarella",
    "allergenIds": ["alg_dairy"]
  }'

GET /ingredients

curl https://api.ordeliya.com/ingredients \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..."

Response 200 OK

{
  "success": true,
  "data": [
    {
      "id": "ing_moz1",
      "name": "Mozzarella",
      "allergens": [{ "id": "alg_dairy", "name": "Milk" }]
    },
    {
      "id": "ing_bas1",
      "name": "Basilikum",
      "allergens": []
    }
  ]
}

PATCH /ingredients/:id

curl -X PATCH https://api.ordeliya.com/ingredients/ing_moz1 \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..." \
  -H "Content-Type: application/json" \
  -d '{ "name": "Fresh Mozzarella" }'

DELETE /ingredients/:id

curl -X DELETE https://api.ordeliya.com/ingredients/ing_moz1 \
  -H "Authorization: Bearer ord_live_sk_7f3a9b2c..."

Response 204 No Content