{
  "openapi": "3.1.0",
  "info": {
    "title": "machs-dir-selbst Public API",
    "version": "1.0.0",
    "description": "\nIntegrate external systems with your company's data in the **machs-dir-selbst**\nsolar planner — customers, projects, offers, materials, services and more — over\na predictable REST API, and react to changes in near-real-time via webhooks.\n\n## Base URL\n\n```\nhttps://api.machsdirselbst.solar\n```\n\n## Authentication\n\nEvery request is authenticated with a **company-scoped API key** sent as a\nBearer token (`Authorization: Bearer mds_live_...`). A key carries a set of\n**scopes** that gate what it can read and write. Keys are created under\n**Settings → API Keys** in the planner. See the *Authentication* guide for the\nscope matrix.\n\nAll data is automatically scoped to the key's company — you can only ever read\nor change your own company's records.\n\n## What you can build\n\n- **CRM / ERP sync** — mirror customers, projects and offers into your own\n  systems, and keep them in sync with webhooks.\n- **Programmatic offer creation** — build a complete offer (roofs, modules,\n  inverters, batteries, wallboxes, services, costs) in one atomic call, then\n  manage individual line items later.\n- **Event-driven automation** — trigger workflows when an offer is accepted, a\n  project is created, or a customer changes, without polling.\n- **Reporting** — pull analytics and pipeline data into a BI tool.\n- **Document generation** — fetch grid-operator (Netzbetreiber) paperwork and\n  string plans for an offer.\n\n## Conventions\n\n- **IDs** are returned as strings.\n- **Lists** accept `limit` (1–200), `offset`, `sort` (`field.asc` /\n  `field.desc`, whitelisted per resource) and per-field filters, and respond\n  with `{ data, total, limit, offset }` — offset pagination, so `total` lets\n  you page deterministically.\n- **Writes** use the resource's create/update endpoints; several resources\n  (customers, projects, offers) persist through atomic transactions so a partial\n  write can never leave inconsistent data.\n- **Errors** are `{ \"error\": \"<code>\", \"message\"?: \"<detail>\" }` with the\n  matching HTTP status: `400` validation, `401` authentication, `403`\n  missing scope, `404` not found in your company, `500` server error.\n",
    "contact": {
      "name": "machs-dir-selbst",
      "url": "https://machsdirselbst.solar"
    }
  },
  "servers": [
    {
      "url": "https://api.machsdirselbst.solar",
      "description": "Production"
    },
    {
      "url": "https://api.staging.machsdirselbst.solar",
      "description": "Staging"
    }
  ],
  "tags": [
    {
      "name": "Customers",
      "description": "The people you sell to. Create, update and list customers; a customer can own several projects."
    },
    {
      "name": "Projects",
      "description": "A solar installation site for a customer, including its roof configurations. Projects hold the offers you make."
    },
    {
      "name": "Offers",
      "description": "A quote for a project. Offers are built from materials, services and costs, move through DRAFT → PUBLISHED → ACCEPTED/DECLINED, and can be created as a complete nested document or edited piece by piece."
    },
    {
      "name": "Offer services",
      "description": "Additional services attached to an offer (e.g. installation, scaffolding). Manage them individually without re-sending the whole offer."
    },
    {
      "name": "Offer additional costs",
      "description": "Free-form line items on an offer (surcharges or rebates via a negative price)."
    },
    {
      "name": "Offer batteries",
      "description": "Battery products configured on an offer."
    },
    {
      "name": "Offer wallboxes",
      "description": "Wallbox (EV charger) products configured on an offer."
    },
    {
      "name": "Offer misc materials",
      "description": "Miscellaneous material line items on an offer."
    },
    {
      "name": "Offer requests",
      "description": "Incoming customer enquiries (Anfragen) that precede a detailed offer."
    },
    {
      "name": "PV modules",
      "description": "Photovoltaic modules. Their electrical characteristics drive string sizing, so they are required on write."
    },
    {
      "name": "Batteries",
      "description": "Battery storage products, AC- or DC-coupled."
    },
    {
      "name": "Inverters",
      "description": "Inverters. MPP trackers belong to the product; adding one to an offer creates a configuration row per tracker automatically."
    },
    {
      "name": "Wallboxes",
      "description": "Wallboxes (EV chargers)."
    },
    {
      "name": "Equipment",
      "description": "Accessories that attach to modules, inverters, batteries, wallboxes or subconstruction."
    },
    {
      "name": "Misc materials",
      "description": "Miscellaneous catalog items. They need no manufacturer and can be pre-selected onto offers."
    },
    {
      "name": "Subconstruction",
      "description": "Mounting systems (Unterkonstruktion), typically per roof type."
    },
    {
      "name": "Emergency power",
      "description": "Emergency power products offered alongside an inverter."
    },
    {
      "name": "Manufacturers",
      "description": "Manufacturers your materials belong to."
    },
    {
      "name": "Services",
      "description": "Reusable services you can add to offers."
    },
    {
      "name": "Analytics",
      "description": "Read-only reporting over your own data: offers and requests over time, request conversion rates, customer demographics, and what you sold or still have sitting in open offers. Everything is scoped to your company."
    },
    {
      "name": "Documents",
      "description": "Generate paperwork for an offer on demand: the grid operator's (Netzbetreiber) registration forms, and the string plan. Nothing is stored — each call renders from the offer as it stands."
    },
    {
      "name": "Company",
      "description": "Your own company profile (support contact, address, branding)."
    },
    {
      "name": "Webhooks",
      "description": "Events we POST to your endpoints when data changes, so you can react without polling. Each event shares an envelope (`id`, `type`, `createdAt`, `data`); `data` is a compact set of the affected record's key fields — fetch the full object via the REST API. Deliveries are signed per Standard Webhooks; see the Webhooks guide for verification, retries and setup."
    }
  ],
  "x-tagGroups": [
    {
      "name": "Customers",
      "tags": [
        "Customers"
      ]
    },
    {
      "name": "Projects",
      "tags": [
        "Projects"
      ]
    },
    {
      "name": "Offer requests",
      "tags": [
        "Offer requests"
      ]
    },
    {
      "name": "Offers",
      "tags": [
        "Offers",
        "Offer services",
        "Offer additional costs",
        "Offer batteries",
        "Offer wallboxes",
        "Offer misc materials"
      ]
    },
    {
      "name": "Materials",
      "tags": [
        "PV modules",
        "Batteries",
        "Inverters",
        "Wallboxes",
        "Equipment",
        "Misc materials",
        "Subconstruction",
        "Emergency power"
      ]
    },
    {
      "name": "Manufacturers",
      "tags": [
        "Manufacturers"
      ]
    },
    {
      "name": "Services",
      "tags": [
        "Services"
      ]
    },
    {
      "name": "Documents",
      "tags": [
        "Documents"
      ]
    },
    {
      "name": "Analytics",
      "tags": [
        "Analytics"
      ]
    },
    {
      "name": "Company",
      "tags": [
        "Company"
      ]
    },
    {
      "name": "Webhooks",
      "tags": [
        "Webhooks"
      ]
    }
  ],
  "webhooks": {
    "project.created": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "project.created",
        "description": "A new project was created for one of your customers.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProjectWebhookEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Return any 2xx to acknowledge receipt. A non-2xx response, or one slower than 10s, is treated as a failed delivery and retried."
          }
        }
      }
    },
    "project.updated": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "project.updated",
        "description": "A project's fields changed. Re-fetch the project to get the current state.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProjectWebhookEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Return any 2xx to acknowledge receipt. A non-2xx response, or one slower than 10s, is treated as a failed delivery and retried."
          }
        }
      }
    },
    "offer.accepted": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "offer.accepted",
        "description": "A customer accepted an offer — a good trigger to start fulfilment or invoicing.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OfferAcceptedWebhookEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Return any 2xx to acknowledge receipt. A non-2xx response, or one slower than 10s, is treated as a failed delivery and retried."
          }
        }
      }
    },
    "offer.state_changed": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "offer.state_changed",
        "description": "An offer moved between states (e.g. DRAFT → PUBLISHED → ACCEPTED/DECLINED). Includes both the previous and the new state.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OfferStateChangedWebhookEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Return any 2xx to acknowledge receipt. A non-2xx response, or one slower than 10s, is treated as a failed delivery and retried."
          }
        }
      }
    },
    "project.deleted": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "project.deleted",
        "description": "A project was deleted. Its offers go with it, so this is the last event you will see for any of them.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProjectWebhookEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Return any 2xx to acknowledge receipt. A non-2xx response, or one slower than 10s, is treated as a failed delivery and retried."
          }
        }
      }
    },
    "customer.created": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "customer.created",
        "description": "A customer was added.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CustomerWebhookEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Return any 2xx to acknowledge receipt. A non-2xx response, or one slower than 10s, is treated as a failed delivery and retried."
          }
        }
      }
    },
    "customer.updated": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "customer.updated",
        "description": "A customer's details changed. Only fields the API exposes count as a change — the offer and project counters do not raise this event.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CustomerWebhookEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Return any 2xx to acknowledge receipt. A non-2xx response, or one slower than 10s, is treated as a failed delivery and retried."
          }
        }
      }
    },
    "customer.deleted": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "customer.deleted",
        "description": "A customer was deleted.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CustomerWebhookEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Return any 2xx to acknowledge receipt. A non-2xx response, or one slower than 10s, is treated as a failed delivery and retried."
          }
        }
      }
    },
    "offer.created": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "offer.created",
        "description": "An offer was created. It starts as a DRAFT and is not visible to the customer yet.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OfferWebhookEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Return any 2xx to acknowledge receipt. A non-2xx response, or one slower than 10s, is treated as a failed delivery and retried."
          }
        }
      }
    },
    "offer.updated": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "offer.updated",
        "description": "An offer was edited. State transitions are reported by offer.state_changed instead, and a price recalculated from its line items does not raise this event.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OfferWebhookEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Return any 2xx to acknowledge receipt. A non-2xx response, or one slower than 10s, is treated as a failed delivery and retried."
          }
        }
      }
    },
    "offer.published": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "offer.published",
        "description": "An offer became visible to the customer. Fires alongside offer.state_changed for the transition to PUBLISHED.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OfferPublishedWebhookEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Return any 2xx to acknowledge receipt. A non-2xx response, or one slower than 10s, is treated as a failed delivery and retried."
          }
        }
      }
    },
    "offer.deleted": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "offer.deleted",
        "description": "An offer was deleted. Not sent when the offer disappears because its project was deleted — project.deleted covers that.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OfferWebhookEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Return any 2xx to acknowledge receipt. A non-2xx response, or one slower than 10s, is treated as a failed delivery and retried."
          }
        }
      }
    },
    "material.created": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "material.created",
        "description": "A product was added to your catalog. `materialType` says which catalog.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MaterialWebhookEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Return any 2xx to acknowledge receipt. A non-2xx response, or one slower than 10s, is treated as a failed delivery and retried."
          }
        }
      }
    },
    "material.updated": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "material.updated",
        "description": "A product was renamed, archived or moved to another manufacturer. A new purchase price alone does not raise this event.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MaterialWebhookEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Return any 2xx to acknowledge receipt. A non-2xx response, or one slower than 10s, is treated as a failed delivery and retried."
          }
        }
      }
    },
    "material.deleted": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "material.deleted",
        "description": "A product was removed from your catalog. Products in use by an offer cannot be deleted, only archived.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MaterialWebhookEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Return any 2xx to acknowledge receipt. A non-2xx response, or one slower than 10s, is treated as a failed delivery and retried."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "API Key",
        "description": "Pass your API key as a Bearer token: `Authorization: Bearer mds_live_...`. Keys are managed in the planner under Settings → API Keys."
      }
    },
    "schemas": {
      "ProjectWebhookEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique event/delivery id — use as an idempotency key.",
            "example": "a1b2c3d4-e5f6-4890-a1b2-c3d4e5f60789"
          },
          "type": {
            "type": "string",
            "description": "Event name.",
            "example": "project.created"
          },
          "createdAt": {
            "type": "string",
            "description": "When the event was sent (ISO 8601).",
            "example": "2026-05-20T12:34:56.789Z"
          },
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "The project id — fetch the full project via GET /v1/projects/{id}.",
                "example": "42"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ],
                "example": "Dach Müller"
              },
              "customerId": {
                "type": "string",
                "example": "77"
              },
              "createdAt": {
                "type": "string",
                "example": "2026-07-01T10:22:00Z"
              }
            },
            "required": [
              "id",
              "customerId",
              "createdAt"
            ]
          }
        },
        "required": [
          "id",
          "type",
          "createdAt",
          "data"
        ]
      },
      "OfferAcceptedWebhookEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique event/delivery id — use as an idempotency key.",
            "example": "a1b2c3d4-e5f6-4890-a1b2-c3d4e5f60789"
          },
          "type": {
            "type": "string",
            "description": "Event name.",
            "example": "offer.accepted"
          },
          "createdAt": {
            "type": "string",
            "description": "When the event was sent (ISO 8601).",
            "example": "2026-05-20T12:34:56.789Z"
          },
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "The offer id — fetch it via GET /v1/offers/{id}.",
                "example": "812"
              },
              "projectId": {
                "type": "string",
                "example": "41"
              },
              "state": {
                "type": "string",
                "description": "Always ACCEPTED for this event.",
                "example": "ACCEPTED"
              },
              "acceptedAt": {
                "type": "string",
                "example": "2026-05-20T12:34:56.789Z"
              },
              "createdAt": {
                "type": "string",
                "example": "2026-05-01T09:00:00Z"
              }
            },
            "required": [
              "id",
              "projectId",
              "state",
              "acceptedAt",
              "createdAt"
            ]
          }
        },
        "required": [
          "id",
          "type",
          "createdAt",
          "data"
        ]
      },
      "OfferStateChangedWebhookEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique event/delivery id — use as an idempotency key.",
            "example": "a1b2c3d4-e5f6-4890-a1b2-c3d4e5f60789"
          },
          "type": {
            "type": "string",
            "description": "Event name.",
            "example": "offer.state_changed"
          },
          "createdAt": {
            "type": "string",
            "description": "When the event was sent (ISO 8601).",
            "example": "2026-05-20T12:34:56.789Z"
          },
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "The offer id.",
                "example": "812"
              },
              "projectId": {
                "type": "string",
                "example": "41"
              },
              "previousState": {
                "type": "string",
                "description": "State before the change.",
                "example": "PUBLISHED"
              },
              "state": {
                "type": "string",
                "description": "The new state.",
                "example": "ACCEPTED"
              },
              "createdAt": {
                "type": "string",
                "example": "2026-05-01T09:00:00Z"
              }
            },
            "required": [
              "id",
              "projectId",
              "previousState",
              "state",
              "createdAt"
            ]
          }
        },
        "required": [
          "id",
          "type",
          "createdAt",
          "data"
        ]
      },
      "OfferWebhookEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique event/delivery id — use as an idempotency key.",
            "example": "a1b2c3d4-e5f6-4890-a1b2-c3d4e5f60789"
          },
          "type": {
            "type": "string",
            "description": "Event name.",
            "example": "offer.created"
          },
          "createdAt": {
            "type": "string",
            "description": "When the event was sent (ISO 8601).",
            "example": "2026-05-20T12:34:56.789Z"
          },
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "The offer id — fetch it via GET /v1/offers/{id}.",
                "example": "812"
              },
              "projectId": {
                "type": "string",
                "example": "41"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ],
                "example": "Angebot Satteldach"
              },
              "state": {
                "type": "string",
                "example": "DRAFT"
              },
              "isIndicationPrice": {
                "type": [
                  "boolean",
                  "null"
                ],
                "description": "Whether this is an indication rather than a detailed offer."
              },
              "createdAt": {
                "type": "string",
                "example": "2026-05-01T09:00:00Z"
              }
            },
            "required": [
              "id",
              "projectId",
              "state",
              "createdAt"
            ]
          }
        },
        "required": [
          "id",
          "type",
          "createdAt",
          "data"
        ]
      },
      "OfferPublishedWebhookEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique event/delivery id — use as an idempotency key.",
            "example": "a1b2c3d4-e5f6-4890-a1b2-c3d4e5f60789"
          },
          "type": {
            "type": "string",
            "description": "Event name.",
            "example": "offer.published"
          },
          "createdAt": {
            "type": "string",
            "description": "When the event was sent (ISO 8601).",
            "example": "2026-05-20T12:34:56.789Z"
          },
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "example": "812"
              },
              "projectId": {
                "type": "string",
                "example": "41"
              },
              "state": {
                "type": "string",
                "description": "Always PUBLISHED for this event.",
                "example": "PUBLISHED"
              },
              "validTo": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "When the published offer expires.",
                "example": "2026-08-15T00:00:00Z"
              },
              "createdAt": {
                "type": "string",
                "example": "2026-05-01T09:00:00Z"
              }
            },
            "required": [
              "id",
              "projectId",
              "state",
              "createdAt"
            ]
          }
        },
        "required": [
          "id",
          "type",
          "createdAt",
          "data"
        ]
      },
      "CustomerWebhookEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique event/delivery id — use as an idempotency key.",
            "example": "a1b2c3d4-e5f6-4890-a1b2-c3d4e5f60789"
          },
          "type": {
            "type": "string",
            "description": "Event name.",
            "example": "customer.created"
          },
          "createdAt": {
            "type": "string",
            "description": "When the event was sent (ISO 8601).",
            "example": "2026-05-20T12:34:56.789Z"
          },
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "The customer id — fetch the full record via GET /v1/customers/{id}.",
                "example": "77"
              },
              "firstName": {
                "type": [
                  "string",
                  "null"
                ],
                "example": "Anna"
              },
              "lastName": {
                "type": [
                  "string",
                  "null"
                ],
                "example": "Müller"
              },
              "email": {
                "type": [
                  "string",
                  "null"
                ],
                "example": "anna.mueller@example.com"
              },
              "createdAt": {
                "type": "string",
                "example": "2026-07-01T10:22:00Z"
              }
            },
            "required": [
              "id",
              "createdAt"
            ]
          }
        },
        "required": [
          "id",
          "type",
          "createdAt",
          "data"
        ]
      },
      "MaterialWebhookEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique event/delivery id — use as an idempotency key.",
            "example": "a1b2c3d4-e5f6-4890-a1b2-c3d4e5f60789"
          },
          "type": {
            "type": "string",
            "description": "Event name.",
            "example": "material.created"
          },
          "createdAt": {
            "type": "string",
            "description": "When the event was sent (ISO 8601).",
            "example": "2026-05-20T12:34:56.789Z"
          },
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "The material id — fetch it via GET /v1/materials/{type}/{id}.",
                "example": "56"
              },
              "materialType": {
                "type": "string",
                "description": "Which catalog the product belongs to: pv_module, battery, inverter, wallbox, equipment, misc, subconstruction or emergency_power.",
                "example": "pv_module"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ],
                "example": "Vitovolt 300-DG M440HC"
              },
              "archived": {
                "type": [
                  "boolean",
                  "null"
                ],
                "description": "Archived products stay on existing offers but are hidden from new ones."
              },
              "manufacturerId": {
                "type": [
                  "string",
                  "null"
                ],
                "example": "12"
              }
            },
            "required": [
              "id",
              "materialType"
            ]
          }
        },
        "required": [
          "id",
          "type",
          "createdAt",
          "data"
        ]
      },
      "Address": {
        "type": [
          "object",
          "null"
        ],
        "properties": {
          "zip": {
            "type": [
              "string",
              "null"
            ],
            "description": "Postal code.",
            "example": "80331"
          },
          "city": {
            "type": [
              "string",
              "null"
            ],
            "description": "City.",
            "example": "München"
          },
          "street": {
            "type": [
              "string",
              "null"
            ],
            "description": "Street name.",
            "example": "Marienplatz"
          },
          "streetNumber": {
            "type": [
              "string",
              "null"
            ],
            "description": "House number.",
            "example": "1"
          }
        },
        "description": "Installation site address."
      },
      "Project": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique project id.",
            "example": "42"
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Project name.",
            "example": "Dach Müller"
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Free-text notes."
          },
          "customerId": {
            "type": [
              "string",
              "null"
            ],
            "description": "The customer this project belongs to.",
            "example": "77"
          },
          "state": {
            "type": [
              "string",
              "null"
            ],
            "description": "Project pipeline state."
          },
          "address": {
            "$ref": "#/components/schemas/Address"
          },
          "customerFullName": {
            "type": [
              "string",
              "null"
            ],
            "example": "Anna Müller"
          },
          "companyMemberName": {
            "type": [
              "string",
              "null"
            ],
            "description": "The assigned company member (Architekt/Berater)."
          },
          "companySiteName": {
            "type": [
              "string",
              "null"
            ],
            "description": "The company site the project belongs to."
          },
          "offersCount": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Total offers on this project.",
            "example": 3
          },
          "activeOffersCount": {
            "type": [
              "integer",
              "null"
            ],
            "example": 1
          },
          "acceptedOfferId": {
            "type": [
              "string",
              "null"
            ],
            "description": "The accepted offer, if any."
          },
          "createdAt": {
            "type": "string",
            "example": "2026-07-01T10:22:00Z"
          }
        },
        "required": [
          "id",
          "createdAt"
        ],
        "description": "A solar installation site for a customer. Holds roof configurations and the offers you make for it."
      },
      "ProjectList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Project"
            }
          },
          "total": {
            "type": "integer",
            "description": "Total rows matching the filter, ignoring limit/offset."
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          }
        },
        "required": [
          "data",
          "total",
          "limit",
          "offset"
        ]
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "example": "invalid_api_key"
          },
          "message": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ]
      },
      "RoofInput": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Omit to create a new roof; include to update an existing one."
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 99
          },
          "azimuth": {
            "type": "integer",
            "minimum": 0,
            "maximum": 360
          },
          "tilt": {
            "type": "integer",
            "minimum": 0,
            "maximum": 90
          },
          "pvCloudingType": {
            "type": "string",
            "enum": [
              "NO_CLOUDING",
              "LITTLE_CLOUDING",
              "MUCH_CLOUDING",
              "VERY_MUCH_CLOUDING"
            ],
            "description": "How much the roof is shaded."
          },
          "logoPath": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "name",
          "azimuth",
          "tilt",
          "pvCloudingType"
        ],
        "description": "A roof surface on a project. `azimuth` is 0–360° (compass orientation), `tilt` is 0–90° (pitch)."
      },
      "ProjectInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 99
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "customerId": {
            "type": "string"
          },
          "associatedCompanyMemberId": {
            "type": "string",
            "description": "Company member (uuid) the project is assigned to."
          },
          "associatedCompanySiteId": {
            "type": [
              "string",
              "null"
            ]
          },
          "networkCarrierId": {
            "type": [
              "string",
              "null"
            ]
          },
          "electricityPriceCentPerKwh": {
            "type": "number",
            "minimum": 10
          },
          "customerElectricityConsumptionKwhPerYear": {
            "type": "integer",
            "minimum": 0
          },
          "address": {
            "type": "object",
            "properties": {
              "zip": {
                "type": "string"
              },
              "city": {
                "type": "string"
              },
              "street": {
                "type": "string"
              },
              "streetNumber": {
                "type": "string"
              }
            }
          },
          "roofs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RoofInput"
            },
            "description": "The full desired roof set; existing roofs not listed are removed. Omit to leave roofs unchanged."
          }
        },
        "required": [
          "name",
          "customerId",
          "associatedCompanyMemberId",
          "electricityPriceCentPerKwh",
          "customerElectricityConsumptionKwhPerYear"
        ],
        "description": "Body for creating (`POST`) or updating (`PATCH`) a project, saved atomically together with its roofs. `customerId`, `associatedCompanyMemberId`, `electricityPriceCentPerKwh` and `customerElectricityConsumptionKwhPerYear` are required on create; on PATCH every field is optional."
      },
      "Offer": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique offer id.",
            "example": "812"
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Human-readable offer name.",
            "example": "Angebot Müller"
          },
          "projectId": {
            "type": [
              "string",
              "null"
            ],
            "description": "The project this offer belongs to.",
            "example": "41"
          },
          "state": {
            "type": "string",
            "description": "Lifecycle state: `DRAFT`, `PUBLISHED`, `ACCEPTED` or `DECLINED`. Change it via `POST /v1/offers/{id}/state`.",
            "example": "ACCEPTED"
          },
          "isIndicationPrice": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "`true` for a rough indication (Vorplanung), `false` for a detailed offer.",
            "example": false
          },
          "offerPrice": {
            "type": [
              "number",
              "null"
            ],
            "description": "Calculated gross price.",
            "example": 24500
          },
          "validTo": {
            "type": [
              "string",
              "null"
            ],
            "description": "Date the offer is valid until.",
            "example": "2026-08-01"
          },
          "customerId": {
            "type": [
              "string",
              "null"
            ],
            "description": "The customer (via the project).",
            "example": "77"
          },
          "projectName": {
            "type": [
              "string",
              "null"
            ],
            "example": "Dach Müller"
          },
          "customerFullName": {
            "type": [
              "string",
              "null"
            ],
            "example": "Anna Müller"
          },
          "createdAt": {
            "type": "string",
            "example": "2026-07-01T10:22:00Z"
          }
        },
        "required": [
          "id",
          "state",
          "createdAt"
        ],
        "description": "A quote for a project. Build one with `POST /v1/offers`, then publish it and let the customer accept."
      },
      "OfferList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Offer"
            }
          },
          "total": {
            "type": "integer",
            "description": "Total rows matching the filter, ignoring limit/offset."
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          }
        },
        "required": [
          "data",
          "total",
          "limit",
          "offset"
        ]
      },
      "OfferInput": {
        "type": "object",
        "properties": {
          "offer": {
            "type": "object",
            "properties": {
              "projectId": {
                "type": "string"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "description": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "footnote": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "link3dView": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "validTo": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "wallboxInstallationCosts": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "fullFeedIn": {
                "type": "boolean"
              },
              "applySalesTaxFreeEntitled": {
                "type": "boolean"
              },
              "isIndicationPrice": {
                "type": "boolean"
              },
              "associatedCompanyMemberId": {
                "type": "string"
              },
              "taxPercent": {
                "type": [
                  "number",
                  "null"
                ]
              }
            },
            "required": [
              "projectId",
              "associatedCompanyMemberId"
            ],
            "description": "The offer's core fields (project, name, validity, flags)."
          },
          "previewImages": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "imagePath": {
                  "type": "string"
                },
                "orderPriority": {
                  "type": "integer"
                }
              },
              "required": [
                "imagePath"
              ]
            },
            "description": "Pre-uploaded preview images (upload to storage first, then reference the path)."
          },
          "roofs": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "projectRoofConfigurationId": {
                  "type": "string"
                },
                "active": {
                  "type": "boolean"
                },
                "pvModulesCount": {
                  "type": "integer"
                },
                "moduleConfigs": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "recommended": {
                        "type": "boolean"
                      },
                      "materialPvModuleId": {
                        "type": "string"
                      },
                      "netPricePerModule": {
                        "type": "number"
                      },
                      "equipment": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "equipmentId": {
                              "type": "string"
                            },
                            "optional": {
                              "type": "boolean"
                            },
                            "netPrice": {
                              "type": "number"
                            },
                            "units": {
                              "type": "integer"
                            }
                          },
                          "required": [
                            "equipmentId"
                          ]
                        }
                      }
                    },
                    "required": [
                      "materialPvModuleId"
                    ]
                  }
                },
                "subconstruction": {
                  "type": [
                    "object",
                    "null"
                  ],
                  "properties": {
                    "materialSubconstructionId": {
                      "type": "string"
                    },
                    "netPrice": {
                      "type": "number"
                    },
                    "pricePerModule": {
                      "type": "number"
                    },
                    "equipment": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "equipmentId": {
                            "type": "string"
                          },
                          "optional": {
                            "type": "boolean"
                          },
                          "units": {
                            "type": "integer"
                          },
                          "netPrice": {
                            "type": "number"
                          },
                          "netPricePerModule": {
                            "type": "number"
                          }
                        },
                        "required": [
                          "equipmentId"
                        ]
                      }
                    }
                  },
                  "required": [
                    "materialSubconstructionId"
                  ]
                }
              },
              "required": [
                "projectRoofConfigurationId"
              ]
            },
            "description": "Per-roof PV module configurations and subconstruction."
          },
          "batteries": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "active": {
                  "type": "boolean"
                },
                "materialBatteryId": {
                  "type": "string"
                },
                "netPrice": {
                  "type": "number"
                }
              },
              "required": [
                "materialBatteryId"
              ]
            },
            "description": "Battery products; mark the chosen one `active`."
          },
          "batteryEquipment": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "optional": {
                  "type": "boolean"
                },
                "units": {
                  "type": "integer"
                },
                "equipmentId": {
                  "type": "string"
                },
                "materialBatteryId": {
                  "type": "string"
                },
                "netPrice": {
                  "type": "number"
                }
              },
              "required": [
                "equipmentId",
                "materialBatteryId"
              ]
            },
            "description": "Accessories for the selected batteries."
          },
          "wallboxes": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "active": {
                  "type": "boolean"
                },
                "optional": {
                  "type": "boolean"
                },
                "materialWallboxId": {
                  "type": "string"
                },
                "netPrice": {
                  "type": "number"
                }
              },
              "required": [
                "materialWallboxId"
              ]
            },
            "description": "Wallbox (EV charger) products."
          },
          "wallboxEquipment": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "optional": {
                  "type": "boolean"
                },
                "units": {
                  "type": "integer"
                },
                "equipmentId": {
                  "type": "string"
                },
                "materialWallboxId": {
                  "type": "string"
                },
                "netPrice": {
                  "type": "number"
                }
              },
              "required": [
                "equipmentId",
                "materialWallboxId"
              ]
            },
            "description": "Accessories for the selected wallboxes."
          },
          "services": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "serviceId": {
                  "type": "string"
                },
                "fixedPrice": {
                  "type": "number"
                },
                "pricePerModule": {
                  "type": "number"
                },
                "optional": {
                  "type": "boolean"
                },
                "category": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "enum": [
                    "OTHER",
                    "ELECTRICIAN",
                    "ROOF"
                  ],
                  "description": "Service category; defaults to OTHER."
                },
                "orderPriority": {
                  "type": "integer"
                }
              },
              "required": [
                "serviceId"
              ]
            },
            "description": "Additional services (installation, scaffolding, …)."
          },
          "misc": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "materialMiscId": {
                  "type": "string"
                },
                "netPrice": {
                  "type": "number"
                },
                "piecesCount": {
                  "type": "integer"
                },
                "pricePerModule": {
                  "type": "number"
                },
                "optional": {
                  "type": "boolean"
                },
                "equipment": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "equipmentId": {
                        "type": "string"
                      },
                      "optional": {
                        "type": "boolean"
                      },
                      "netPrice": {
                        "type": "number"
                      },
                      "units": {
                        "type": "integer"
                      }
                    },
                    "required": [
                      "equipmentId"
                    ]
                  }
                }
              },
              "required": [
                "materialMiscId"
              ]
            },
            "description": "Miscellaneous material line items."
          },
          "inverters": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "materialInverterId": {
                  "type": "string"
                },
                "netPrice": {
                  "type": "number"
                },
                "mpptCircuitTypes": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "materialInverterMppTrackerId": {
                        "type": "string"
                      },
                      "circuitType": {
                        "type": "string",
                        "enum": [
                          "SERIES",
                          "PARALLEL"
                        ],
                        "description": "How the tracker's strings are wired."
                      }
                    },
                    "required": [
                      "materialInverterMppTrackerId",
                      "circuitType"
                    ]
                  },
                  "description": "Wiring per MPP tracker of this inverter. A configuration row exists for each of the inverter's trackers already; entries here set that row's circuit type. Trackers cannot be added or removed."
                },
                "equipment": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "equipmentId": {
                        "type": "string"
                      },
                      "optional": {
                        "type": "boolean"
                      },
                      "netPrice": {
                        "type": "number"
                      },
                      "units": {
                        "type": "integer"
                      }
                    },
                    "required": [
                      "equipmentId"
                    ]
                  }
                },
                "emergencyPowers": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "name": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "description": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "emergencyPowerSupplyPhases": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "enum": [
                          "ONE_PHASE",
                          "THREE_PHASE"
                        ],
                        "description": "Whether the emergency supply is single- or three-phase."
                      },
                      "netPrice": {
                        "type": "number"
                      },
                      "emergencyPowerSwitchTimeMs": {
                        "type": [
                          "number",
                          "null"
                        ]
                      },
                      "emergencyPowerMaxMainsOperationCurrentA": {
                        "type": [
                          "number",
                          "null"
                        ]
                      },
                      "emergencyPowerMaxOutputCurrentA": {
                        "type": [
                          "number",
                          "null"
                        ]
                      },
                      "emergencyPowerSwitchType": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "enum": [
                          "INTERN",
                          "EXTERN"
                        ],
                        "description": "Whether switching happens inside the inverter or via an external device."
                      },
                      "emergencyPowerManualSwitching": {
                        "type": [
                          "boolean",
                          "null"
                        ]
                      },
                      "recommended": {
                        "type": "boolean"
                      },
                      "equipment": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "equipmentId": {
                              "type": "string"
                            },
                            "netPrice": {
                              "type": "number"
                            },
                            "optional": {
                              "type": "boolean"
                            }
                          },
                          "required": [
                            "equipmentId"
                          ]
                        }
                      }
                    }
                  }
                },
                "mpptStrings": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "trackerMaterialInverterMppTrackerId": {
                        "type": "string"
                      },
                      "roofProjectRoofConfigurationId": {
                        "type": "string"
                      },
                      "parallelStringsCount": {
                        "type": "integer"
                      },
                      "pvModulesCount": {
                        "type": "integer"
                      }
                    },
                    "required": [
                      "trackerMaterialInverterMppTrackerId",
                      "roofProjectRoofConfigurationId"
                    ]
                  }
                }
              },
              "required": [
                "materialInverterId"
              ]
            },
            "description": "Inverters with their MPPT circuit types, equipment, emergency-power settings and string layout."
          },
          "additionalCosts": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "fixedPrice": {
                  "type": "number"
                },
                "pricePerModule": {
                  "type": "number"
                },
                "description": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "optional": {
                  "type": "boolean"
                },
                "salesTaxFreeEntitled": {
                  "type": "boolean"
                }
              },
              "required": [
                "name",
                "fixedPrice"
              ]
            },
            "description": "Free-form line items; a negative `fixedPrice` is a rebate."
          }
        },
        "required": [
          "offer"
        ],
        "description": "The complete offer as one document. `POST /v1/offers` creates it (starts as DRAFT); `PUT /v1/offers/{id}` atomically replaces it. Every collection is optional — a minimal offer is just `offer` with a `projectId` — and you can also manage individual line items later via the `/v1/offers/{id}/...` sub-resources."
      },
      "OfferStateInput": {
        "type": "object",
        "properties": {
          "state": {
            "type": "string",
            "enum": [
              "DRAFT",
              "ACCEPTED",
              "DECLINED",
              "PUBLISHED"
            ]
          }
        },
        "required": [
          "state"
        ]
      },
      "OfferService": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "serviceId": {
            "type": [
              "string",
              "null"
            ]
          },
          "fixedPrice": {
            "type": [
              "number",
              "null"
            ]
          },
          "pricePerModule": {
            "type": [
              "number",
              "null"
            ]
          },
          "optional": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "category": {
            "type": [
              "string",
              "null"
            ]
          },
          "orderPriority": {
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "id"
        ]
      },
      "OfferServiceInput": {
        "type": "object",
        "properties": {
          "serviceId": {
            "type": "string",
            "description": "The service catalog id to attach.",
            "example": "5"
          },
          "fixedPrice": {
            "type": "number",
            "description": "Flat price for this line.",
            "example": 450
          },
          "pricePerModule": {
            "type": [
              "number",
              "null"
            ],
            "description": "Price multiplied by the offer's PV module count."
          },
          "optional": {
            "type": "boolean",
            "description": "Optional line the customer can opt into."
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "OTHER",
              "ELECTRICIAN",
              "ROOF"
            ],
            "description": "Service category; defaults to OTHER."
          },
          "orderPriority": {
            "type": "integer",
            "description": "Sort order in the offer."
          }
        },
        "required": [
          "serviceId"
        ],
        "description": "Add a service line to an offer."
      },
      "OfferAdditionalCost": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "fixedPrice": {
            "type": [
              "number",
              "null"
            ]
          },
          "pricePerModule": {
            "type": [
              "number",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "optional": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "salesTaxFreeEntitled": {
            "type": [
              "boolean",
              "null"
            ]
          }
        },
        "required": [
          "id"
        ]
      },
      "OfferAdditionalCostInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "fixedPrice": {
            "type": "number"
          },
          "pricePerModule": {
            "type": [
              "number",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "optional": {
            "type": "boolean"
          },
          "salesTaxFreeEntitled": {
            "type": "boolean"
          }
        },
        "required": [
          "name",
          "fixedPrice"
        ],
        "description": "Add a free-form cost line to an offer. A negative `fixedPrice` is a rebate."
      },
      "OfferBattery": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "materialBatteryId": {
            "type": [
              "string",
              "null"
            ]
          },
          "netPrice": {
            "type": [
              "number",
              "null"
            ]
          },
          "active": {
            "type": [
              "boolean",
              "null"
            ]
          }
        },
        "required": [
          "id"
        ]
      },
      "OfferBatteryInput": {
        "type": "object",
        "properties": {
          "materialBatteryId": {
            "type": "string",
            "description": "The battery material id. A battery can appear on an offer once — adding the same one twice answers 409.",
            "example": "88"
          },
          "netPrice": {
            "type": "number",
            "description": "Net price of the battery on this offer.",
            "example": 4200
          },
          "active": {
            "type": "boolean",
            "description": "Mark the battery selected on the offer."
          }
        },
        "required": [
          "materialBatteryId",
          "netPrice"
        ],
        "description": "Add a battery to an offer."
      },
      "OfferWallbox": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "materialWallboxId": {
            "type": [
              "string",
              "null"
            ]
          },
          "netPrice": {
            "type": [
              "number",
              "null"
            ]
          },
          "active": {
            "type": [
              "boolean",
              "null"
            ]
          }
        },
        "required": [
          "id"
        ]
      },
      "OfferWallboxInput": {
        "type": "object",
        "properties": {
          "materialWallboxId": {
            "type": "string",
            "description": "The wallbox material id. A wallbox can appear on an offer once — adding the same one twice answers 409."
          },
          "netPrice": {
            "type": "number",
            "description": "Net price of the wallbox on this offer."
          },
          "active": {
            "type": "boolean"
          }
        },
        "required": [
          "materialWallboxId",
          "netPrice"
        ],
        "description": "Add a wallbox (EV charger) to an offer."
      },
      "OfferMisc": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "materialMiscId": {
            "type": [
              "string",
              "null"
            ]
          },
          "netPrice": {
            "type": [
              "number",
              "null"
            ]
          },
          "piecesCount": {
            "type": [
              "integer",
              "null"
            ]
          },
          "pricePerModule": {
            "type": [
              "number",
              "null"
            ]
          },
          "optional": {
            "type": [
              "boolean",
              "null"
            ]
          }
        },
        "required": [
          "id"
        ]
      },
      "OfferMiscInput": {
        "type": "object",
        "properties": {
          "materialMiscId": {
            "type": "string"
          },
          "netPrice": {
            "type": "number",
            "description": "Net price per piece."
          },
          "piecesCount": {
            "type": "integer",
            "description": "How many pieces are on the offer."
          },
          "pricePerModule": {
            "type": "number"
          },
          "optional": {
            "type": "boolean"
          }
        },
        "required": [
          "materialMiscId",
          "netPrice",
          "piecesCount"
        ],
        "description": "Add a miscellaneous material line to an offer."
      },
      "Customer": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique customer id.",
            "example": "42"
          },
          "firstName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Given name.",
            "example": "Anna"
          },
          "lastName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Family name.",
            "example": "Müller"
          },
          "fullName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Convenience: first and last name joined.",
            "example": "Anna Müller"
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "description": "Contact email.",
            "example": "anna.mueller@example.com"
          },
          "phoneNumber": {
            "type": [
              "string",
              "null"
            ],
            "description": "Contact phone number.",
            "example": "+49 30 1234567"
          },
          "address": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Address"
              },
              {
                "description": "A postal address."
              }
            ]
          },
          "projectsCount": {
            "type": [
              "integer",
              "null"
            ],
            "description": "How many projects this customer owns.",
            "example": 2
          },
          "offersCount": {
            "type": [
              "integer",
              "null"
            ],
            "description": "How many offers exist across the customer's projects.",
            "example": 3
          },
          "createdAt": {
            "type": "string",
            "description": "ISO 8601 creation timestamp.",
            "example": "2026-07-01T10:22:00Z"
          }
        },
        "required": [
          "id",
          "createdAt"
        ],
        "description": "A customer of your company — the person an offer is ultimately made to."
      },
      "CustomerList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Customer"
            }
          },
          "total": {
            "type": "integer",
            "description": "Total rows matching the filter, ignoring limit/offset."
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          }
        },
        "required": [
          "data",
          "total",
          "limit",
          "offset"
        ]
      },
      "CustomerInput": {
        "type": "object",
        "properties": {
          "firstName": {
            "type": "string",
            "minLength": 1,
            "maxLength": 99,
            "description": "Given name.",
            "example": "Anna"
          },
          "lastName": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 99,
            "description": "Family name.",
            "example": "Müller"
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 99,
            "description": "Contact email.",
            "example": "anna.mueller@example.com"
          },
          "phoneNumber": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 20,
            "description": "Contact phone number.",
            "example": "+49 30 1234567"
          },
          "gender": {
            "type": "string",
            "enum": [
              "MALE",
              "FEMALE"
            ],
            "description": "Used for the salutation in customer-facing documents. Defaults to MALE."
          },
          "address": {
            "type": "object",
            "properties": {
              "zip": {
                "type": "string",
                "example": "80331"
              },
              "city": {
                "type": "string",
                "example": "München"
              },
              "street": {
                "type": "string",
                "example": "Marienplatz"
              },
              "streetNumber": {
                "type": "string",
                "example": "1"
              }
            },
            "description": "Postal address; any subset of fields."
          }
        },
        "required": [
          "firstName"
        ],
        "description": "Body for creating (`POST`) or updating (`PATCH`) a customer. On PATCH every field is optional and only the supplied fields change; send an explicit `null` to clear a nullable field."
      },
      "OfferRequest": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "state": {
            "type": [
              "string",
              "null"
            ]
          },
          "customer": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "firstName": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "lastName": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "fullName": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "email": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "phoneNumber": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "address": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Address"
                  },
                  {
                    "description": "A postal address."
                  }
                ]
              }
            }
          },
          "projectId": {
            "type": [
              "string",
              "null"
            ]
          },
          "projectName": {
            "type": [
              "string",
              "null"
            ]
          },
          "companyMemberName": {
            "type": [
              "string",
              "null"
            ]
          },
          "offersCount": {
            "type": [
              "integer",
              "null"
            ]
          },
          "indicationOffersCount": {
            "type": [
              "integer",
              "null"
            ]
          },
          "detailedOffersCount": {
            "type": [
              "integer",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "example": "2026-07-01T10:22:00Z"
          }
        },
        "required": [
          "id",
          "createdAt"
        ],
        "description": "An incoming customer enquiry (Anfrage) that precedes a detailed offer. Customer-portal access tokens are intentionally not exposed."
      },
      "OfferRequestList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OfferRequest"
            }
          },
          "total": {
            "type": "integer",
            "description": "Total rows matching the filter, ignoring limit/offset."
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          }
        },
        "required": [
          "data",
          "total",
          "limit",
          "offset"
        ]
      },
      "Material": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique material id (within its type).",
            "example": "150"
          },
          "type": {
            "type": "string",
            "description": "Material type — one of `pv_module`, `battery`, `inverter`, `wallbox`, `equipment`, `misc`, `subconstruction`, `emergency_power`. Determines the `specs` fields.",
            "example": "pv_module"
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "example": "Aiko Neostar 2S 445W"
          },
          "manufacturerId": {
            "type": [
              "string",
              "null"
            ],
            "description": "The manufacturer this material belongs to.",
            "example": "12"
          },
          "manufacturerName": {
            "type": [
              "string",
              "null"
            ],
            "example": "Aiko"
          },
          "priceNet": {
            "type": [
              "number",
              "null"
            ],
            "description": "Net purchase/list price.",
            "example": 89.9
          },
          "priceGross": {
            "type": [
              "number",
              "null"
            ],
            "description": "Gross price (incl. VAT).",
            "example": 106.98
          },
          "archived": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Archived materials are hidden from new offers.",
            "example": false
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "example": "2026-07-01T10:22:00Z"
          },
          "specs": {
            "type": "object",
            "additionalProperties": {},
            "description": "Type-specific attributes. E.g. a `pv_module` has `nominalPowerW`, `moduleMaterial`, `uMppV`, `iMppA`; a `battery` has `capacityKwh`, `powerKw`; an `inverter` has `mpptCount`, `acNominalPowerKw`, etc."
          }
        },
        "required": [
          "id",
          "type",
          "specs"
        ],
        "description": "A catalog product. All material types share this shape; type-specific attributes live under `specs`."
      },
      "MaterialList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Material"
            }
          },
          "total": {
            "type": "integer",
            "description": "Total rows matching the filter, ignoring limit/offset."
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          }
        },
        "required": [
          "data",
          "total",
          "limit",
          "offset"
        ]
      },
      "DimensionsInput": {
        "type": "object",
        "properties": {
          "widthMm": {
            "type": [
              "number",
              "null"
            ]
          },
          "heightMm": {
            "type": [
              "number",
              "null"
            ]
          },
          "depthMm": {
            "type": [
              "number",
              "null"
            ]
          }
        },
        "description": "Physical dimensions in millimetres."
      },
      "PurchasePriceInput": {
        "type": "object",
        "properties": {
          "pricePerPiece": {
            "type": "number",
            "description": "Net purchase price for a single piece.",
            "example": 189.5
          },
          "vendorName": {
            "type": "string",
            "description": "Who you buy it from. Required — purchase prices are always recorded against a vendor.",
            "example": "Großhandel Müller"
          },
          "vendorLink": {
            "type": [
              "string",
              "null"
            ],
            "description": "Link to the vendor's offer or product page."
          }
        },
        "required": [
          "pricePerPiece",
          "vendorName"
        ],
        "description": "Purchase price for the material. Stored as its own record and linked to the material, so writing a new one replaces the price the catalog uses."
      },
      "PvModuleInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturerId": {
            "type": "string"
          },
          "materialSeriesId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Optional series this product is part of."
          },
          "productSeries": {
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturerArticleNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "internalArticleNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "productGuaranteeYears": {
            "type": [
              "integer",
              "null"
            ],
            "example": 15
          },
          "salesTaxFreeEntitled": {
            "type": "boolean",
            "description": "Eligible for the 0% VAT rule."
          },
          "weightG": {
            "type": [
              "number",
              "null"
            ]
          },
          "dimensions": {
            "$ref": "#/components/schemas/DimensionsInput"
          },
          "datasheetFilePath": {
            "type": [
              "string",
              "null"
            ]
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "archived": {
            "type": "boolean",
            "description": "Archived products stay referencable by existing offers but are hidden from new ones."
          },
          "favourite": {
            "type": "boolean"
          },
          "purchasePrice": {
            "$ref": "#/components/schemas/PurchasePriceInput"
          },
          "nominalPowerW": {
            "type": "number",
            "description": "Nominal power under STC.",
            "example": 440
          },
          "uMppV": {
            "type": "number",
            "description": "Voltage at the maximum power point."
          },
          "iMppA": {
            "type": "number",
            "description": "Current at the maximum power point."
          },
          "noLoadVoltageV": {
            "type": "number",
            "description": "Open-circuit voltage (Uoc)."
          },
          "shortCircuitCurrentI": {
            "type": "number",
            "description": "Short-circuit current (Isc)."
          },
          "moduleMaterial": {
            "type": "string",
            "enum": [
              "GLASS_GLASS",
              "GLASS_FOIL"
            ]
          },
          "plug": {
            "type": "string",
            "enum": [
              "MC4_STAEUBLI",
              "MC4_STAEUBLI_COMPATIBLE"
            ]
          },
          "fullBlack": {
            "type": "boolean"
          },
          "uOcCoefficientPercentPerKelvin": {
            "type": "number"
          },
          "iScCoefficientPercentPerKelvin": {
            "type": "number"
          },
          "pCoefficientPercentPerKelvin": {
            "type": "number"
          },
          "performanceGuaranteeYears": {
            "type": "integer",
            "example": 25
          },
          "bifacialityFactorPercent": {
            "type": [
              "number",
              "null"
            ]
          }
        },
        "required": [
          "name",
          "manufacturerId",
          "nominalPowerW",
          "uMppV",
          "iMppA",
          "noLoadVoltageV",
          "shortCircuitCurrentI",
          "moduleMaterial",
          "plug",
          "fullBlack",
          "uOcCoefficientPercentPerKelvin",
          "iScCoefficientPercentPerKelvin",
          "pCoefficientPercentPerKelvin",
          "performanceGuaranteeYears"
        ],
        "description": "A photovoltaic module. Electrical characteristics drive the string sizing in the planner, so they are required."
      },
      "BatteryInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturerId": {
            "type": "string"
          },
          "materialSeriesId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Optional series this product is part of."
          },
          "productSeries": {
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturerArticleNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "internalArticleNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "productGuaranteeYears": {
            "type": [
              "integer",
              "null"
            ],
            "example": 15
          },
          "salesTaxFreeEntitled": {
            "type": "boolean",
            "description": "Eligible for the 0% VAT rule."
          },
          "weightG": {
            "type": [
              "number",
              "null"
            ]
          },
          "dimensions": {
            "$ref": "#/components/schemas/DimensionsInput"
          },
          "datasheetFilePath": {
            "type": [
              "string",
              "null"
            ]
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "archived": {
            "type": "boolean",
            "description": "Archived products stay referencable by existing offers but are hidden from new ones."
          },
          "favourite": {
            "type": "boolean"
          },
          "purchasePrice": {
            "$ref": "#/components/schemas/PurchasePriceInput"
          },
          "capacityKwh": {
            "type": "number",
            "description": "Nominal usable capacity.",
            "example": 10.1
          },
          "powerKw": {
            "type": "number",
            "description": "Nominal power.",
            "example": 5
          },
          "linkType": {
            "type": "string",
            "enum": [
              "AC",
              "DC"
            ],
            "description": "How the battery couples to the system."
          },
          "chemicalType": {
            "type": "string",
            "enum": [
              "LFP",
              "NMC"
            ]
          },
          "montageFloor": {
            "type": "string",
            "enum": [
              "INCLUDED",
              "NOT_INCLUDED",
              "OPTIONAL"
            ],
            "description": "Whether floor mounting is included."
          },
          "montageWall": {
            "type": "string",
            "enum": [
              "INCLUDED",
              "NOT_INCLUDED",
              "OPTIONAL"
            ],
            "description": "Whether wall mounting is included."
          },
          "nominalVoltageV": {
            "type": [
              "number",
              "null"
            ]
          },
          "nominalChargeCurrentA": {
            "type": [
              "number",
              "null"
            ]
          },
          "nominalDischargeCurrentA": {
            "type": [
              "number",
              "null"
            ]
          },
          "nominalDischargePowerKw": {
            "type": [
              "number",
              "null"
            ]
          }
        },
        "required": [
          "name",
          "manufacturerId",
          "capacityKwh",
          "powerKw",
          "linkType",
          "chemicalType",
          "montageFloor",
          "montageWall"
        ],
        "description": "A battery storage product."
      },
      "InverterInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturerId": {
            "type": "string"
          },
          "materialSeriesId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Optional series this product is part of."
          },
          "productSeries": {
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturerArticleNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "internalArticleNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "productGuaranteeYears": {
            "type": [
              "integer",
              "null"
            ],
            "example": 15
          },
          "salesTaxFreeEntitled": {
            "type": "boolean",
            "description": "Eligible for the 0% VAT rule."
          },
          "weightG": {
            "type": [
              "number",
              "null"
            ]
          },
          "dimensions": {
            "$ref": "#/components/schemas/DimensionsInput"
          },
          "datasheetFilePath": {
            "type": [
              "string",
              "null"
            ]
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "archived": {
            "type": "boolean",
            "description": "Archived products stay referencable by existing offers but are hidden from new ones."
          },
          "favourite": {
            "type": "boolean"
          },
          "purchasePrice": {
            "$ref": "#/components/schemas/PurchasePriceInput"
          },
          "dcMinInputVoltageV": {
            "type": "number"
          },
          "dcMaxInputVoltageV": {
            "type": "number"
          },
          "dcMaxPowerKw": {
            "type": [
              "number",
              "null"
            ]
          },
          "dcOverVoltageProtection1": {
            "type": "string",
            "enum": [
              "INCLUDED",
              "NO_PROTECTION",
              "OPTIONAL"
            ]
          },
          "dcOverVoltageProtection2": {
            "type": "string",
            "enum": [
              "INCLUDED",
              "NO_PROTECTION",
              "OPTIONAL"
            ]
          },
          "dcOverVoltageProtection3": {
            "type": "string",
            "enum": [
              "INCLUDED",
              "NO_PROTECTION",
              "OPTIONAL"
            ]
          },
          "acOverVoltageProtection1": {
            "type": "string",
            "enum": [
              "INCLUDED",
              "NO_PROTECTION",
              "OPTIONAL"
            ]
          },
          "acOverVoltageProtection2": {
            "type": "string",
            "enum": [
              "INCLUDED",
              "NO_PROTECTION",
              "OPTIONAL"
            ]
          },
          "acOverVoltageProtection3": {
            "type": "string",
            "enum": [
              "INCLUDED",
              "NO_PROTECTION",
              "OPTIONAL"
            ]
          },
          "acNominalPowerKw": {
            "type": "number",
            "example": 10
          },
          "acMaxPowerKw": {
            "type": "number"
          },
          "acRatedCurrentI": {
            "type": "number"
          },
          "acNominalVoltageV": {
            "type": [
              "number",
              "null"
            ]
          },
          "acSupplyPhasesType": {
            "type": "string",
            "enum": [
              "ONE_PHASE",
              "THREE_PHASE"
            ]
          },
          "acZnasType": {
            "type": "string",
            "enum": [
              "INCLUDED",
              "NOT_INCLUDED",
              "OPTIONAL"
            ]
          },
          "ethernet": {
            "type": "string",
            "enum": [
              "INCLUDED",
              "NOT_INCLUDED",
              "OPTIONAL"
            ]
          },
          "wlan": {
            "type": "string",
            "enum": [
              "INCLUDED",
              "NOT_INCLUDED",
              "OPTIONAL"
            ]
          },
          "rse": {
            "type": "string",
            "enum": [
              "INCLUDED",
              "NOT_INCLUDED",
              "OPTIONAL"
            ],
            "description": "Ripple control receiver (Rundsteuerempfänger)."
          },
          "smartgridReady": {
            "type": "string",
            "enum": [
              "INCLUDED",
              "NOT_INCLUDED",
              "OPTIONAL"
            ]
          },
          "batteryConfigurationType": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "INCLUDED",
              "NOT_INCLUDED",
              "OPTIONAL"
            ]
          }
        },
        "required": [
          "name",
          "manufacturerId",
          "dcMinInputVoltageV",
          "dcMaxInputVoltageV",
          "dcOverVoltageProtection1",
          "dcOverVoltageProtection2",
          "dcOverVoltageProtection3",
          "acOverVoltageProtection1",
          "acOverVoltageProtection2",
          "acOverVoltageProtection3",
          "acNominalPowerKw",
          "acMaxPowerKw",
          "acRatedCurrentI",
          "acSupplyPhasesType",
          "acZnasType",
          "ethernet",
          "wlan",
          "rse",
          "smartgridReady"
        ],
        "description": "An inverter. MPP trackers are managed separately; adding an inverter to an offer creates a configuration row per tracker automatically."
      },
      "WallboxInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturerId": {
            "type": "string"
          },
          "materialSeriesId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Optional series this product is part of."
          },
          "productSeries": {
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturerArticleNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "internalArticleNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "productGuaranteeYears": {
            "type": [
              "integer",
              "null"
            ],
            "example": 15
          },
          "salesTaxFreeEntitled": {
            "type": "boolean",
            "description": "Eligible for the 0% VAT rule."
          },
          "weightG": {
            "type": [
              "number",
              "null"
            ]
          },
          "dimensions": {
            "$ref": "#/components/schemas/DimensionsInput"
          },
          "datasheetFilePath": {
            "type": [
              "string",
              "null"
            ]
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "archived": {
            "type": "boolean",
            "description": "Archived products stay referencable by existing offers but are hidden from new ones."
          },
          "favourite": {
            "type": "boolean"
          },
          "purchasePrice": {
            "$ref": "#/components/schemas/PurchasePriceInput"
          },
          "chargingPower": {
            "type": "string",
            "enum": [
              "POWER_11_KW",
              "POWER_22_KW"
            ]
          },
          "meterIntegrated": {
            "type": "boolean"
          },
          "rfidIntegrated": {
            "type": "boolean"
          },
          "dcFaultCurrentDetection": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "networkEthernet": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "networkWlan": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "networkDirectConnection": {
            "type": [
              "boolean",
              "null"
            ]
          }
        },
        "required": [
          "name",
          "manufacturerId",
          "chargingPower",
          "meterIntegrated",
          "rfidIntegrated"
        ],
        "description": "A wallbox (EV charger)."
      },
      "EquipmentInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturerId": {
            "type": "string"
          },
          "materialSeriesId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Optional series this product is part of."
          },
          "productSeries": {
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturerArticleNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "internalArticleNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "productGuaranteeYears": {
            "type": [
              "integer",
              "null"
            ],
            "example": 15
          },
          "salesTaxFreeEntitled": {
            "type": "boolean",
            "description": "Eligible for the 0% VAT rule."
          },
          "weightG": {
            "type": [
              "number",
              "null"
            ]
          },
          "dimensions": {
            "$ref": "#/components/schemas/DimensionsInput"
          },
          "datasheetFilePath": {
            "type": [
              "string",
              "null"
            ]
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "archived": {
            "type": "boolean",
            "description": "Archived products stay referencable by existing offers but are hidden from new ones."
          },
          "favourite": {
            "type": "boolean"
          },
          "purchasePrice": {
            "$ref": "#/components/schemas/PurchasePriceInput"
          }
        },
        "required": [
          "name",
          "manufacturerId"
        ],
        "description": "An accessory that can be attached to modules, inverters, batteries, wallboxes or subconstruction."
      },
      "MiscMaterialInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturerId": {
            "type": "string",
            "description": "Manufacturer this product belongs to.",
            "example": "12"
          },
          "materialSeriesId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Optional series this product is part of."
          },
          "productSeries": {
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturerArticleNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "internalArticleNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "productGuaranteeYears": {
            "type": [
              "integer",
              "null"
            ],
            "example": 15
          },
          "salesTaxFreeEntitled": {
            "type": "boolean",
            "description": "Eligible for the 0% VAT rule."
          },
          "weightG": {
            "type": [
              "number",
              "null"
            ]
          },
          "dimensions": {
            "$ref": "#/components/schemas/DimensionsInput"
          },
          "datasheetFilePath": {
            "type": [
              "string",
              "null"
            ]
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "archived": {
            "type": "boolean",
            "description": "Archived products stay referencable by existing offers but are hidden from new ones."
          },
          "favourite": {
            "type": "boolean"
          },
          "purchasePrice": {
            "$ref": "#/components/schemas/PurchasePriceInput"
          },
          "includedInOffer": {
            "type": "boolean"
          },
          "includedInIndicationOffer": {
            "type": "boolean"
          },
          "optional": {
            "type": "boolean"
          }
        },
        "required": [
          "name"
        ],
        "description": "A miscellaneous material line. Unlike other subtypes it needs no manufacturer, and it can be pre-selected onto offers."
      },
      "SubconstructionInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturerId": {
            "type": "string"
          },
          "materialSeriesId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Optional series this product is part of."
          },
          "productSeries": {
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturerArticleNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "internalArticleNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "productGuaranteeYears": {
            "type": [
              "integer",
              "null"
            ],
            "example": 15
          },
          "salesTaxFreeEntitled": {
            "type": "boolean",
            "description": "Eligible for the 0% VAT rule."
          },
          "weightG": {
            "type": [
              "number",
              "null"
            ]
          },
          "dimensions": {
            "$ref": "#/components/schemas/DimensionsInput"
          },
          "datasheetFilePath": {
            "type": [
              "string",
              "null"
            ]
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "archived": {
            "type": "boolean",
            "description": "Archived products stay referencable by existing offers but are hidden from new ones."
          },
          "favourite": {
            "type": "boolean"
          },
          "purchasePrice": {
            "$ref": "#/components/schemas/PurchasePriceInput"
          },
          "roofType": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "TILED_ROOF",
              "BITUMEN_ROOF",
              "TRAPEZOIDAL_SHEET_METAL_ROOF",
              "BEADED_PLATE_ROOF",
              "FACADE",
              "OPEN_FIELD"
            ],
            "description": "Roof type this mounting system is for."
          }
        },
        "required": [
          "name",
          "manufacturerId"
        ],
        "description": "A mounting system (Unterkonstruktion)."
      },
      "EmergencyPowerMaterialInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Product name as it appears in offers.",
            "example": "Vitovolt 300-DG M440HC"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturerId": {
            "type": "string",
            "description": "Manufacturer this product belongs to.",
            "example": "12"
          },
          "materialSeriesId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Optional series this product is part of."
          },
          "productSeries": {
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturerArticleNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "internalArticleNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "productGuaranteeYears": {
            "type": [
              "integer",
              "null"
            ],
            "example": 15
          },
          "salesTaxFreeEntitled": {
            "type": "boolean",
            "description": "Eligible for the 0% VAT rule."
          },
          "weightG": {
            "type": [
              "number",
              "null"
            ]
          },
          "dimensions": {
            "$ref": "#/components/schemas/DimensionsInput"
          },
          "datasheetFilePath": {
            "type": [
              "string",
              "null"
            ]
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "archived": {
            "type": "boolean",
            "description": "Archived products stay referencable by existing offers but are hidden from new ones."
          },
          "favourite": {
            "type": "boolean"
          },
          "purchasePrice": {
            "$ref": "#/components/schemas/PurchasePriceInput"
          }
        },
        "description": "An emergency power product that can be offered alongside an inverter."
      },
      "Manufacturer": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "12"
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "example": "Aiko"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "phoneNumber": {
            "type": [
              "string",
              "null"
            ],
            "example": "+49 30 1234567"
          },
          "websiteUrl": {
            "type": [
              "string",
              "null"
            ],
            "example": "https://aikosolar.com"
          },
          "logoPath": {
            "type": [
              "string",
              "null"
            ],
            "description": "Storage path of the logo, if uploaded."
          },
          "contactEmail": {
            "type": [
              "string",
              "null"
            ],
            "example": "sales@aikosolar.com"
          },
          "createdAt": {
            "type": "string",
            "example": "2026-07-01T10:22:00Z"
          }
        },
        "required": [
          "id",
          "createdAt"
        ],
        "description": "A manufacturer your materials belong to."
      },
      "ManufacturerList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Manufacturer"
            }
          },
          "total": {
            "type": "integer",
            "description": "Total rows matching the filter, ignoring limit/offset."
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          }
        },
        "required": [
          "data",
          "total",
          "limit",
          "offset"
        ]
      },
      "ManufacturerInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 99,
            "description": "Manufacturer name.",
            "example": "Aiko"
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 999
          },
          "phoneNumber": {
            "type": [
              "string",
              "null"
            ],
            "example": "+49 30 1234567"
          },
          "websiteUrl": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 199,
            "example": "https://aikosolar.com"
          },
          "logoPath": {
            "type": [
              "string",
              "null"
            ]
          },
          "contactEmail": {
            "type": [
              "string",
              "null"
            ],
            "example": "sales@aikosolar.com"
          }
        },
        "required": [
          "name"
        ],
        "description": "Body for creating (`POST`) or updating (`PATCH`) a manufacturer. On PATCH every field is optional."
      },
      "Service": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "5"
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "example": "Gerüst"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "includedInOffer": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Whether the service is added to new offers by default.",
            "example": true
          },
          "fixedPrice": {
            "type": [
              "number",
              "null"
            ],
            "description": "Flat price for the service.",
            "example": 450
          },
          "pricePerModule": {
            "type": [
              "number",
              "null"
            ],
            "description": "Price multiplied by the number of PV modules.",
            "example": 0
          },
          "salesTaxFreeEntitled": {
            "type": [
              "boolean",
              "null"
            ],
            "example": false
          },
          "categoryId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Optional service category."
          },
          "createdAt": {
            "type": "string",
            "example": "2026-07-01T10:22:00Z"
          }
        },
        "required": [
          "id",
          "createdAt"
        ],
        "description": "A reusable service you can add to offers (installation, scaffolding, …)."
      },
      "ServiceList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Service"
            }
          },
          "total": {
            "type": "integer",
            "description": "Total rows matching the filter, ignoring limit/offset."
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          }
        },
        "required": [
          "data",
          "total",
          "limit",
          "offset"
        ]
      },
      "ServiceInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "description": "Service name.",
            "example": "Gerüst"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "includedInOffer": {
            "type": "boolean",
            "description": "Add to new offers by default.",
            "example": true
          },
          "fixedPrice": {
            "type": "number",
            "description": "Flat price.",
            "example": 450
          },
          "pricePerModule": {
            "type": "number",
            "description": "Per-PV-module price; use 0 for none.",
            "example": 0
          },
          "salesTaxFreeEntitled": {
            "type": "boolean"
          }
        },
        "required": [
          "name",
          "includedInOffer",
          "fixedPrice",
          "pricePerModule"
        ],
        "description": "Body for creating (`POST`) or updating (`PATCH`) a service. On PATCH every field is optional."
      },
      "Company": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "1"
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "example": "Solario GmbH"
          },
          "supportPhoneNumber": {
            "type": [
              "string",
              "null"
            ],
            "example": "+49 30 1234567"
          },
          "supportEmail": {
            "type": [
              "string",
              "null"
            ],
            "example": "support@solario.example"
          },
          "logoPath": {
            "type": [
              "string",
              "null"
            ],
            "description": "Storage path of the company logo."
          },
          "websiteUrl": {
            "type": [
              "string",
              "null"
            ],
            "example": "https://solario.example"
          },
          "address": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Address"
              },
              {
                "description": "A postal address."
              }
            ]
          },
          "createdAt": {
            "type": "string",
            "example": "2026-07-01T10:22:00Z"
          }
        },
        "required": [
          "id",
          "createdAt"
        ],
        "description": "Your own company profile — support contact, address and branding shown to customers."
      },
      "CompanyInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1
          },
          "supportPhoneNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "supportEmail": {
            "type": [
              "string",
              "null"
            ]
          },
          "logoPath": {
            "type": [
              "string",
              "null"
            ]
          },
          "websiteUrl": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 199
          },
          "address": {
            "type": "object",
            "properties": {
              "zip": {
                "type": "string"
              },
              "city": {
                "type": "string"
              },
              "street": {
                "type": "string"
              },
              "streetNumber": {
                "type": "string"
              }
            }
          }
        },
        "description": "Body for updating your company profile (`PATCH /v1/company`). Every field is optional; only supplied fields change."
      },
      "AnalyticsSeriesPoint": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "description": "Start of the bucket.",
            "example": "2026-07-01"
          },
          "count": {
            "type": "integer",
            "example": 42
          }
        },
        "required": [
          "date",
          "count"
        ]
      },
      "AnalyticsSeries": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnalyticsSeriesPoint"
            }
          }
        },
        "required": [
          "data"
        ]
      },
      "AnalyticsConversionPoint": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string"
          },
          "count": {
            "type": "integer",
            "description": "Requests that entered this bucket."
          },
          "converted": {
            "type": "integer",
            "description": "How many of them converted."
          }
        },
        "required": [
          "date",
          "count",
          "converted"
        ]
      },
      "AnalyticsConversions": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnalyticsConversionPoint"
            }
          }
        },
        "required": [
          "data"
        ]
      },
      "AnalyticsDemographics": {
        "type": "object",
        "properties": {
          "customerCount": {
            "type": "integer"
          },
          "maleCount": {
            "type": "integer"
          },
          "femaleCount": {
            "type": "integer"
          }
        },
        "required": [
          "customerCount",
          "maleCount",
          "femaleCount"
        ],
        "description": "Headline customer counts for your company."
      },
      "AnalyticsProductSale": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string"
          },
          "productKey": {
            "type": [
              "string",
              "null"
            ]
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "description": "Which product family the line belongs to (module, battery, inverter, …)."
          },
          "materialId": {
            "type": [
              "string",
              "null"
            ]
          },
          "productName": {
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturerName": {
            "type": [
              "string",
              "null"
            ]
          },
          "soldUnits": {
            "type": [
              "number",
              "null"
            ]
          },
          "soldOffersCount": {
            "type": [
              "integer",
              "null"
            ]
          },
          "lastSoldAt": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "date"
        ]
      },
      "AnalyticsProductSaleList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnalyticsProductSale"
            }
          },
          "total": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          }
        },
        "required": [
          "data",
          "total",
          "limit",
          "offset"
        ]
      },
      "AnalyticsOpenOffer": {
        "type": "object",
        "properties": {
          "productKey": {
            "type": [
              "string",
              "null"
            ]
          },
          "category": {
            "type": [
              "string",
              "null"
            ]
          },
          "materialId": {
            "type": [
              "string",
              "null"
            ]
          },
          "productName": {
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturerName": {
            "type": [
              "string",
              "null"
            ]
          },
          "openUnits": {
            "type": [
              "number",
              "null"
            ],
            "description": "Units sitting in offers that are still open."
          },
          "openOffersCount": {
            "type": [
              "integer",
              "null"
            ]
          }
        }
      },
      "AnalyticsOpenOfferList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnalyticsOpenOffer"
            }
          },
          "total": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          }
        },
        "required": [
          "data",
          "total",
          "limit",
          "offset"
        ]
      },
      "NetworkCarrierDocumentsInput": {
        "type": "object",
        "properties": {
          "networkCarrierId": {
            "type": "string",
            "description": "The grid operator whose forms to fill. Its document set defines which `documentIds` are valid.",
            "example": "7"
          },
          "documentIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "minItems": 1,
            "description": "Which of the operator's documents to produce, in the order the operator configured.",
            "example": [
              "1",
              "4"
            ]
          },
          "plannedGoingLiveDate": {
            "type": [
              "string",
              "null"
            ],
            "description": "Planned commissioning date, written into the forms that ask for one.",
            "example": "2026-09-01"
          },
          "electronicsFirmId": {
            "type": [
              "string",
              "null"
            ],
            "description": "The electrical contractor to name on the forms. Defaults to the one on the project."
          },
          "moduleConfigurationIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Restrict the technical data to these module configurations of the offer. Omit to include all of them."
          },
          "batteryId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Which battery on the offer to describe, when the offer carries more than one."
          },
          "includeCertificates": {
            "type": "boolean",
            "description": "Append product certificates to the archive."
          },
          "includeDatasheets": {
            "type": "boolean",
            "description": "Append product datasheets to the archive."
          },
          "includeMergedDocument": {
            "type": "boolean",
            "description": "Also include a single PDF with every generated document merged, alongside the individual files."
          }
        },
        "required": [
          "networkCarrierId",
          "documentIds"
        ],
        "description": "Which grid-operator documents to generate for an offer, and what to put on them."
      }
    },
    "parameters": {}
  },
  "paths": {
    "/v1/projects": {
      "get": {
        "summary": "List projects",
        "description": "List your company's projects. Filter by `name`, `state`, `customerId` or `city`; sort (e.g. `sort=createdAt.desc`) and page with `limit`/`offset`. Requires `read:projects`.",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50,
              "description": "Maximum rows to return (1–200)."
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "minimum": 0,
              "default": 0,
              "description": "Rows to skip before returning results."
            },
            "required": false,
            "name": "offset",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "`<field>.asc` or `<field>.desc`, where `<field>` is a response field name. Each resource allows its own set; an unknown field answers 400 with the allowed list.",
              "example": "createdAt.desc"
            },
            "required": false,
            "name": "sort",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: project name contains this substring."
            },
            "required": false,
            "name": "name",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: exact project state."
            },
            "required": false,
            "name": "state",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: projects for this customer id."
            },
            "required": false,
            "name": "customerId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: project city contains this substring."
            },
            "required": false,
            "name": "city",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Filterable, sortable, paginated list of projects.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProjectList"
                }
              }
            }
          },
          "400": {
            "description": "invalid_sort",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a project",
        "description": "Create a project (installation site) for a customer, saved atomically with its roof configurations. Provide `customerId` (from a customer you created), `associatedCompanyMemberId`, the electricity price and yearly consumption. Returns the created project. Requires `write:projects`.",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProjectInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/projects/{id}": {
      "get": {
        "summary": "Get a project",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update a project",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProjectInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete a project",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Project deleted."
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/offers": {
      "get": {
        "summary": "List offers",
        "tags": [
          "Offers"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50,
              "description": "Maximum rows to return (1–200)."
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "minimum": 0,
              "default": 0,
              "description": "Rows to skip before returning results."
            },
            "required": false,
            "name": "offset",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "`<field>.asc` or `<field>.desc`, where `<field>` is a response field name. Each resource allows its own set; an unknown field answers 400 with the allowed list.",
              "example": "createdAt.desc"
            },
            "required": false,
            "name": "sort",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: offer name contains this substring."
            },
            "required": false,
            "name": "name",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: exact offer state (DRAFT, ACCEPTED, DECLINED, PUBLISHED)."
            },
            "required": false,
            "name": "state",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: offers for this project id."
            },
            "required": false,
            "name": "projectId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: offers for this customer id."
            },
            "required": false,
            "name": "customerId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ],
              "description": "Filter: indication vs detailed offers."
            },
            "required": false,
            "name": "isIndication",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Filterable, sortable, paginated list of offers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OfferList"
                }
              }
            }
          },
          "400": {
            "description": "invalid_sort",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create an offer",
        "description": "Creates a new offer (starts as DRAFT). All collections are optional — a minimal body is just `offer`.",
        "tags": [
          "Offers"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OfferInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created offer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Offer"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/offers/{id}": {
      "get": {
        "summary": "Get an offer",
        "tags": [
          "Offers"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The offer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Offer"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "summary": "Replace an offer",
        "description": "Atomically replaces the entire offer (delete + re-insert) from a full payload.",
        "tags": [
          "Offers"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OfferInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The replaced offer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Offer"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete an offer",
        "tags": [
          "Offers"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Offer deleted."
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/offers/{id}/state": {
      "post": {
        "summary": "Change offer state",
        "description": "Transition an offer's state (e.g. publish or accept). The DB trigger materializes the published/accepted snapshot.",
        "tags": [
          "Offers"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OfferStateInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The offer with its new state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Offer"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/offers/{offerId}/services": {
      "get": {
        "summary": "List offer services",
        "tags": [
          "Offer services"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "offerId",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Items on the offer.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/OfferService"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Add a service to an offer",
        "tags": [
          "Offer services"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "offerId",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OfferServiceInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created item.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OfferService"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "already_exists",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/offers/{offerId}/services/{itemId}": {
      "put": {
        "summary": "Update a service on an offer",
        "tags": [
          "Offer services"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "offerId",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "example": "7"
            },
            "required": true,
            "name": "itemId",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OfferServiceInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated item.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OfferService"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Remove a service from an offer",
        "tags": [
          "Offer services"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "offerId",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "example": "7"
            },
            "required": true,
            "name": "itemId",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Item removed."
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/offers/{offerId}/additional-costs": {
      "get": {
        "summary": "List offer additional costs",
        "tags": [
          "Offer additional costs"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "offerId",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Items on the offer.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/OfferAdditionalCost"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Add a cost line to an offer",
        "tags": [
          "Offer additional costs"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "offerId",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OfferAdditionalCostInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created item.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OfferAdditionalCost"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "already_exists",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/offers/{offerId}/additional-costs/{itemId}": {
      "put": {
        "summary": "Update a cost line on an offer",
        "tags": [
          "Offer additional costs"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "offerId",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "example": "7"
            },
            "required": true,
            "name": "itemId",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OfferAdditionalCostInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated item.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OfferAdditionalCost"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Remove a cost line from an offer",
        "tags": [
          "Offer additional costs"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "offerId",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "example": "7"
            },
            "required": true,
            "name": "itemId",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Item removed."
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/offers/{offerId}/batteries": {
      "get": {
        "summary": "List offer batteries",
        "tags": [
          "Offer batteries"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "offerId",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Items on the offer.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/OfferBattery"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Add a battery to an offer",
        "tags": [
          "Offer batteries"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "offerId",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OfferBatteryInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created item.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OfferBattery"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "already_exists",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/offers/{offerId}/batteries/{itemId}": {
      "put": {
        "summary": "Update a battery on an offer",
        "tags": [
          "Offer batteries"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "offerId",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "example": "7"
            },
            "required": true,
            "name": "itemId",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OfferBatteryInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated item.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OfferBattery"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Remove a battery from an offer",
        "tags": [
          "Offer batteries"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "offerId",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "example": "7"
            },
            "required": true,
            "name": "itemId",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Item removed."
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/offers/{offerId}/wallboxes": {
      "get": {
        "summary": "List offer wallboxes",
        "tags": [
          "Offer wallboxes"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "offerId",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Items on the offer.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/OfferWallbox"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Add a wallbox to an offer",
        "tags": [
          "Offer wallboxes"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "offerId",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OfferWallboxInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created item.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OfferWallbox"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "already_exists",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/offers/{offerId}/wallboxes/{itemId}": {
      "put": {
        "summary": "Update a wallbox on an offer",
        "tags": [
          "Offer wallboxes"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "offerId",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "example": "7"
            },
            "required": true,
            "name": "itemId",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OfferWallboxInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated item.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OfferWallbox"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Remove a wallbox from an offer",
        "tags": [
          "Offer wallboxes"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "offerId",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "example": "7"
            },
            "required": true,
            "name": "itemId",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Item removed."
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/offers/{offerId}/misc": {
      "get": {
        "summary": "List offer misc",
        "tags": [
          "Offer misc materials"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "offerId",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Items on the offer.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/OfferMisc"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Add a misc line to an offer",
        "tags": [
          "Offer misc materials"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "offerId",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OfferMiscInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created item.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OfferMisc"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "already_exists",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/offers/{offerId}/misc/{itemId}": {
      "put": {
        "summary": "Update a misc line on an offer",
        "tags": [
          "Offer misc materials"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "offerId",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "example": "7"
            },
            "required": true,
            "name": "itemId",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OfferMiscInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated item.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OfferMisc"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Remove a misc line from an offer",
        "tags": [
          "Offer misc materials"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "offerId",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "example": "7"
            },
            "required": true,
            "name": "itemId",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Item removed."
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/customers": {
      "get": {
        "summary": "List customers",
        "description": "List your company's customers. Narrow the results with the `name`, `email` and `city` filters (substring match), order with `sort` (e.g. `sort=fullName.asc`), and page with `limit`/`offset`. The response `total` is the full match count so you can page deterministically. Requires the `read:customers` scope.",
        "tags": [
          "Customers"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50,
              "description": "Maximum rows to return (1–200)."
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "minimum": 0,
              "default": 0,
              "description": "Rows to skip before returning results."
            },
            "required": false,
            "name": "offset",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "`<field>.asc` or `<field>.desc`, where `<field>` is a response field name. Each resource allows its own set; an unknown field answers 400 with the allowed list.",
              "example": "createdAt.desc"
            },
            "required": false,
            "name": "sort",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: full name contains this substring."
            },
            "required": false,
            "name": "name",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: email contains this substring."
            },
            "required": false,
            "name": "email",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: city contains this substring."
            },
            "required": false,
            "name": "city",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Filterable, sortable, paginated list of customers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerList"
                }
              }
            }
          },
          "400": {
            "description": "invalid_sort",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a customer",
        "description": "Create a customer for your company. Only `firstName` is required; everything else is optional. Returns the created customer, including its generated `id`, which you then reference when creating a project. Requires the `write:customers` scope.",
        "tags": [
          "Customers"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CustomerInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created customer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/customers/{id}": {
      "get": {
        "summary": "Get a customer",
        "tags": [
          "Customers"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The customer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update a customer",
        "tags": [
          "Customers"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CustomerInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated customer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete a customer",
        "tags": [
          "Customers"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Customer deleted."
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/offer-requests": {
      "get": {
        "summary": "List offer requests",
        "tags": [
          "Offer requests"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50,
              "description": "Maximum rows to return (1–200)."
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "minimum": 0,
              "default": 0,
              "description": "Rows to skip before returning results."
            },
            "required": false,
            "name": "offset",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "`<field>.asc` or `<field>.desc`, where `<field>` is a response field name. Each resource allows its own set; an unknown field answers 400 with the allowed list.",
              "example": "createdAt.desc"
            },
            "required": false,
            "name": "sort",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: customer full name contains this substring."
            },
            "required": false,
            "name": "name",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: exact request state."
            },
            "required": false,
            "name": "state",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: project city contains this substring."
            },
            "required": false,
            "name": "city",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Filterable, sortable, paginated list of offer requests.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OfferRequestList"
                }
              }
            }
          },
          "400": {
            "description": "invalid_sort",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/offer-requests/{id}": {
      "get": {
        "summary": "Get an offer request",
        "description": "Fetch a single offer request (Anfrage) with its customer and project details. Requires the `read:offers` scope.",
        "tags": [
          "Offer requests"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The offer request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OfferRequest"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/materials/pv-modules": {
      "get": {
        "summary": "List PV modules",
        "description": "List your pv_module catalog. Filter by `name`, `manufacturerId` or `archived`; sort and page with `limit`/`offset`. Type-specific attributes are returned under `specs`. Requires `read:materials`.",
        "tags": [
          "PV modules"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50,
              "description": "Maximum rows to return (1–200)."
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "minimum": 0,
              "default": 0,
              "description": "Rows to skip before returning results."
            },
            "required": false,
            "name": "offset",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "`<field>.asc` or `<field>.desc`, where `<field>` is a response field name. Each resource allows its own set; an unknown field answers 400 with the allowed list.",
              "example": "createdAt.desc"
            },
            "required": false,
            "name": "sort",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: name contains this substring."
            },
            "required": false,
            "name": "name",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: materials from this manufacturer id."
            },
            "required": false,
            "name": "manufacturerId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ],
              "description": "Filter: archived (true) or active (false) only."
            },
            "required": false,
            "name": "archived",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Filterable, sortable, paginated list of pv_module materials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MaterialList"
                }
              }
            }
          },
          "400": {
            "description": "invalid_sort",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a PV module",
        "description": "Add a pv_module to your catalog. Include `purchasePrice` to set what you pay for it — the catalog's net and gross selling prices are derived from that and your margins. Requires `write:materials`.",
        "tags": [
          "PV modules"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PvModuleInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created pv_module.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Material"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/materials/pv-modules/{id}": {
      "get": {
        "summary": "Get a PV module",
        "description": "Fetch one pv_module from your catalog. Requires `read:materials`.",
        "tags": [
          "PV modules"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The pv_module.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Material"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update a PV module",
        "description": "Change a pv_module. Every field is optional; omitted fields keep their current value. Sending `purchasePrice` records a new purchase price and points the material at it. Requires `write:materials`.",
        "tags": [
          "PV modules"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PvModuleInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated pv_module.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Material"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete a PV module",
        "description": "Remove a pv_module from your catalog. A product already used by an offer cannot be deleted — archive it instead (`archived: true`), which hides it from new offers while keeping existing ones intact. Requires `write:materials`.",
        "tags": [
          "PV modules"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "The pv_module was deleted."
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "in_use",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/materials/batteries": {
      "get": {
        "summary": "List batteries",
        "description": "List your battery catalog. Filter by `name`, `manufacturerId` or `archived`; sort and page with `limit`/`offset`. Type-specific attributes are returned under `specs`. Requires `read:materials`.",
        "tags": [
          "Batteries"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50,
              "description": "Maximum rows to return (1–200)."
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "minimum": 0,
              "default": 0,
              "description": "Rows to skip before returning results."
            },
            "required": false,
            "name": "offset",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "`<field>.asc` or `<field>.desc`, where `<field>` is a response field name. Each resource allows its own set; an unknown field answers 400 with the allowed list.",
              "example": "createdAt.desc"
            },
            "required": false,
            "name": "sort",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: name contains this substring."
            },
            "required": false,
            "name": "name",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: materials from this manufacturer id."
            },
            "required": false,
            "name": "manufacturerId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ],
              "description": "Filter: archived (true) or active (false) only."
            },
            "required": false,
            "name": "archived",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Filterable, sortable, paginated list of battery materials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MaterialList"
                }
              }
            }
          },
          "400": {
            "description": "invalid_sort",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a battery",
        "description": "Add a battery to your catalog. Include `purchasePrice` to set what you pay for it — the catalog's net and gross selling prices are derived from that and your margins. Requires `write:materials`.",
        "tags": [
          "Batteries"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BatteryInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created battery.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Material"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/materials/batteries/{id}": {
      "get": {
        "summary": "Get a battery",
        "description": "Fetch one battery from your catalog. Requires `read:materials`.",
        "tags": [
          "Batteries"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The battery.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Material"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update a battery",
        "description": "Change a battery. Every field is optional; omitted fields keep their current value. Sending `purchasePrice` records a new purchase price and points the material at it. Requires `write:materials`.",
        "tags": [
          "Batteries"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BatteryInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated battery.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Material"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete a battery",
        "description": "Remove a battery from your catalog. A product already used by an offer cannot be deleted — archive it instead (`archived: true`), which hides it from new offers while keeping existing ones intact. Requires `write:materials`.",
        "tags": [
          "Batteries"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "The battery was deleted."
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "in_use",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/materials/inverters": {
      "get": {
        "summary": "List inverters",
        "description": "List your inverter catalog. Filter by `name`, `manufacturerId` or `archived`; sort and page with `limit`/`offset`. Type-specific attributes are returned under `specs`. Requires `read:materials`.",
        "tags": [
          "Inverters"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50,
              "description": "Maximum rows to return (1–200)."
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "minimum": 0,
              "default": 0,
              "description": "Rows to skip before returning results."
            },
            "required": false,
            "name": "offset",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "`<field>.asc` or `<field>.desc`, where `<field>` is a response field name. Each resource allows its own set; an unknown field answers 400 with the allowed list.",
              "example": "createdAt.desc"
            },
            "required": false,
            "name": "sort",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: name contains this substring."
            },
            "required": false,
            "name": "name",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: materials from this manufacturer id."
            },
            "required": false,
            "name": "manufacturerId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ],
              "description": "Filter: archived (true) or active (false) only."
            },
            "required": false,
            "name": "archived",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Filterable, sortable, paginated list of inverter materials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MaterialList"
                }
              }
            }
          },
          "400": {
            "description": "invalid_sort",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create an inverter",
        "description": "Add a inverter to your catalog. Include `purchasePrice` to set what you pay for it — the catalog's net and gross selling prices are derived from that and your margins. Requires `write:materials`.",
        "tags": [
          "Inverters"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InverterInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created inverter.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Material"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/materials/inverters/{id}": {
      "get": {
        "summary": "Get an inverter",
        "description": "Fetch one inverter from your catalog. Requires `read:materials`.",
        "tags": [
          "Inverters"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The inverter.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Material"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update an inverter",
        "description": "Change a inverter. Every field is optional; omitted fields keep their current value. Sending `purchasePrice` records a new purchase price and points the material at it. Requires `write:materials`.",
        "tags": [
          "Inverters"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InverterInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated inverter.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Material"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete an inverter",
        "description": "Remove a inverter from your catalog. A product already used by an offer cannot be deleted — archive it instead (`archived: true`), which hides it from new offers while keeping existing ones intact. Requires `write:materials`.",
        "tags": [
          "Inverters"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "The inverter was deleted."
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "in_use",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/materials/wallboxes": {
      "get": {
        "summary": "List wallboxes",
        "description": "List your wallbox catalog. Filter by `name`, `manufacturerId` or `archived`; sort and page with `limit`/`offset`. Type-specific attributes are returned under `specs`. Requires `read:materials`.",
        "tags": [
          "Wallboxes"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50,
              "description": "Maximum rows to return (1–200)."
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "minimum": 0,
              "default": 0,
              "description": "Rows to skip before returning results."
            },
            "required": false,
            "name": "offset",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "`<field>.asc` or `<field>.desc`, where `<field>` is a response field name. Each resource allows its own set; an unknown field answers 400 with the allowed list.",
              "example": "createdAt.desc"
            },
            "required": false,
            "name": "sort",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: name contains this substring."
            },
            "required": false,
            "name": "name",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: materials from this manufacturer id."
            },
            "required": false,
            "name": "manufacturerId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ],
              "description": "Filter: archived (true) or active (false) only."
            },
            "required": false,
            "name": "archived",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Filterable, sortable, paginated list of wallbox materials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MaterialList"
                }
              }
            }
          },
          "400": {
            "description": "invalid_sort",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a wallbox",
        "description": "Add a wallbox to your catalog. Include `purchasePrice` to set what you pay for it — the catalog's net and gross selling prices are derived from that and your margins. Requires `write:materials`.",
        "tags": [
          "Wallboxes"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WallboxInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created wallbox.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Material"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/materials/wallboxes/{id}": {
      "get": {
        "summary": "Get a wallbox",
        "description": "Fetch one wallbox from your catalog. Requires `read:materials`.",
        "tags": [
          "Wallboxes"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The wallbox.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Material"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update a wallbox",
        "description": "Change a wallbox. Every field is optional; omitted fields keep their current value. Sending `purchasePrice` records a new purchase price and points the material at it. Requires `write:materials`.",
        "tags": [
          "Wallboxes"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WallboxInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated wallbox.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Material"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete a wallbox",
        "description": "Remove a wallbox from your catalog. A product already used by an offer cannot be deleted — archive it instead (`archived: true`), which hides it from new offers while keeping existing ones intact. Requires `write:materials`.",
        "tags": [
          "Wallboxes"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "The wallbox was deleted."
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "in_use",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/materials/equipment": {
      "get": {
        "summary": "List equipment",
        "description": "List your equipment catalog. Filter by `name`, `manufacturerId` or `archived`; sort and page with `limit`/`offset`. Type-specific attributes are returned under `specs`. Requires `read:materials`.",
        "tags": [
          "Equipment"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50,
              "description": "Maximum rows to return (1–200)."
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "minimum": 0,
              "default": 0,
              "description": "Rows to skip before returning results."
            },
            "required": false,
            "name": "offset",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "`<field>.asc` or `<field>.desc`, where `<field>` is a response field name. Each resource allows its own set; an unknown field answers 400 with the allowed list.",
              "example": "createdAt.desc"
            },
            "required": false,
            "name": "sort",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: name contains this substring."
            },
            "required": false,
            "name": "name",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: materials from this manufacturer id."
            },
            "required": false,
            "name": "manufacturerId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ],
              "description": "Filter: archived (true) or active (false) only."
            },
            "required": false,
            "name": "archived",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Filterable, sortable, paginated list of equipment materials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MaterialList"
                }
              }
            }
          },
          "400": {
            "description": "invalid_sort",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create an equipment item",
        "description": "Add a equipment to your catalog. Include `purchasePrice` to set what you pay for it — the catalog's net and gross selling prices are derived from that and your margins. Requires `write:materials`.",
        "tags": [
          "Equipment"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EquipmentInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created equipment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Material"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/materials/equipment/{id}": {
      "get": {
        "summary": "Get an equipment item",
        "description": "Fetch one equipment from your catalog. Requires `read:materials`.",
        "tags": [
          "Equipment"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The equipment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Material"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update an equipment item",
        "description": "Change a equipment. Every field is optional; omitted fields keep their current value. Sending `purchasePrice` records a new purchase price and points the material at it. Requires `write:materials`.",
        "tags": [
          "Equipment"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EquipmentInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated equipment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Material"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete an equipment item",
        "description": "Remove a equipment from your catalog. A product already used by an offer cannot be deleted — archive it instead (`archived: true`), which hides it from new offers while keeping existing ones intact. Requires `write:materials`.",
        "tags": [
          "Equipment"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "The equipment was deleted."
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "in_use",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/materials/misc": {
      "get": {
        "summary": "List misc materials",
        "description": "List your misc catalog. Filter by `name`, `manufacturerId` or `archived`; sort and page with `limit`/`offset`. Type-specific attributes are returned under `specs`. Requires `read:materials`.",
        "tags": [
          "Misc materials"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50,
              "description": "Maximum rows to return (1–200)."
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "minimum": 0,
              "default": 0,
              "description": "Rows to skip before returning results."
            },
            "required": false,
            "name": "offset",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "`<field>.asc` or `<field>.desc`, where `<field>` is a response field name. Each resource allows its own set; an unknown field answers 400 with the allowed list.",
              "example": "createdAt.desc"
            },
            "required": false,
            "name": "sort",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: name contains this substring."
            },
            "required": false,
            "name": "name",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: materials from this manufacturer id."
            },
            "required": false,
            "name": "manufacturerId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ],
              "description": "Filter: archived (true) or active (false) only."
            },
            "required": false,
            "name": "archived",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Filterable, sortable, paginated list of misc materials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MaterialList"
                }
              }
            }
          },
          "400": {
            "description": "invalid_sort",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a misc material",
        "description": "Add a misc to your catalog. Include `purchasePrice` to set what you pay for it — the catalog's net and gross selling prices are derived from that and your margins. Requires `write:materials`.",
        "tags": [
          "Misc materials"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MiscMaterialInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created misc.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Material"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/materials/misc/{id}": {
      "get": {
        "summary": "Get a misc material",
        "description": "Fetch one misc from your catalog. Requires `read:materials`.",
        "tags": [
          "Misc materials"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The misc.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Material"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update a misc material",
        "description": "Change a misc. Every field is optional; omitted fields keep their current value. Sending `purchasePrice` records a new purchase price and points the material at it. Requires `write:materials`.",
        "tags": [
          "Misc materials"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MiscMaterialInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated misc.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Material"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete a misc material",
        "description": "Remove a misc from your catalog. A product already used by an offer cannot be deleted — archive it instead (`archived: true`), which hides it from new offers while keeping existing ones intact. Requires `write:materials`.",
        "tags": [
          "Misc materials"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "The misc was deleted."
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "in_use",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/materials/subconstruction": {
      "get": {
        "summary": "List subconstruction",
        "description": "List your subconstruction catalog. Filter by `name`, `manufacturerId` or `archived`; sort and page with `limit`/`offset`. Type-specific attributes are returned under `specs`. Requires `read:materials`.",
        "tags": [
          "Subconstruction"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50,
              "description": "Maximum rows to return (1–200)."
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "minimum": 0,
              "default": 0,
              "description": "Rows to skip before returning results."
            },
            "required": false,
            "name": "offset",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "`<field>.asc` or `<field>.desc`, where `<field>` is a response field name. Each resource allows its own set; an unknown field answers 400 with the allowed list.",
              "example": "createdAt.desc"
            },
            "required": false,
            "name": "sort",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: name contains this substring."
            },
            "required": false,
            "name": "name",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: materials from this manufacturer id."
            },
            "required": false,
            "name": "manufacturerId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ],
              "description": "Filter: archived (true) or active (false) only."
            },
            "required": false,
            "name": "archived",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Filterable, sortable, paginated list of subconstruction materials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MaterialList"
                }
              }
            }
          },
          "400": {
            "description": "invalid_sort",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a subconstruction",
        "description": "Add a subconstruction to your catalog. Include `purchasePrice` to set what you pay for it — the catalog's net and gross selling prices are derived from that and your margins. Requires `write:materials`.",
        "tags": [
          "Subconstruction"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubconstructionInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created subconstruction.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Material"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/materials/subconstruction/{id}": {
      "get": {
        "summary": "Get a subconstruction",
        "description": "Fetch one subconstruction from your catalog. Requires `read:materials`.",
        "tags": [
          "Subconstruction"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The subconstruction.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Material"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update a subconstruction",
        "description": "Change a subconstruction. Every field is optional; omitted fields keep their current value. Sending `purchasePrice` records a new purchase price and points the material at it. Requires `write:materials`.",
        "tags": [
          "Subconstruction"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubconstructionInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated subconstruction.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Material"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete a subconstruction",
        "description": "Remove a subconstruction from your catalog. A product already used by an offer cannot be deleted — archive it instead (`archived: true`), which hides it from new offers while keeping existing ones intact. Requires `write:materials`.",
        "tags": [
          "Subconstruction"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "The subconstruction was deleted."
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "in_use",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/materials/emergency-powers": {
      "get": {
        "summary": "List emergency power products",
        "description": "List your emergency_power catalog. Filter by `name`, `manufacturerId` or `archived`; sort and page with `limit`/`offset`. Type-specific attributes are returned under `specs`. Requires `read:materials`.",
        "tags": [
          "Emergency power"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50,
              "description": "Maximum rows to return (1–200)."
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "minimum": 0,
              "default": 0,
              "description": "Rows to skip before returning results."
            },
            "required": false,
            "name": "offset",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "`<field>.asc` or `<field>.desc`, where `<field>` is a response field name. Each resource allows its own set; an unknown field answers 400 with the allowed list.",
              "example": "createdAt.desc"
            },
            "required": false,
            "name": "sort",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: name contains this substring."
            },
            "required": false,
            "name": "name",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: materials from this manufacturer id."
            },
            "required": false,
            "name": "manufacturerId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ],
              "description": "Filter: archived (true) or active (false) only."
            },
            "required": false,
            "name": "archived",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Filterable, sortable, paginated list of emergency_power materials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MaterialList"
                }
              }
            }
          },
          "400": {
            "description": "invalid_sort",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create an emergency power",
        "description": "Add a emergency_power to your catalog. Include `purchasePrice` to set what you pay for it — the catalog's net and gross selling prices are derived from that and your margins. Requires `write:materials`.",
        "tags": [
          "Emergency power"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EmergencyPowerMaterialInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created emergency_power.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Material"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/materials/emergency-powers/{id}": {
      "get": {
        "summary": "Get an emergency power",
        "description": "Fetch one emergency_power from your catalog. Requires `read:materials`.",
        "tags": [
          "Emergency power"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The emergency_power.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Material"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update an emergency power",
        "description": "Change a emergency_power. Every field is optional; omitted fields keep their current value. Sending `purchasePrice` records a new purchase price and points the material at it. Requires `write:materials`.",
        "tags": [
          "Emergency power"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EmergencyPowerMaterialInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated emergency_power.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Material"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete an emergency power",
        "description": "Remove a emergency_power from your catalog. A product already used by an offer cannot be deleted — archive it instead (`archived: true`), which hides it from new offers while keeping existing ones intact. Requires `write:materials`.",
        "tags": [
          "Emergency power"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "The emergency_power was deleted."
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "in_use",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/manufacturers": {
      "get": {
        "summary": "List manufacturers",
        "tags": [
          "Manufacturers"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50,
              "description": "Maximum rows to return (1–200)."
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "minimum": 0,
              "default": 0,
              "description": "Rows to skip before returning results."
            },
            "required": false,
            "name": "offset",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "`<field>.asc` or `<field>.desc`, where `<field>` is a response field name. Each resource allows its own set; an unknown field answers 400 with the allowed list.",
              "example": "createdAt.desc"
            },
            "required": false,
            "name": "sort",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: name contains this substring."
            },
            "required": false,
            "name": "name",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Filterable, sortable, paginated list.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ManufacturerList"
                }
              }
            }
          },
          "400": {
            "description": "invalid_sort",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a manufacturer",
        "tags": [
          "Manufacturers"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ManufacturerInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created manufacturer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Manufacturer"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/manufacturers/{id}": {
      "get": {
        "summary": "Get a manufacturer",
        "tags": [
          "Manufacturers"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The manufacturer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Manufacturer"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update a manufacturer",
        "tags": [
          "Manufacturers"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ManufacturerInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated manufacturer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Manufacturer"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete a manufacturer",
        "tags": [
          "Manufacturers"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Manufacturer deleted."
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "in_use",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/services": {
      "get": {
        "summary": "List services",
        "tags": [
          "Services"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50,
              "description": "Maximum rows to return (1–200)."
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "minimum": 0,
              "default": 0,
              "description": "Rows to skip before returning results."
            },
            "required": false,
            "name": "offset",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "`<field>.asc` or `<field>.desc`, where `<field>` is a response field name. Each resource allows its own set; an unknown field answers 400 with the allowed list.",
              "example": "createdAt.desc"
            },
            "required": false,
            "name": "sort",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: name contains this substring."
            },
            "required": false,
            "name": "name",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ],
              "description": "Filter: services included in offers by default."
            },
            "required": false,
            "name": "includedInOffer",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Filterable, sortable, paginated list.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceList"
                }
              }
            }
          },
          "400": {
            "description": "invalid_sort",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a service",
        "tags": [
          "Services"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ServiceInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created service.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Service"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/services/{id}": {
      "get": {
        "summary": "Get a service",
        "tags": [
          "Services"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The service.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Service"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update a service",
        "tags": [
          "Services"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ServiceInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated service.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Service"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete a service",
        "tags": [
          "Services"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "42"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "204": {
            "description": "Service deleted."
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "in_use",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/company": {
      "get": {
        "summary": "Get your company",
        "tags": [
          "Company"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "The caller's company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Company"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update your company",
        "tags": [
          "Company"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CompanyInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Company"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/analytics/offers": {
      "get": {
        "summary": "Offers over time",
        "description": "How many offers created, bucketed `daily`, `weekly` or `monthly`. Returns the whole requested window — there is no paging — so bound it with `from`/`to`; without them you get the last 12 months. Requires the `read:analytics` scope.",
        "tags": [
          "Analytics"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "description": "Start of the window (YYYY-MM-DD). Defaults to 12 months before `to`.",
              "example": "2026-01-01"
            },
            "required": false,
            "name": "from",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "description": "End of the window (YYYY-MM-DD). Defaults to today.",
              "example": "2026-12-31"
            },
            "required": false,
            "name": "to",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "daily",
                "weekly",
                "monthly"
              ],
              "default": "monthly",
              "description": "Bucket size for the series."
            },
            "required": false,
            "name": "interval",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "One point per bucket, oldest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyticsSeries"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/analytics/offer-requests": {
      "get": {
        "summary": "Offer requests over time",
        "description": "How many offer requests received, bucketed `daily`, `weekly` or `monthly`. Returns the whole requested window — there is no paging — so bound it with `from`/`to`; without them you get the last 12 months. Requires the `read:analytics` scope.",
        "tags": [
          "Analytics"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "description": "Start of the window (YYYY-MM-DD). Defaults to 12 months before `to`.",
              "example": "2026-01-01"
            },
            "required": false,
            "name": "from",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "description": "End of the window (YYYY-MM-DD). Defaults to today.",
              "example": "2026-12-31"
            },
            "required": false,
            "name": "to",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "daily",
                "weekly",
                "monthly"
              ],
              "default": "monthly",
              "description": "Bucket size for the series."
            },
            "required": false,
            "name": "interval",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "One point per bucket, oldest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyticsSeries"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/analytics/conversions": {
      "get": {
        "summary": "Request conversion rates",
        "description": "How many incoming offer requests went on to become an indication offer, a detailed offer, or expired without one. `count` is how many requests fell into the bucket and `converted` how many of them converted, so the rate is `converted / count`. Returns the whole requested window. Requires the `read:analytics` scope.",
        "tags": [
          "Analytics"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "description": "Start of the window (YYYY-MM-DD). Defaults to 12 months before `to`.",
              "example": "2026-01-01"
            },
            "required": false,
            "name": "from",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "description": "End of the window (YYYY-MM-DD). Defaults to today.",
              "example": "2026-12-31"
            },
            "required": false,
            "name": "to",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "indication",
                "detailed",
                "expired"
              ],
              "default": "detailed",
              "description": "Which conversion to report."
            },
            "required": false,
            "name": "type",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "One point per bucket, oldest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyticsConversions"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/analytics/demographics": {
      "get": {
        "summary": "Customer demographics",
        "description": "Headline counts for your customer base. Requires the `read:analytics` scope.",
        "tags": [
          "Analytics"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Customer counts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyticsDemographics"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/analytics/product-sales": {
      "get": {
        "summary": "Product sales",
        "description": "What you actually sold, per product and day. A product counts as sold once its project has an accepted detailed offer. Filter by `category` or `materialId`, bound with `from`/`to`, and page as usual. Requires the `read:analytics` scope.",
        "tags": [
          "Analytics"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50,
              "description": "Maximum rows to return (1–200)."
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "minimum": 0,
              "default": 0,
              "description": "Rows to skip before returning results."
            },
            "required": false,
            "name": "offset",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "`<field>.asc` or `<field>.desc`, where `<field>` is a response field name. Each resource allows its own set; an unknown field answers 400 with the allowed list.",
              "example": "createdAt.desc"
            },
            "required": false,
            "name": "sort",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "description": "Start of the window (YYYY-MM-DD). Defaults to 12 months before `to`.",
              "example": "2026-01-01"
            },
            "required": false,
            "name": "from",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "description": "End of the window (YYYY-MM-DD). Defaults to today.",
              "example": "2026-12-31"
            },
            "required": false,
            "name": "to",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: product family."
            },
            "required": false,
            "name": "category",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: a single material."
            },
            "required": false,
            "name": "materialId",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated sales rows.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyticsProductSaleList"
                }
              }
            }
          },
          "400": {
            "description": "invalid_sort",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/analytics/open-offers": {
      "get": {
        "summary": "Products in open offers",
        "description": "What is sitting in offers that are still open — the pipeline, per product. Useful for forecasting demand before anything is accepted. Requires the `read:analytics` scope.",
        "tags": [
          "Analytics"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50,
              "description": "Maximum rows to return (1–200)."
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "minimum": 0,
              "default": 0,
              "description": "Rows to skip before returning results."
            },
            "required": false,
            "name": "offset",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "`<field>.asc` or `<field>.desc`, where `<field>` is a response field name. Each resource allows its own set; an unknown field answers 400 with the allowed list.",
              "example": "createdAt.desc"
            },
            "required": false,
            "name": "sort",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: product family."
            },
            "required": false,
            "name": "category",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter: a single material."
            },
            "required": false,
            "name": "materialId",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated pipeline rows.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyticsOpenOfferList"
                }
              }
            }
          },
          "400": {
            "description": "invalid_sort",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "db_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/offers/{id}/network-carrier-documents": {
      "post": {
        "summary": "Generate grid-operator documents",
        "description": "Fill the grid operator's (Netzbetreiber) registration forms for an offer and return them as a ZIP archive. The documents are rendered on demand from the offer's current technical data and are not stored, so each call produces a fresh archive.\n\nThis is a `POST` because the request carries a body — the operator, which of its documents to produce, and what to put on them — but it changes nothing. Requires the `read:documents` scope.",
        "tags": [
          "Documents"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "812"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NetworkCarrierDocumentsInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A ZIP archive with one PDF per requested document, plus the merged PDF when asked for.",
            "content": {
              "application/zip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "validation_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "generation_failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/offers/{id}/string-plan": {
      "get": {
        "summary": "Generate the string plan",
        "description": "Render the offer's string plan — how the modules are wired into the inverters' MPP trackers — as a PDF. Generated on demand from the offer's current configuration and not stored, so each call reflects the offer as it is now. Requires the `read:documents` scope.",
        "tags": [
          "Documents"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "812"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The string plan as a PDF.",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "generation_failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  }
}
