{
  "openapi": "3.0.4",
  "info": {
    "title": "RapidHub POS API",
    "description": "REST API for POS systems connected to RapidHub. One integration receives orders from Wolt, Uber Eats, Delivery Hero (foodora, Glovo …), Just Eat Takeaway and the built-in Mock sandbox platform. Orders are pushed in real time over SignalR (`/hubs/orders`, event `OrderEvent`); this document covers the REST endpoints. Full guide: https://hub.rapidpaygroup.com/integration/documentation",
    "contact": {
      "name": "RapidHub integration support",
      "url": "https://hub.rapidpaygroup.com/integration/documentation"
    },
    "version": "1.0"
  },
  "servers": [
    {
      "url": "https://hub.rapidpaygroup.com",
      "description": "Production (sandbox orders use the Mock platform on the same host)"
    }
  ],
  "paths": {
    "/pos/catalog/connections": {
      "get": {
        "tags": [
          "Catalog"
        ],
        "summary": "List platform connections",
        "description": "Platform connections of the tenant that owns the restaurant key, with their capabilities. Drive your UI from `capabilities`, not from the platform name. Authenticate with `X-Restaurant-Key: <restaurant key>` (or `Authorization: Bearer <restaurant key>`).",
        "operationId": "listConnections",
        "responses": {
          "200": {
            "description": "Connections.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PlatformConnection"
                  }
                },
                "example": [
                  {
                    "id": "7d0c2a4e-5b1f-4c8e-9a3d-2f6b8e1c4a70",
                    "platform": "Mock",
                    "platformStoreId": "mock-3f9a1c2e",
                    "environment": "Staging",
                    "brand": null,
                    "isActive": true,
                    "capabilities": {
                      "supportsPrepTime": true,
                      "supportsRejectCodes": true,
                      "supportsStoreStatus": true,
                      "supportsItemAvailability": true,
                      "menuPush": "Full"
                    }
                  },
                  {
                    "id": "1e5a9c3f-7b2d-4e8a-b6c0-4f9d2a7e1c38",
                    "platform": "Wolt",
                    "platformStoreId": "65f1c0a2b3d4e5f6a7b8c9d0",
                    "environment": "Production",
                    "brand": null,
                    "isActive": true,
                    "capabilities": {
                      "supportsPrepTime": true,
                      "supportsRejectCodes": true,
                      "supportsStoreStatus": true,
                      "supportsItemAvailability": true,
                      "menuPush": "Full"
                    }
                  },
                  {
                    "id": "9b3e7d1a-4c6f-4a2b-8d5e-0f1a3c5e7b92",
                    "platform": "UberEats",
                    "platformStoreId": "0f6a3c1e-2b4d-4e8f-9a7c-5d3b1e9f7a24",
                    "environment": "Production",
                    "brand": null,
                    "isActive": true,
                    "capabilities": {
                      "supportsPrepTime": false,
                      "supportsRejectCodes": false,
                      "supportsStoreStatus": false,
                      "supportsItemAvailability": false,
                      "menuPush": "None"
                    }
                  }
                ]
              }
            }
          },
          "401": {
            "description": "Missing, unknown or inactive restaurant key. Empty body."
          }
        }
      }
    },
    "/pos/catalog/push": {
      "post": {
        "tags": [
          "Catalog"
        ],
        "summary": "Push the full menu",
        "description": "Upload the canonical menu once; RapidHub converts it for every target platform and returns one job per connection. `connectionIds` omitted or empty = all active connections. Prices are minor units (cents), VAT-inclusive. A full push replaces the menu on the platform. Wolt allows one menu upload per venue every 15 minutes. Authenticate with `X-Restaurant-Key: <restaurant key>` (or `Authorization: Bearer <restaurant key>`).",
        "operationId": "pushCatalog",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PushRequestDto"
              },
              "example": {
                "connectionIds": [
                  "7d0c2a4e-5b1f-4c8e-9a3d-2f6b8e1c4a70"
                ],
                "currency": "EUR",
                "products": [
                  {
                    "sku": "BURGER",
                    "name": "Cheeseburger",
                    "description": "Beef patty, cheddar, pickles, house sauce",
                    "priceMinor": 890,
                    "taxRatePercent": 7,
                    "imageUrl": "https://example.com/img/cheeseburger.jpg",
                    "modifierGroupIds": [
                      "G-EXTRAS"
                    ],
                    "allergens": [
                      "Gluten",
                      "Milk",
                      "Mustard"
                    ]
                  },
                  {
                    "sku": "FRIES",
                    "name": "Fries",
                    "priceMinor": 350,
                    "taxRatePercent": 7,
                    "foodInfo": {
                      "vegetarian": true
                    }
                  },
                  {
                    "sku": "COLA",
                    "name": "Cola 0.33 l",
                    "priceMinor": 250,
                    "taxRatePercent": 19,
                    "depositMinor": 25,
                    "depositTaxRatePercent": 19,
                    "foodInfo": {
                      "additives": [
                        "Mit Farbstoff(en)",
                        "Koffeinhaltig"
                      ],
                      "caffeineMgPer100ml": 10,
                      "netAmount": 330,
                      "netUnit": "ml",
                      "prepackaged": true
                    }
                  },
                  {
                    "sku": "MENU1",
                    "name": "Burger Menu",
                    "description": "Cheeseburger, fries and a drink",
                    "priceMinor": 1190,
                    "taxRatePercent": 7,
                    "modifierGroupIds": [
                      "STEP-MAIN",
                      "STEP-SIDE",
                      "STEP-DRINK"
                    ]
                  }
                ],
                "categories": [
                  {
                    "id": "cat-burgers",
                    "name": "Burgers",
                    "itemSkus": [
                      "BURGER"
                    ]
                  },
                  {
                    "id": "cat-sides",
                    "name": "Sides",
                    "itemSkus": [
                      "FRIES"
                    ]
                  },
                  {
                    "id": "cat-drinks",
                    "name": "Drinks",
                    "itemSkus": [
                      "COLA"
                    ]
                  },
                  {
                    "id": "cat-menus",
                    "name": "Menus",
                    "itemSkus": [
                      "MENU1"
                    ]
                  }
                ],
                "modifierGroups": [
                  {
                    "id": "G-EXTRAS",
                    "name": "Extras",
                    "minSelect": 0,
                    "maxSelect": 3,
                    "options": [
                      {
                        "sku": "X-CHEESE",
                        "name": "Extra cheese",
                        "priceMinor": 100
                      },
                      {
                        "sku": "X-ONION",
                        "name": "Onions",
                        "priceMinor": 0
                      }
                    ]
                  },
                  {
                    "id": "STEP-MAIN",
                    "name": "Main",
                    "minSelect": 1,
                    "maxSelect": 1,
                    "options": [
                      {
                        "sku": "BURGER",
                        "name": "Cheeseburger",
                        "priceMinor": 0,
                        "isDefault": true
                      }
                    ]
                  },
                  {
                    "id": "STEP-SIDE",
                    "name": "Side",
                    "minSelect": 1,
                    "maxSelect": 1,
                    "options": [
                      {
                        "sku": "FRIES",
                        "name": "Fries",
                        "priceMinor": 0,
                        "isDefault": true
                      }
                    ]
                  },
                  {
                    "id": "STEP-DRINK",
                    "name": "Drink",
                    "minSelect": 1,
                    "maxSelect": 1,
                    "options": [
                      {
                        "sku": "COLA",
                        "name": "Cola 0.33 l",
                        "priceMinor": 0,
                        "isDefault": true,
                        "depositMinor": 25
                      }
                    ]
                  }
                ]
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PushRequestDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PushRequestDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "One job per target connection.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogPushResponse"
                },
                "example": {
                  "jobs": [
                    {
                      "connectionId": "7d0c2a4e-5b1f-4c8e-9a3d-2f6b8e1c4a70",
                      "platform": "Mock",
                      "platformStoreId": "mock-3f9a1c2e",
                      "jobId": "a1f3c5e7-0b2d-4f6a-8c9e-1d3f5a7b9c0e",
                      "status": "Completed",
                      "externalJobId": null,
                      "itemCount": 4,
                      "lastError": null
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or inactive restaurant key. Empty body."
          },
          "400": {
            "description": "`empty_catalog` (no products) or `no_target_connections`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "empty_catalog"
                }
              }
            }
          },
          "404": {
            "description": "A `connectionIds` entry is not a connection of this tenant.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unknown_connection"
                }
              }
            }
          }
        }
      }
    },
    "/pos/catalog/items": {
      "post": {
        "tags": [
          "Catalog"
        ],
        "summary": "Update item price / availability",
        "description": "Change availability (sold out / back) and optionally the price of items or modifier options by SKU, without re-sending the menu. On rate-limited platforms (Wolt: one item update per venue every 15 minutes) updates are merged in a durable queue and sent when allowed — the job is then `Queued` with `lastError: \"queued until <time>\"`. Authenticate with `X-Restaurant-Key: <restaurant key>` (or `Authorization: Bearer <restaurant key>`).",
        "operationId": "updateItems",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ItemUpdatesRequestDto"
              },
              "example": {
                "items": [
                  {
                    "sku": "COLA",
                    "available": false
                  },
                  {
                    "sku": "BURGER",
                    "available": true,
                    "priceMinor": 950
                  },
                  {
                    "sku": "X-CHEESE",
                    "available": false
                  }
                ]
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/ItemUpdatesRequestDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/ItemUpdatesRequestDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "One job per target connection.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogPushResponse"
                },
                "example": {
                  "jobs": [
                    {
                      "connectionId": "7d0c2a4e-5b1f-4c8e-9a3d-2f6b8e1c4a70",
                      "platform": "Mock",
                      "platformStoreId": "mock-3f9a1c2e",
                      "jobId": "d2e4f6a8-1b3c-4d5e-9f7a-2b4c6d8e0f13",
                      "status": "Completed",
                      "externalJobId": null,
                      "itemCount": 3,
                      "lastError": null
                    },
                    {
                      "connectionId": "1e5a9c3f-7b2d-4e8a-b6c0-4f9d2a7e1c38",
                      "platform": "Wolt",
                      "platformStoreId": "65f1c0a2b3d4e5f6a7b8c9d0",
                      "jobId": "e3f5a7b9-2c4d-4e6f-8a0b-3c5d7e9f1a24",
                      "status": "Queued",
                      "externalJobId": null,
                      "itemCount": 3,
                      "lastError": "queued until 2026-10-15T12:45:00.0000000+00:00"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or inactive restaurant key. Empty body."
          },
          "400": {
            "description": "`no_items` or `no_target_connections`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "no_items"
                }
              }
            }
          },
          "404": {
            "description": "Unknown connection.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unknown_connection"
                }
              }
            }
          }
        }
      }
    },
    "/pos/catalog/{connectionId}/store-status": {
      "post": {
        "tags": [
          "Store"
        ],
        "summary": "Open or pause the store",
        "description": "`open: false` pauses the store on the platform (optionally until `closedUntil`, ISO 8601); `open: true` brings it back online. Sandbox stores on real platforms often start offline — set them online once after the first menu push. Authenticate with `X-Restaurant-Key: <restaurant key>` (or `Authorization: Bearer <restaurant key>`).",
        "operationId": "setStoreStatus",
        "parameters": [
          {
            "name": "connectionId",
            "in": "path",
            "description": "Connection id from `GET /pos/catalog/connections`.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StoreStatusDto"
              },
              "examples": {
                "pause": {
                  "summary": "Pause until 18:00 UTC",
                  "value": {
                    "open": false,
                    "closedUntil": "2026-10-15T18:00:00Z"
                  }
                },
                "pauseIndefinitely": {
                  "summary": "Pause without an end time",
                  "value": {
                    "open": false
                  }
                },
                "open": {
                  "summary": "Back online",
                  "value": {
                    "open": true
                  }
                }
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/StoreStatusDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/StoreStatusDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Job result — check `status`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StoreStatusChangeResult"
                },
                "example": {
                  "jobId": "c4d6e8f0-1a3b-4c5d-8e7f-9a0b1c2d3e4f",
                  "status": "Completed",
                  "lastError": null
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or inactive restaurant key. Empty body."
          },
          "404": {
            "description": "Not a connection of this tenant. Empty body."
          }
        }
      },
      "get": {
        "tags": [
          "Store"
        ],
        "summary": "Read store status and opening hours",
        "description": "Reads the store's open/online flags and weekly opening hours from the platform. Authenticate with `X-Restaurant-Key: <restaurant key>` (or `Authorization: Bearer <restaurant key>`).",
        "operationId": "getStoreStatus",
        "parameters": [
          {
            "name": "connectionId",
            "in": "path",
            "description": "Connection id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Store status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StoreStatus"
                },
                "example": {
                  "isOpen": true,
                  "isOnline": true,
                  "openingTimes": [
                    {
                      "openDay": "MONDAY",
                      "openTime": "11:00",
                      "closeDay": "MONDAY",
                      "closeTime": "22:00"
                    },
                    {
                      "openDay": "FRIDAY",
                      "openTime": "11:00",
                      "closeDay": "SATURDAY",
                      "closeTime": "02:00"
                    },
                    {
                      "openDay": "SUNDAY",
                      "openTime": "00:00",
                      "closeDay": "SUNDAY",
                      "closeTime": "24:00"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or inactive restaurant key. Empty body."
          },
          "404": {
            "description": "Not a connection of this tenant. Empty body."
          },
          "501": {
            "description": "The platform does not support reading the store status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "not_supported"
                }
              }
            }
          },
          "502": {
            "description": "The platform call failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "platform_error",
                  "detail": "Response status code does not indicate success: 503"
                }
              }
            }
          }
        }
      }
    },
    "/pos/catalog/{connectionId}/opening-hours": {
      "put": {
        "tags": [
          "Store"
        ],
        "summary": "Replace weekly opening hours",
        "description": "Replaces the full weekly schedule on the platform. Send every window of the week; days `MONDAY`…`SUNDAY`, times `HH:mm`, `24:00` allowed as closing time. Overnight: `{FRIDAY 18:00 → SATURDAY 02:00}`. Open 24/7: seven windows `00:00 → 24:00`. Authenticate with `X-Restaurant-Key: <restaurant key>` (or `Authorization: Bearer <restaurant key>`).",
        "operationId": "setOpeningHours",
        "parameters": [
          {
            "name": "connectionId",
            "in": "path",
            "description": "Connection id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/OpeningWindowDto"
                }
              },
              "example": [
                {
                  "openDay": "MONDAY",
                  "openTime": "11:00",
                  "closeDay": "MONDAY",
                  "closeTime": "22:00"
                },
                {
                  "openDay": "TUESDAY",
                  "openTime": "11:00",
                  "closeDay": "TUESDAY",
                  "closeTime": "22:00"
                },
                {
                  "openDay": "WEDNESDAY",
                  "openTime": "11:00",
                  "closeDay": "WEDNESDAY",
                  "closeTime": "22:00"
                },
                {
                  "openDay": "THURSDAY",
                  "openTime": "11:00",
                  "closeDay": "THURSDAY",
                  "closeTime": "22:00"
                },
                {
                  "openDay": "FRIDAY",
                  "openTime": "11:00",
                  "closeDay": "SATURDAY",
                  "closeTime": "02:00"
                },
                {
                  "openDay": "SATURDAY",
                  "openTime": "12:00",
                  "closeDay": "SUNDAY",
                  "closeTime": "02:00"
                },
                {
                  "openDay": "SUNDAY",
                  "openTime": "12:00",
                  "closeDay": "SUNDAY",
                  "closeTime": "21:30"
                }
              ]
            },
            "text/json": {
              "schema": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/OpeningWindowDto"
                }
              }
            },
            "application/*+json": {
              "schema": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/OpeningWindowDto"
                }
              }
            }
          }
        },
        "responses": {
          "400": {
            "description": "A day name could not be parsed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "invalid_day",
                  "openDay": "MON",
                  "closeDay": "MON"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or inactive restaurant key. Empty body."
          },
          "204": {
            "description": "Saved on the platform."
          },
          "404": {
            "description": "Not a connection of this tenant. Empty body."
          },
          "501": {
            "description": "The platform does not support opening hours.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "not_supported"
                }
              }
            }
          },
          "502": {
            "description": "The platform call failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "platform_error",
                  "detail": "opening times: 400 …"
                }
              }
            }
          }
        }
      }
    },
    "/pos/catalog/jobs/{jobId}": {
      "get": {
        "tags": [
          "Catalog"
        ],
        "summary": "Get a push job",
        "description": "Current status of a menu, item or store job. For asynchronous platforms RapidHub asks the platform for the latest status. Authenticate with `X-Restaurant-Key: <restaurant key>` (or `Authorization: Bearer <restaurant key>`).",
        "operationId": "getJob",
        "parameters": [
          {
            "name": "jobId",
            "in": "path",
            "description": "`jobId` from a push response.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Job.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobStatus"
                },
                "example": {
                  "jobId": "a1f3c5e7-0b2d-4f6a-8c9e-1d3f5a7b9c0e",
                  "platform": "Mock",
                  "status": "Completed",
                  "externalJobId": null,
                  "itemCount": 4,
                  "lastError": null
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or inactive restaurant key. Empty body."
          },
          "404": {
            "description": "Unknown job or a job of another tenant. Empty body."
          }
        }
      }
    },
    "/pos/orders/active": {
      "get": {
        "tags": [
          "Orders"
        ],
        "summary": "List active orders",
        "description": "All orders of the restaurant that are not in a terminal state (`Delivered`, `Rejected`, `Canceled`), oldest first. Call it on start-up and after every (re)connect of the SignalR stream to catch up on events you missed. The body is a JSON array of canonical orders (not wrapped). Authenticate with `X-Restaurant-Key: <restaurant key>` (or `Authorization: Bearer <restaurant key>`).",
        "operationId": "listActiveOrders",
        "responses": {
          "200": {
            "description": "Active canonical orders (JSON array).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CanonicalOrder"
                  }
                },
                "example": [
                  {
                    "schemaVersion": "2.0",
                    "id": "3c9d2b7e-41a8-4f0b-8e6d-5a1c9f2e7b40",
                    "platform": "Mock",
                    "platformOrderId": "mock-261015123000-1",
                    "displayCode": "TEST-1",
                    "environment": "Staging",
                    "tenantRef": "acme-pos:tenant:1042",
                    "store": {
                      "platformStoreId": "mock-3f9a1c2e",
                      "restaurantId": "b5e1f0d2-8c3a-4e7b-9f1d-6a2c4e8b0d13"
                    },
                    "channel": "Delivery",
                    "fulfillment": {
                      "courierType": "Platform",
                      "promisedFor": "2026-10-15T13:00:00+00:00",
                      "deliveryAddress": {
                        "street": "Teststraße",
                        "houseNumber": "1",
                        "zip": "10115",
                        "city": "Berlin",
                        "lat": 52.5321,
                        "lng": 13.3849,
                        "notes": "2nd floor, ring \"Sandbox\""
                      }
                    },
                    "state": "Received",
                    "platformState": "mock:created",
                    "timestamps": {
                      "placedAt": "2026-10-15T12:30:00+00:00",
                      "receivedByHubAt": "2026-10-15T12:30:00+00:00"
                    },
                    "customer": {
                      "name": "Test Customer",
                      "phone": "+49 30 1234567"
                    },
                    "items": [
                      {
                        "lineId": "1",
                        "ref": {
                          "sku": "BURGER",
                          "plu": "BURGER",
                          "platformItemId": "mock-item-BURGER"
                        },
                        "name": "Cheeseburger",
                        "quantity": 1,
                        "unitPrice": {
                          "amountMinor": 890,
                          "currency": "EUR"
                        },
                        "modifiers": [
                          {
                            "name": "Extra cheese",
                            "quantity": 1,
                            "price": {
                              "amountMinor": 100,
                              "currency": "EUR"
                            },
                            "groupName": "Extras",
                            "sku": "X-CHEESE",
                            "taxRatePercent": 7
                          },
                          {
                            "name": "Ohne Onions",
                            "quantity": 1,
                            "price": {
                              "amountMinor": 0,
                              "currency": "EUR"
                            },
                            "groupName": "Extras",
                            "sku": "NO-X-ONION",
                            "taxRatePercent": 7
                          }
                        ],
                        "taxRatePercent": 7,
                        "lineTotal": {
                          "amountMinor": 990,
                          "currency": "EUR"
                        }
                      },
                      {
                        "lineId": "2",
                        "ref": {
                          "sku": "FRIES",
                          "plu": "FRIES",
                          "platformItemId": "mock-item-FRIES"
                        },
                        "name": "Fries",
                        "quantity": 1,
                        "unitPrice": {
                          "amountMinor": 350,
                          "currency": "EUR"
                        },
                        "modifiers": [ ],
                        "taxRatePercent": 7,
                        "lineTotal": {
                          "amountMinor": 350,
                          "currency": "EUR"
                        }
                      }
                    ],
                    "amounts": {
                      "currency": "EUR",
                      "itemsSubtotal": 1340,
                      "deliveryFee": 299,
                      "discounts": 0,
                      "tip": 200,
                      "tax": 88,
                      "total": 1839
                    },
                    "payment": {
                      "status": "PaidOnline",
                      "method": "mock_online"
                    },
                    "discounts": [ ],
                    "fees": [
                      {
                        "type": "delivery",
                        "amount": {
                          "amountMinor": 299,
                          "currency": "EUR"
                        },
                        "venueRevenue": false,
                        "taxRatePercent": 19,
                        "platformType": "mock_delivery_fee"
                      }
                    ],
                    "timing": "Instant",
                    "acceptance": "ByVenue",
                    "platformExtras": {
                      "mock": "1",
                      "venueName": "RapidHub Sandbox",
                      "courierName": "Test Courier"
                    },
                    "signatureVerified": true,
                    "notificationOnly": false,
                    "acceptDeadline": "2026-10-15T12:33:00+00:00"
                  }
                ]
              }
            }
          },
          "401": {
            "description": "Missing, unknown or inactive restaurant key. Empty body."
          }
        }
      }
    },
    "/pos/orders/{orderId}/state": {
      "post": {
        "tags": [
          "Orders"
        ],
        "summary": "Change the state of an order",
        "description": "Moves the order to `state` and forwards the change to the platform (accept, reject, ready, picked up, delivered …). Only transitions allowed by the canonical state machine are accepted (409 otherwise — also when the order is already in `state`). Use `Dispatched` for delivery orders only. A successful call also produces an `Updated` OrderEvent on the SignalR stream. Authenticate with `X-Restaurant-Key: <restaurant key>` (or `Authorization: Bearer <restaurant key>`).",
        "operationId": "setOrderState",
        "parameters": [
          {
            "name": "orderId",
            "in": "path",
            "description": "RapidHub order id (`id` of the canonical order — not `platformOrderId`).",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StateDto"
              },
              "examples": {
                "accept": {
                  "summary": "Accept with a preparation time",
                  "value": {
                    "state": "Accepted",
                    "prepTimeMinutes": 20
                  }
                },
                "acceptDefault": {
                  "summary": "Accept with the platform's default time",
                  "value": {
                    "state": "Accepted"
                  }
                },
                "reject": {
                  "summary": "Reject with a reason code",
                  "value": {
                    "state": "Rejected",
                    "rejectCode": "ItemsUnavailable",
                    "reason": "Sorry, the cheeseburger is sold out."
                  }
                },
                "preparing": {
                  "summary": "Kitchen started",
                  "value": {
                    "state": "Preparing"
                  }
                },
                "ready": {
                  "summary": "Ready for pickup",
                  "value": {
                    "state": "Ready"
                  }
                },
                "dispatched": {
                  "summary": "Own courier left (delivery only)",
                  "value": {
                    "state": "Dispatched"
                  }
                },
                "delivered": {
                  "summary": "Handed over / delivered",
                  "value": {
                    "state": "Delivered"
                  }
                },
                "cancel": {
                  "summary": "Cancel an accepted order",
                  "value": {
                    "state": "Canceled",
                    "reason": "Customer asked to cancel"
                  }
                }
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/StateDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/StateDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "State changed (locally and on the platform).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StateChangeResult"
                },
                "example": {
                  "status": "ok",
                  "state": "Accepted"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or inactive restaurant key. Empty body."
          },
          "400": {
            "description": "Invalid body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalidState": {
                    "summary": "Unknown state",
                    "value": {
                      "error": "invalid_state",
                      "state": "Done"
                    }
                  },
                  "invalidRejectCode": {
                    "summary": "Unknown reject code",
                    "value": {
                      "error": "invalid_reject_code",
                      "rejectCode": "Busy"
                    }
                  },
                  "invalidPrepTime": {
                    "summary": "prepTimeMinutes outside 0–240",
                    "value": {
                      "error": "invalid_prep_time",
                      "prepTimeMinutes": 300
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The order belongs to another restaurant.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "not_your_order"
                }
              }
            }
          },
          "404": {
            "description": "No order with this id. Empty body."
          },
          "409": {
            "description": "Transition not allowed, or the platform reports a different state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalidTransition": {
                    "summary": "Not allowed by the state machine",
                    "value": {
                      "error": "invalid_transition",
                      "detail": "Received→Ready"
                    }
                  },
                  "stateMismatch": {
                    "summary": "Platform is in another state — refresh the order",
                    "value": {
                      "error": "state_mismatch",
                      "upstream": "CANCELLED"
                    }
                  }
                }
              }
            }
          },
          "502": {
            "description": "The platform call failed; the local state is unchanged. Safe to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "failed",
                  "detail": "accept: {\"message\":\"Order not found\"}"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "AcceptanceMode": {
        "enum": [
          "ByVenue",
          "AutoByPlatform"
        ],
        "type": "string"
      },
      "CanonicalOrder": {
        "required": [
          "acceptance",
          "amounts",
          "channel",
          "environment",
          "fulfillment",
          "id",
          "items",
          "payment",
          "platform",
          "platformOrderId",
          "schemaVersion",
          "state",
          "store",
          "tenantRef",
          "timestamps",
          "timing"
        ],
        "type": "object",
        "properties": {
          "schemaVersion": {
            "type": "string",
            "description": "Order schema version (\"2.0\"). New optional fields can appear at any time — ignore unknown properties."
          },
          "id": {
            "type": "string",
            "description": "RapidHub order id — stable; use it for state changes and de-duplication.",
            "format": "uuid"
          },
          "platform": {
            "$ref": "#/components/schemas/PlatformKey"
          },
          "platformOrderId": {
            "type": "string",
            "description": "The platform's order id.",
            "nullable": true
          },
          "displayCode": {
            "type": "string",
            "description": "Short code shown to the customer and courier.",
            "nullable": true
          },
          "environment": {
            "$ref": "#/components/schemas/PlatformEnvironment"
          },
          "tenantRef": {
            "type": "string",
            "description": "Tenant reference.",
            "nullable": true
          },
          "store": {
            "$ref": "#/components/schemas/StoreRef"
          },
          "channel": {
            "$ref": "#/components/schemas/OrderChannel"
          },
          "fulfillment": {
            "$ref": "#/components/schemas/Fulfillment"
          },
          "state": {
            "$ref": "#/components/schemas/CanonicalOrderState"
          },
          "platformState": {
            "type": "string",
            "description": "Raw platform status (audit only).",
            "nullable": true
          },
          "timestamps": {
            "$ref": "#/components/schemas/OrderTimestamps"
          },
          "customer": {
            "$ref": "#/components/schemas/Customer"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrderItem"
            },
            "description": "Order lines.",
            "nullable": true
          },
          "amounts": {
            "$ref": "#/components/schemas/OrderAmounts"
          },
          "payment": {
            "$ref": "#/components/schemas/OrderPayment"
          },
          "discounts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrderDiscount"
            },
            "description": "v2: discount breakdown (sum = amounts.discounts). Missing = no breakdown from the platform.",
            "nullable": true
          },
          "fees": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrderFee"
            },
            "description": "v2: fee breakdown (delivery, service, small_order, priority, delivery_discount, other).",
            "nullable": true
          },
          "timing": {
            "$ref": "#/components/schemas/OrderTiming"
          },
          "acceptance": {
            "$ref": "#/components/schemas/AcceptanceMode"
          },
          "platformExtras": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "v2: display-only details (courierName, cashGiven, venueName, mock …). Never branch business logic on them; `mock: \"1\"` marks sandbox orders.",
            "nullable": true
          },
          "signatureVerified": {
            "type": "boolean",
            "description": "The platform webhook signature was verified."
          },
          "notificationOnly": {
            "type": "boolean",
            "description": "Internal; false for orders delivered to the POS."
          },
          "acceptDeadline": {
            "type": "string",
            "description": "Accept before this time or the platform cancels the order. Missing = no known deadline.",
            "format": "date-time",
            "nullable": true
          },
          "rejectReason": {
            "type": "string",
            "description": "Reason text of a rejection / cancellation.",
            "nullable": true,
            "readOnly": true
          },
          "rawPayload": {
            "type": "string",
            "description": "The platform's original JSON (debugging only; do not parse, contains personal data).",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "CanonicalOrderState": {
        "enum": [
          "Received",
          "Accepted",
          "Preparing",
          "Ready",
          "Dispatched",
          "Delivered",
          "Rejected",
          "Canceled"
        ],
        "type": "string"
      },
      "CatalogJob": {
        "required": [
          "connectionId",
          "platform",
          "platformStoreId",
          "jobId",
          "status",
          "itemCount"
        ],
        "type": "object",
        "properties": {
          "connectionId": {
            "type": "string",
            "description": "Target connection.",
            "format": "uuid"
          },
          "platform": {
            "enum": [
              "Glovo",
              "UberEats",
              "Deliveroo",
              "Wolt",
              "JustEatTakeaway",
              "DeliveryHero",
              "Mock"
            ],
            "type": "string",
            "description": "Platform of the connection."
          },
          "platformStoreId": {
            "type": "string",
            "description": "Store id on the platform."
          },
          "jobId": {
            "type": "string",
            "description": "RapidHub job id — poll `GET /pos/catalog/jobs/{jobId}`.",
            "format": "uuid"
          },
          "status": {
            "enum": [
              "Pending",
              "Queued",
              "InProgress",
              "Completed",
              "Failed"
            ],
            "type": "string",
            "description": "Pending → Queued / InProgress → Completed or Failed. `Completed` can still carry warnings in `lastError`."
          },
          "externalJobId": {
            "type": "string",
            "description": "The platform's own job id (asynchronous platforms).",
            "nullable": true
          },
          "itemCount": {
            "type": "integer",
            "description": "Number of items in the push.",
            "format": "int32"
          },
          "lastError": {
            "type": "string",
            "description": "Error, warnings (`… Warnings: a; b`) or `queued until <ISO time>` for rate-limited item updates.",
            "nullable": true
          }
        },
        "description": "Result of a push to one connection."
      },
      "CatalogPushResponse": {
        "required": [
          "jobs"
        ],
        "type": "object",
        "properties": {
          "jobs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CatalogJob"
            }
          }
        },
        "description": "One job per target connection. Partial failure is normal — check every job."
      },
      "CategoryDto": {
        "required": [
          "id",
          "itemSkus",
          "name"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Your category id."
          },
          "name": {
            "type": "string",
            "description": "Category name."
          },
          "itemSkus": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "SKUs in display order."
          }
        },
        "additionalProperties": false
      },
      "ConnectorCapabilities": {
        "type": "object",
        "properties": {
          "supportsPrepTime": {
            "type": "boolean"
          },
          "supportsRejectCodes": {
            "type": "boolean"
          },
          "supportsStoreStatus": {
            "type": "boolean"
          },
          "supportsItemAvailability": {
            "type": "boolean"
          },
          "menuPush": {
            "$ref": "#/components/schemas/MenuPushMode"
          }
        },
        "additionalProperties": false
      },
      "CourierType": {
        "enum": [
          "Platform",
          "Marketplace",
          "Self"
        ],
        "type": "string"
      },
      "Customer": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "nullable": true
          },
          "phone": {
            "type": "string",
            "nullable": true
          },
          "note": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "DeliveryAddress": {
        "type": "object",
        "properties": {
          "street": {
            "type": "string",
            "nullable": true
          },
          "houseNumber": {
            "type": "string",
            "nullable": true
          },
          "zip": {
            "type": "string",
            "nullable": true
          },
          "city": {
            "type": "string",
            "nullable": true
          },
          "lat": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "lng": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "notes": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "DiscountFundedBy": {
        "enum": [
          "Unknown",
          "Venue",
          "Platform",
          "Shared"
        ],
        "type": "string"
      },
      "DiscountScope": {
        "enum": [
          "Basket",
          "Item",
          "Delivery"
        ],
        "type": "string"
      },
      "Error": {
        "required": [
          "error"
        ],
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "Error code, e.g. `invalid_state`, `invalid_transition`, `state_mismatch`, `not_your_order`, `empty_catalog`, `unknown_connection`, `no_target_connections`, `no_items`, `invalid_day`, `not_supported`, `platform_error`, `failed`."
          },
          "detail": {
            "type": "string",
            "description": "Human-readable detail (`invalid_transition`: `\"Received→Ready\"`; `failed`/`platform_error`: upstream message).",
            "nullable": true
          },
          "upstream": {
            "type": "string",
            "description": "`state_mismatch` only: the platform's own (raw) state of the order.",
            "nullable": true
          }
        },
        "description": "Error body. `error` is a stable machine-readable code; the other fields depend on the code."
      },
      "FoodInfo": {
        "type": "object",
        "properties": {
          "additives": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true
          },
          "alcoholPercent": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "caffeineMgPer100ml": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "netAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "netUnit": {
            "type": "string",
            "nullable": true
          },
          "prepackaged": {
            "type": "boolean",
            "nullable": true
          },
          "ingredients": {
            "type": "string",
            "nullable": true
          },
          "producer": {
            "type": "string",
            "nullable": true
          },
          "distributor": {
            "type": "string",
            "nullable": true
          },
          "countryOfOrigin": {
            "type": "string",
            "nullable": true
          },
          "storage": {
            "type": "string",
            "nullable": true
          },
          "nutrition": {
            "$ref": "#/components/schemas/Nutrition"
          },
          "vegetarian": {
            "type": "boolean",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "Fulfillment": {
        "type": "object",
        "properties": {
          "courierType": {
            "$ref": "#/components/schemas/CourierType"
          },
          "promisedFor": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "preparationDeadline": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "deliveryAddress": {
            "$ref": "#/components/schemas/DeliveryAddress"
          }
        },
        "additionalProperties": false
      },
      "ItemDto": {
        "required": [
          "name",
          "priceMinor",
          "sku"
        ],
        "type": "object",
        "properties": {
          "sku": {
            "type": "string",
            "description": "Your POS id. Comes back in orders as `items[].ref.sku` — must be unique."
          },
          "name": {
            "type": "string",
            "description": "Display name."
          },
          "priceMinor": {
            "type": "integer",
            "description": "Gross price (VAT included) in minor units, e.g. 890 = 8.90.",
            "format": "int64"
          },
          "available": {
            "type": "boolean",
            "description": "false = sold out / hidden. Default true."
          },
          "description": {
            "type": "string",
            "description": "Description shown on the platform.",
            "nullable": true
          },
          "barcode": {
            "type": "string",
            "description": "EAN/GTIN (retail platforms).",
            "nullable": true
          },
          "categoryName": {
            "type": "string",
            "description": "Category name when `categories` is not sent.",
            "nullable": true
          },
          "imageUrl": {
            "type": "string",
            "description": "Public http(s) image URL.",
            "nullable": true
          },
          "taxRatePercent": {
            "type": "number",
            "description": "VAT rate in percent (7, 19, 10 …). Required by most platforms.",
            "format": "double",
            "nullable": true
          },
          "modifierGroupIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Ids of `modifierGroups` offered with this item (in order). A combo menu = an item whose groups are its steps.",
            "nullable": true
          },
          "depositMinor": {
            "type": "integer",
            "description": "Deposit (e.g. German Pfand) per unit in minor units — NOT included in `priceMinor`.",
            "format": "int64",
            "nullable": true
          },
          "depositTaxRatePercent": {
            "type": "number",
            "description": "VAT of the deposit; null = the item's rate.",
            "format": "double",
            "nullable": true
          },
          "allergens": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Allergen names in any language (e.g. \"Gluten\", \"Milch\"); converted to each platform's codes.",
            "nullable": true
          },
          "foodInfo": {
            "$ref": "#/components/schemas/FoodInfo"
          }
        },
        "additionalProperties": false
      },
      "ItemRef": {
        "type": "object",
        "properties": {
          "sku": {
            "type": "string",
            "nullable": true
          },
          "plu": {
            "type": "string",
            "nullable": true
          },
          "barcode": {
            "type": "string",
            "nullable": true
          },
          "platformItemId": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "ItemUpdateDto": {
        "required": [
          "sku"
        ],
        "type": "object",
        "properties": {
          "sku": {
            "type": "string",
            "description": "SKU from the last menu push."
          },
          "available": {
            "type": "boolean",
            "description": "New availability. Default true."
          },
          "priceMinor": {
            "type": "integer",
            "description": "New gross price in minor units; omit to keep the price.",
            "format": "int64",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "ItemUpdatesRequestDto": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "connectionIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Target connections; omit for all active connections.",
            "nullable": true
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ItemUpdateDto"
            },
            "description": "Updates by SKU (items or modifier options)."
          }
        },
        "additionalProperties": false
      },
      "JobStatus": {
        "required": [
          "jobId",
          "platform",
          "status",
          "itemCount"
        ],
        "type": "object",
        "properties": {
          "jobId": {
            "type": "string",
            "description": "RapidHub job id.",
            "format": "uuid"
          },
          "platform": {
            "enum": [
              "Glovo",
              "UberEats",
              "Deliveroo",
              "Wolt",
              "JustEatTakeaway",
              "DeliveryHero",
              "Mock"
            ],
            "type": "string",
            "description": "Platform."
          },
          "status": {
            "enum": [
              "Pending",
              "Queued",
              "InProgress",
              "Completed",
              "Failed"
            ],
            "type": "string",
            "description": "Pending → Queued / InProgress → Completed or Failed. `Completed` can still carry warnings in `lastError`."
          },
          "externalJobId": {
            "type": "string",
            "description": "The platform's own job id.",
            "nullable": true
          },
          "itemCount": {
            "type": "integer",
            "description": "Number of items in the push.",
            "format": "int32"
          },
          "lastError": {
            "type": "string",
            "description": "Error or warnings.",
            "nullable": true
          }
        },
        "description": "Current state of a push job."
      },
      "MenuPushMode": {
        "enum": [
          "None",
          "Full",
          "UpdatesOnly"
        ],
        "type": "string"
      },
      "ModifierGroupDto": {
        "required": [
          "id",
          "maxSelect",
          "minSelect",
          "name",
          "options"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Group id referenced by `modifierGroupIds`."
          },
          "name": {
            "type": "string",
            "description": "Group name shown to the customer (e.g. \"Extras\", \"Choose your drink\")."
          },
          "minSelect": {
            "type": "integer",
            "description": "Minimum selections (0 = optional, 1 = required step).",
            "format": "int32"
          },
          "maxSelect": {
            "type": "integer",
            "description": "Maximum selections.",
            "format": "int32"
          },
          "options": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ModifierOptionDto"
            },
            "description": "Selectable options."
          }
        },
        "additionalProperties": false
      },
      "ModifierOptionDto": {
        "required": [
          "name",
          "priceMinor",
          "sku"
        ],
        "type": "object",
        "properties": {
          "sku": {
            "type": "string",
            "description": "Your POS id of the option. Comes back as `modifiers[].sku`. For combo steps use the product SKU."
          },
          "name": {
            "type": "string",
            "description": "Option name."
          },
          "priceMinor": {
            "type": "integer",
            "description": "Surcharge in minor units (0 = free / included).",
            "format": "int64"
          },
          "available": {
            "type": "boolean",
            "description": "false = option sold out."
          },
          "imageUrl": {
            "type": "string",
            "nullable": true
          },
          "taxRatePercent": {
            "type": "number",
            "description": "VAT of the option; null = the item's rate.",
            "format": "double",
            "nullable": true
          },
          "depositMinor": {
            "type": "integer",
            "description": "Deposit per unit (e.g. the drink inside a combo).",
            "format": "int64",
            "nullable": true
          },
          "allergens": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true
          },
          "isDefault": {
            "type": "boolean",
            "description": "Pre-selected option (combo step default).",
            "nullable": true
          },
          "maxQuantity": {
            "type": "integer",
            "description": "How many times this option can be picked; null = 1.",
            "format": "int32",
            "nullable": true
          },
          "foodInfo": {
            "$ref": "#/components/schemas/FoodInfo"
          }
        },
        "additionalProperties": false
      },
      "Money": {
        "required": [
          "amountMinor",
          "currency"
        ],
        "type": "object",
        "properties": {
          "amountMinor": {
            "type": "integer",
            "description": "Amount in minor units (cents): 890 = 8.90.",
            "format": "int64"
          },
          "currency": {
            "type": "string",
            "description": "ISO 4217 currency code."
          }
        },
        "additionalProperties": false
      },
      "Nutrition": {
        "type": "object",
        "properties": {
          "per100ml": {
            "type": "boolean"
          },
          "energyKj": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "energyKcal": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "fat": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "saturatedFat": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "carbohydrate": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "sugar": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "protein": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "salt": {
            "type": "number",
            "format": "double",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "OpeningWindow": {
        "required": [
          "openDay",
          "openTime",
          "closeDay",
          "closeTime"
        ],
        "type": "object",
        "properties": {
          "openDay": {
            "enum": [
              "MONDAY",
              "TUESDAY",
              "WEDNESDAY",
              "THURSDAY",
              "FRIDAY",
              "SATURDAY",
              "SUNDAY"
            ],
            "type": "string",
            "description": "Day the window opens. Case-insensitive."
          },
          "openTime": {
            "type": "string",
            "description": "Opening time, `HH:mm` (24 h, store local time).",
            "example": "11:00"
          },
          "closeDay": {
            "enum": [
              "MONDAY",
              "TUESDAY",
              "WEDNESDAY",
              "THURSDAY",
              "FRIDAY",
              "SATURDAY",
              "SUNDAY"
            ],
            "type": "string",
            "description": "Day the window closes (next day for overnight windows). Case-insensitive."
          },
          "closeTime": {
            "type": "string",
            "description": "Closing time, `HH:mm`; `24:00` allowed.",
            "example": "22:00"
          }
        },
        "description": "One weekly opening window. Overnight windows end on the next day; `24:00` closes at midnight."
      },
      "OpeningWindowDto": {
        "required": [
          "closeDay",
          "closeTime",
          "openDay",
          "openTime"
        ],
        "type": "object",
        "properties": {
          "openDay": {
            "type": "string",
            "description": "MONDAY … SUNDAY."
          },
          "openTime": {
            "type": "string",
            "description": "HH:mm."
          },
          "closeDay": {
            "type": "string",
            "description": "MONDAY … SUNDAY (next day for overnight windows)."
          },
          "closeTime": {
            "type": "string",
            "description": "HH:mm; 24:00 allowed."
          }
        },
        "additionalProperties": false
      },
      "OrderAmounts": {
        "type": "object",
        "properties": {
          "currency": {
            "type": "string",
            "nullable": true
          },
          "itemsSubtotal": {
            "type": "integer",
            "format": "int64"
          },
          "deliveryFee": {
            "type": "integer",
            "format": "int64"
          },
          "discounts": {
            "type": "integer",
            "format": "int64"
          },
          "tip": {
            "type": "integer",
            "format": "int64"
          },
          "tax": {
            "type": "integer",
            "format": "int64"
          },
          "total": {
            "type": "integer",
            "format": "int64"
          }
        },
        "additionalProperties": false
      },
      "OrderChannel": {
        "enum": [
          "Delivery",
          "Pickup",
          "DineIn"
        ],
        "type": "string"
      },
      "OrderDeposit": {
        "type": "object",
        "properties": {
          "unitAmount": {
            "$ref": "#/components/schemas/Money"
          },
          "taxRatePercent": {
            "type": "number",
            "format": "double",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "OrderDiscount": {
        "type": "object",
        "properties": {
          "scope": {
            "$ref": "#/components/schemas/DiscountScope"
          },
          "amount": {
            "$ref": "#/components/schemas/Money"
          },
          "fundedBy": {
            "$ref": "#/components/schemas/DiscountFundedBy"
          },
          "lineId": {
            "type": "string",
            "nullable": true
          },
          "name": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "OrderEventEnvelope": {
        "required": [
          "eventKind",
          "order",
          "orderId",
          "platform",
          "platformOrderId"
        ],
        "type": "object",
        "properties": {
          "eventKind": {
            "type": "string",
            "description": "Received | Updated | Canceled."
          },
          "platform": {
            "type": "string",
            "description": "Platform of the order."
          },
          "platformOrderId": {
            "type": "string",
            "description": "The platform's order id."
          },
          "orderId": {
            "type": "string",
            "description": "RapidHub order id (= order.id).",
            "format": "uuid"
          },
          "order": {
            "type": "string",
            "description": "The full canonical order as a JSON string (parse it again)."
          }
        },
        "additionalProperties": false
      },
      "OrderFee": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "nullable": true
          },
          "amount": {
            "$ref": "#/components/schemas/Money"
          },
          "venueRevenue": {
            "type": "boolean",
            "nullable": true
          },
          "taxRatePercent": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "platformType": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "OrderItem": {
        "type": "object",
        "properties": {
          "lineId": {
            "type": "string",
            "nullable": true
          },
          "ref": {
            "$ref": "#/components/schemas/ItemRef"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "quantity": {
            "type": "integer",
            "format": "int32"
          },
          "unitPrice": {
            "$ref": "#/components/schemas/Money"
          },
          "modifiers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrderModifier"
            },
            "nullable": true
          },
          "note": {
            "type": "string",
            "nullable": true
          },
          "taxRatePercent": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "deposit": {
            "$ref": "#/components/schemas/OrderDeposit"
          },
          "discount": {
            "$ref": "#/components/schemas/Money"
          },
          "lineTotal": {
            "$ref": "#/components/schemas/Money"
          }
        },
        "additionalProperties": false
      },
      "OrderModifier": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "nullable": true
          },
          "quantity": {
            "type": "integer",
            "format": "int32"
          },
          "price": {
            "$ref": "#/components/schemas/Money"
          },
          "groupName": {
            "type": "string",
            "nullable": true
          },
          "sku": {
            "type": "string",
            "nullable": true
          },
          "taxRatePercent": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "deposit": {
            "$ref": "#/components/schemas/OrderDeposit"
          }
        },
        "additionalProperties": false
      },
      "OrderPayment": {
        "type": "object",
        "properties": {
          "status": {
            "$ref": "#/components/schemas/PaymentStatus"
          },
          "method": {
            "type": "string",
            "nullable": true
          },
          "cashToCollect": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "OrderTimestamps": {
        "type": "object",
        "properties": {
          "placedAt": {
            "type": "string",
            "format": "date-time"
          },
          "receivedByHubAt": {
            "type": "string",
            "format": "date-time"
          },
          "acceptedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "OrderTiming": {
        "enum": [
          "Instant",
          "Scheduled"
        ],
        "type": "string"
      },
      "PaymentStatus": {
        "enum": [
          "Unknown",
          "PaidOnline",
          "CashOnDelivery",
          "CardOnDelivery"
        ],
        "type": "string"
      },
      "PlatformConnection": {
        "required": [
          "id",
          "platform",
          "platformStoreId",
          "environment",
          "isActive",
          "capabilities"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Connection id — use it as `connectionIds[]` and in `/pos/catalog/{connectionId}/…`.",
            "format": "uuid"
          },
          "platform": {
            "enum": [
              "Glovo",
              "UberEats",
              "Deliveroo",
              "Wolt",
              "JustEatTakeaway",
              "DeliveryHero",
              "Mock"
            ],
            "type": "string",
            "description": "Platform."
          },
          "platformStoreId": {
            "type": "string",
            "description": "The store/venue id on the platform."
          },
          "environment": {
            "enum": [
              "Staging",
              "Production"
            ],
            "type": "string",
            "description": "Staging or Production."
          },
          "brand": {
            "type": "string",
            "description": "Brand within a multi-brand platform (Delivery Hero: e.g. `FO_AT`; Just Eat Takeaway: `Lieferando` …).",
            "nullable": true
          },
          "isActive": {
            "type": "boolean",
            "description": "Inactive connections are skipped when `connectionIds` is omitted."
          },
          "capabilities": {
            "$ref": "#/components/schemas/ConnectorCapabilities"
          }
        },
        "description": "A platform connection (one store on one platform) of the tenant that owns the restaurant key."
      },
      "PlatformEnvironment": {
        "enum": [
          "Staging",
          "Production"
        ],
        "type": "string"
      },
      "PlatformKey": {
        "enum": [
          "Glovo",
          "UberEats",
          "Deliveroo",
          "Wolt",
          "JustEatTakeaway",
          "DeliveryHero",
          "Mock"
        ],
        "type": "string"
      },
      "PushRequestDto": {
        "required": [
          "products"
        ],
        "type": "object",
        "properties": {
          "connectionIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Target connections. Omit or send [] for all active connections of the tenant.",
            "nullable": true
          },
          "currency": {
            "type": "string",
            "description": "ISO 4217 code for every price in the request. Default EUR.",
            "nullable": true
          },
          "products": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ItemDto"
            },
            "description": "Sellable items (also every product used as a combo step). At least one."
          },
          "categories": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CategoryDto"
            },
            "description": "Menu categories; `itemSkus` sets the order. Omit to group by `products[].categoryName`.",
            "nullable": true
          },
          "modifierGroups": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ModifierGroupDto"
            },
            "description": "Modifier groups (extras, removals, combo steps) referenced by `products[].modifierGroupIds`.",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "StateChangeResult": {
        "required": [
          "status",
          "state"
        ],
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "Always `ok`."
          },
          "state": {
            "enum": [
              "Received",
              "Accepted",
              "Preparing",
              "Ready",
              "Dispatched",
              "Delivered",
              "Rejected",
              "Canceled"
            ],
            "type": "string",
            "description": "The new canonical state."
          }
        },
        "description": "Successful state change."
      },
      "StateDto": {
        "required": [
          "state"
        ],
        "type": "object",
        "properties": {
          "state": {
            "enum": [
              "Accepted",
              "Preparing",
              "Ready",
              "Dispatched",
              "Delivered",
              "Rejected",
              "Canceled"
            ],
            "type": "string",
            "description": "Target canonical state: Accepted, Rejected, Preparing, Ready, Dispatched, Delivered, Canceled (case-insensitive)."
          },
          "reason": {
            "type": "string",
            "description": "Free text for Rejected/Canceled. Some platforms show it to the customer (Wolt). Optional.",
            "nullable": true
          },
          "rejectCode": {
            "enum": [
              "Other",
              "ItemsUnavailable",
              "TooBusy",
              "Closing",
              "Closed"
            ],
            "type": "string",
            "description": "Canonical reject code for Rejected: Other, ItemsUnavailable, TooBusy, Closing, Closed. Mapped to the platform's own code.",
            "nullable": true
          },
          "prepTimeMinutes": {
            "maximum": 240,
            "minimum": 0,
            "type": "integer",
            "description": "For Accepted: preparation time in minutes (0–240). Omit to keep the platform's default/estimate.",
            "format": "int32",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "StoreRef": {
        "type": "object",
        "properties": {
          "platformStoreId": {
            "type": "string",
            "nullable": true
          },
          "restaurantId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "StoreStatus": {
        "required": [
          "openingTimes"
        ],
        "type": "object",
        "properties": {
          "isOpen": {
            "type": "boolean",
            "description": "Open according to the platform (within opening hours and not paused). Null = the platform did not say.",
            "nullable": true
          },
          "isOnline": {
            "type": "boolean",
            "description": "Online / accepting orders (not paused). Null = unknown.",
            "nullable": true
          },
          "openingTimes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OpeningWindow"
            },
            "description": "Weekly opening hours (Wolt and Mock; empty on other platforms)."
          }
        },
        "description": "Store status as reported by the platform."
      },
      "StoreStatusChangeResult": {
        "required": [
          "jobId",
          "status"
        ],
        "type": "object",
        "properties": {
          "jobId": {
            "type": "string",
            "description": "Job id.",
            "format": "uuid"
          },
          "status": {
            "enum": [
              "Pending",
              "Queued",
              "InProgress",
              "Completed",
              "Failed"
            ],
            "type": "string",
            "description": "Pending → Queued / InProgress → Completed or Failed. `Completed` can still carry warnings in `lastError`."
          },
          "lastError": {
            "type": "string",
            "description": "Platform error, if any.",
            "nullable": true
          }
        },
        "description": "Result of a store status change. HTTP 200 is returned even when the platform refused — check `status`."
      },
      "StoreStatusDto": {
        "required": [
          "open"
        ],
        "type": "object",
        "properties": {
          "open": {
            "type": "boolean",
            "description": "true = online, false = paused."
          },
          "closedUntil": {
            "type": "string",
            "description": "When pausing: ISO 8601 time the store comes back by itself. Omit for an indefinite pause.",
            "nullable": true
          }
        },
        "additionalProperties": false
      }
    },
    "securitySchemes": {
      "RestaurantKey": {
        "type": "apiKey",
        "description": "The restaurant key shown once when a restaurant is created in the RapidHub dashboard (48 hex characters). One key = one restaurant (branch).",
        "name": "X-Restaurant-Key",
        "in": "header"
      },
      "Bearer": {
        "type": "http",
        "description": "Alternative to X-Restaurant-Key: `Authorization: Bearer <restaurant key>`. X-Restaurant-Key wins when both are sent.",
        "scheme": "bearer"
      }
    }
  },
  "security": [
    {
      "RestaurantKey": [ ]
    },
    {
      "Bearer": [ ]
    }
  ],
  "tags": [
    {
      "name": "Orders",
      "description": "Active orders (REST fallback for the SignalR stream) and order state changes."
    },
    {
      "name": "Catalog",
      "description": "Platform connections, menu push, item price/availability updates and push jobs."
    },
    {
      "name": "Store",
      "description": "Store open/paused status and weekly opening hours on the platform."
    }
  ]
}