{
  "openapi": "3.0.4",
  "info": {
    "title": "Acme Webshop API",
    "description": "The storefront API for the Acme Webshop \u2014 products, orders, and customer\nlookups for the web shop and the mobile app.\n",
    "contact": {
      "email": "platform@acmewebshop.com"
    },
    "version": "2.0.0"
  },
  "servers": [
    {
      "url": "https://api.acmewebshop.com/v2"
    }
  ],
  "paths": {
    "/auth/login": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Log in and obtain a bearer token",
        "description": "Exchanges email and password for a bearer token. Tokens are valid for one hour (\u0060expiresIn: 3600\u0060) \u2014 request a new one when it expires instead of retrying on 401.",
        "operationId": "login",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LoginRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LoginResponse"
                }
              }
            }
          },
          "401": {
            "description": "Invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-routebase-folder-path": "Auth"
      }
    },
    "/customers/{customerId}": {
      "get": {
        "tags": [
          "Customers"
        ],
        "summary": "Get a customer",
        "description": "Returns a customer profile including the default shipping address.",
        "operationId": "getCustomer",
        "parameters": [
          {
            "name": "customerId",
            "in": "path",
            "description": "Public ID of the customer.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-routebase-folder-path": "Customers"
      }
    },
    "/orders": {
      "get": {
        "tags": [
          "Orders"
        ],
        "summary": "List orders",
        "description": "Returns the shop\u0027s orders, newest first. Filter by \u0060status\u0060 to reconcile open orders.",
        "operationId": "listOrders",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Page number, starting at 1.",
            "schema": {
              "minimum": 1,
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Return only orders in this lifecycle state.",
            "schema": {
              "$ref": "#/components/schemas/OrderStatus"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrderList"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-routebase-folder-path": "Orders"
      },
      "post": {
        "tags": [
          "Orders"
        ],
        "summary": "Create an order",
        "description": "Places an order for the given items and returns the priced order.",
        "operationId": "createOrder",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateOrderRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-routebase-folder-path": "Orders"
      }
    },
    "/orders/{orderId}": {
      "get": {
        "tags": [
          "Orders"
        ],
        "summary": "Get an order",
        "description": "Returns a single order including line items and the server-calculated totals.",
        "operationId": "getOrder",
        "parameters": [
          {
            "name": "orderId",
            "in": "path",
            "description": "Public ID of the order.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-routebase-folder-path": "Orders"
      }
    },
    "/orders/{orderId}/cancel": {
      "post": {
        "tags": [
          "Orders"
        ],
        "summary": "Cancel an order",
        "description": "Cancels an order that has not shipped yet.",
        "operationId": "cancelOrder",
        "parameters": [
          {
            "name": "orderId",
            "in": "path",
            "description": "Public ID of the order.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Cancelled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                }
              }
            }
          },
          "409": {
            "description": "Order already shipped",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-routebase-folder-path": "Orders"
      }
    },
    "/products": {
      "get": {
        "tags": [
          "Products"
        ],
        "summary": "List products",
        "description": "Returns the catalog with prices and stock, paginated.",
        "operationId": "listProducts",
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "description": "Filter by category slug, e.g. \u0060garden-tools\u0060.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Page number, starting at 1.",
            "schema": {
              "minimum": 1,
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "description": "Items per page (max 100).",
            "schema": {
              "maximum": 100,
              "minimum": 1,
              "type": "integer",
              "default": 20
            }
          },
          {
            "name": "sort",
            "in": "query",
            "description": "Sort order for the result list.",
            "schema": {
              "enum": [
                "price-asc",
                "price-desc",
                "name-asc",
                "newest"
              ],
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProductList"
                }
              }
            }
          }
        },
        "x-routebase-folder-path": "Products"
      },
      "post": {
        "tags": [
          "Products"
        ],
        "summary": "Create a product",
        "description": "Adds a product to the catalog. The SKU must be unique across the shop; new products start with the given stock level (default 0) and are sellable immediately.",
        "operationId": "createProduct",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateProductRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Product"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-routebase-folder-path": "Products"
      }
    },
    "/products/{productId}": {
      "get": {
        "tags": [
          "Products"
        ],
        "summary": "Get a product",
        "description": "Returns a single product with price, stock and category. Public \u2014 no token required.",
        "operationId": "getProduct",
        "parameters": [
          {
            "name": "productId",
            "in": "path",
            "description": "Public ID of the product.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Product"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-routebase-folder-path": "Products"
      },
      "patch": {
        "tags": [
          "Products"
        ],
        "summary": "Update a product",
        "description": "Partially updates a product \u2014 only the fields present in the body change. Typical uses: price corrections and stock adjustments.",
        "operationId": "updateProduct",
        "parameters": [
          {
            "name": "productId",
            "in": "path",
            "description": "Public ID of the product.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateProductRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Product"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-routebase-folder-path": "Products"
      },
      "delete": {
        "tags": [
          "Products"
        ],
        "summary": "Delete a product",
        "description": "Removes a product from the catalog. Existing orders keep their line items; the product simply stops being sellable.",
        "operationId": "deleteProduct",
        "parameters": [
          {
            "name": "productId",
            "in": "path",
            "description": "Public ID of the product.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-routebase-folder-path": "Products"
      }
    }
  },
  "components": {
    "schemas": {
      "LoginRequest": {
        "required": [
          "email",
          "password"
        ],
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "description": "Email address of the shop account.",
            "format": "email",
            "example": "shop-admin@acmewebshop.com"
          },
          "password": {
            "type": "string",
            "description": "The account password. Sent only over TLS.",
            "format": "password"
          }
        },
        "description": "Credentials for \u0060POST /auth/login\u0060."
      },
      "LoginResponse": {
        "required": [
          "token",
          "expiresIn"
        ],
        "type": "object",
        "properties": {
          "token": {
            "type": "string",
            "description": "Bearer token for subsequent requests."
          },
          "expiresIn": {
            "type": "integer",
            "description": "Token lifetime in seconds.",
            "example": 3600
          }
        },
        "description": "Bearer token issued after a successful login."
      },
      "Product": {
        "required": [
          "id",
          "sku",
          "name",
          "category",
          "price",
          "stock"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Public ID of the product.",
            "format": "uuid",
            "example": "4f7c2a1e-9b3d-4e8f-a2c6-1d5b8e3f7a90"
          },
          "sku": {
            "type": "string",
            "description": "Merchant SKU \u2014 unique across the shop.",
            "example": "GT-1041"
          },
          "name": {
            "type": "string",
            "description": "Display name shown in the shop.",
            "example": "Hand Trowel, Ash Handle"
          },
          "description": {
            "type": "string",
            "description": "Short marketing copy for product pages.",
            "example": "Forged carbon-steel blade with a smoothed ash handle."
          },
          "category": {
            "type": "string",
            "description": "Category slug, e.g. \u0060garden-tools\u0060.",
            "example": "garden-tools"
          },
          "price": {
            "type": "number",
            "description": "Unit price in EUR, the shop\u0027s base currency.",
            "format": "double",
            "example": 12.9
          },
          "stock": {
            "type": "integer",
            "description": "Units currently in stock.",
            "example": 240
          },
          "imageUrl": {
            "type": "string",
            "description": "CDN URL of the primary product image.",
            "format": "uri",
            "example": "https://cdn.acmewebshop.com/p/gt-1041.jpg"
          },
          "createdAt": {
            "type": "string",
            "description": "When the product was added to the catalog.",
            "format": "date-time"
          }
        },
        "description": "A sellable catalog item with price and stock."
      },
      "CreateProductRequest": {
        "required": [
          "sku",
          "name",
          "category",
          "price"
        ],
        "type": "object",
        "properties": {
          "sku": {
            "type": "string",
            "description": "Merchant SKU \u2014 must be unique across the shop."
          },
          "name": {
            "type": "string",
            "description": "Display name shown in the shop."
          },
          "description": {
            "type": "string",
            "description": "Short marketing copy for product pages."
          },
          "category": {
            "type": "string",
            "description": "Category slug the product is listed under."
          },
          "price": {
            "type": "number",
            "description": "Unit price in EUR.",
            "format": "double"
          },
          "stock": {
            "type": "integer",
            "description": "Initial stock level.",
            "default": 0
          },
          "imageUrl": {
            "type": "string",
            "description": "CDN URL of the primary product image.",
            "format": "uri"
          }
        },
        "description": "Payload for creating a catalog product."
      },
      "ProductList": {
        "required": [
          "items",
          "page",
          "pageSize",
          "total"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Product"
            },
            "description": "The products on this page."
          },
          "page": {
            "type": "integer",
            "description": "Current page number.",
            "example": 1
          },
          "pageSize": {
            "type": "integer",
            "description": "Requested page size.",
            "example": 20
          },
          "total": {
            "type": "integer",
            "description": "Total number of products across all pages.",
            "example": 68
          }
        },
        "description": "One page of the product catalog, with the pagination envelope."
      },
      "OrderStatus": {
        "enum": [
          "pending",
          "paid",
          "shipped",
          "cancelled"
        ],
        "type": "string",
        "description": "Lifecycle state of an order: pending \u2192 paid \u2192 shipped, or cancelled."
      },
      "UpdateProductRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "New display name."
          },
          "description": {
            "type": "string",
            "description": "New marketing copy."
          },
          "category": {
            "type": "string",
            "description": "New category slug."
          },
          "price": {
            "type": "number",
            "description": "New unit price in EUR.",
            "format": "double"
          },
          "stock": {
            "type": "integer",
            "description": "New stock level."
          },
          "imageUrl": {
            "type": "string",
            "description": "New primary image URL.",
            "format": "uri"
          }
        },
        "description": "Partial update for a product \u2014 every field is optional."
      },
      "Order": {
        "required": [
          "id",
          "status",
          "items",
          "subtotal",
          "shippingCost",
          "total",
          "createdAt"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Public ID of the order.",
            "format": "uuid",
            "example": "e4a7c1d8-5f2b-4c9e-8a3d-6b9f2e5c1a77"
          },
          "status": {
            "$ref": "#/components/schemas/OrderStatus"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrderItem"
            },
            "description": "The ordered line items."
          },
          "subtotal": {
            "type": "number",
            "description": "Sum of all line items in EUR.",
            "format": "double",
            "example": 25.8
          },
          "shippingCost": {
            "type": "number",
            "description": "Shipping cost in EUR.",
            "format": "double",
            "example": 4.95
          },
          "total": {
            "type": "number",
            "description": "Grand total in EUR \u2014 subtotal plus shipping.",
            "format": "double",
            "example": 30.75
          },
          "createdAt": {
            "type": "string",
            "description": "When the order was placed.",
            "format": "date-time"
          }
        },
        "description": "A customer order with line items and server-calculated totals."
      },
      "OrderItem": {
        "required": [
          "productId",
          "name",
          "quantity",
          "unitPrice"
        ],
        "type": "object",
        "properties": {
          "productId": {
            "type": "string",
            "description": "Public ID of the ordered product.",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "Product name at order time, denormalized for history.",
            "example": "Hand Trowel, Ash Handle"
          },
          "quantity": {
            "minimum": 1,
            "type": "integer",
            "description": "Ordered units.",
            "example": 2
          },
          "unitPrice": {
            "type": "number",
            "description": "Price per unit in EUR, captured at order time.",
            "format": "double",
            "example": 12.9
          }
        },
        "description": "A single order line: product, quantity and the unit price captured at order time."
      },
      "Address": {
        "required": [
          "street",
          "city",
          "postalCode",
          "country"
        ],
        "type": "object",
        "properties": {
          "street": {
            "type": "string",
            "description": "Street and house number.",
            "example": "Gartenweg 12"
          },
          "city": {
            "type": "string",
            "description": "City or town.",
            "example": "Freiburg"
          },
          "postalCode": {
            "type": "string",
            "description": "Postal code.",
            "example": "79098"
          },
          "country": {
            "type": "string",
            "description": "ISO 3166-1 alpha-2",
            "example": "DE"
          }
        },
        "description": "A postal address used for shipping."
      },
      "CreateOrderRequest": {
        "required": [
          "customerId",
          "items",
          "shippingAddress"
        ],
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string",
            "description": "Public ID of the ordering customer.",
            "format": "uuid"
          },
          "items": {
            "minItems": 1,
            "type": "array",
            "items": {
              "required": [
                "productId",
                "quantity"
              ],
              "type": "object",
              "properties": {
                "productId": {
                  "type": "string",
                  "description": "Public ID of the product to order.",
                  "format": "uuid"
                },
                "quantity": {
                  "minimum": 1,
                  "type": "integer",
                  "description": "Units to order."
                }
              }
            },
            "description": "Line items to order \u2014 at least one."
          },
          "shippingAddress": {
            "$ref": "#/components/schemas/Address"
          }
        },
        "description": "Payload for placing an order \u2014 items are priced server-side."
      },
      "Customer": {
        "required": [
          "id",
          "email",
          "firstName",
          "lastName"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Public ID of the customer.",
            "format": "uuid"
          },
          "email": {
            "type": "string",
            "description": "Email address \u2014 unique per account.",
            "format": "email",
            "example": "lena.gruber@example.com"
          },
          "firstName": {
            "type": "string",
            "description": "Given name.",
            "example": "Lena"
          },
          "lastName": {
            "type": "string",
            "description": "Family name.",
            "example": "Gruber"
          },
          "defaultAddress": {
            "$ref": "#/components/schemas/Address"
          }
        },
        "description": "A registered shop customer."
      },
      "Error": {
        "required": [
          "code",
          "message"
        ],
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "description": "Stable, machine-readable error code \u2014 branch on this, not on the message.",
            "example": "NOT_FOUND"
          },
          "message": {
            "type": "string",
            "description": "Human-readable explanation, intended for logs and debugging.",
            "example": "The requested resource does not exist."
          }
        },
        "description": "Uniform error body returned by all 4xx responses."
      },
      "OrderList": {
        "required": [
          "items",
          "page",
          "pageSize",
          "total"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Order"
            },
            "description": "The orders on this page, newest first."
          },
          "page": {
            "type": "integer",
            "description": "Current page number.",
            "example": 1
          },
          "pageSize": {
            "type": "integer",
            "description": "Requested page size.",
            "example": 20
          },
          "total": {
            "type": "integer",
            "description": "Total number of orders across all pages.",
            "example": 412
          }
        },
        "description": "One page of orders, with the pagination envelope."
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      }
    }
  },
  "tags": [
    {
      "name": "Auth",
      "description": "Sign-in and token handling"
    },
    {
      "name": "Products",
      "description": "Catalog \u2014 products, categories, stock"
    },
    {
      "name": "Orders",
      "description": "Checkout and order lifecycle"
    },
    {
      "name": "Customers",
      "description": "Customer profiles"
    }
  ],
  "x-routebase-folders": [
    {
      "name": "Auth",
      "children": []
    },
    {
      "name": "Customers",
      "children": []
    },
    {
      "name": "Orders",
      "children": []
    },
    {
      "name": "Products",
      "children": []
    }
  ]
}