{
  "openapi": "3.0.3",
  "info": {
    "title": "Reprix API",
    "version": "1.8.0",
    "description": "REST-API van Reprix voor het aanmaken en opvragen van orders en facturen, het beheren van eigen ordernummer-reeksen en het ophalen van factuur-PDF's, vanuit je eigen software (boekhoudpakket, webshop, automatisering).\n\n## Authenticatie\nMaak een API-key aan in de Reprix-app onder **Instellingen → API**. Stuur de key mee met elke request, op één van twee manieren:\n\n- `Authorization: Bearer rxk_…`\n- `X-Api-Key: rxk_…`\n\nDe key wordt maar één keer getoond bij het aanmaken. Keys gelden voor alle Bol-accounts die aan je Reprix-account gekoppeld zijn, als eigenaar of als teamlid.\n\n## Rate limit\nMaximaal **60 requests per minuut per Reprix-account** (alle keys en alle endpoints samen, vast minuutvenster). Daarboven krijg je `429 rate_limited` met een `Retry-After`-header (in seconden).\n\n## retailerId\nElk endpoint vereist een `retailerId`: het Bol-verkopersnummer van een account dat aan jouw Reprix-account gekoppeld is (als eigenaar of als teamlid). Een `retailerId` dat niet aan je account gekoppeld is geeft `404 not_linked`.\n\n## Facturen\n- **Direct en definitief:** alles wat je via de API instuurt of wijzigt wordt meteen verwerkt. Een aangemaakte factuur is geen concept: hij krijgt direct een definitief factuurnummer en is vanaf dat moment meteen voor de klant beschikbaar (bijvoorbeeld via de factuurlink of een gekoppelde factuurmailing).\n- **Vastgelegde PDF:** de factuur-PDF van een handmatige order wordt bij uitgifte vastgelegd (snapshot). `url` en `pdfUrl` leveren daarna altijd exact dat document, ook als het factuurtemplate later wijzigt; er is geen live weergave meer. Correcties lopen via een creditfactuur plus nieuwe factuur.\n- Een factuurnummer wordt alleen toegekend als facturering voor het account actief is (Reprix-app → Facturen → Facturatie-instellingen); anders bevat de response `invoiceError: numbering_not_active` en staat de order er wél, zonder factuur.\n- Voor het renderen van een PDF is minimaal één factuurtemplate voor het account nodig (`doc_no_template` anders).\n- De `url`/`pdfUrl` in responses zijn **permanente, niet te raden HMAC-links** die zonder API-key werken; handig om door te geven aan bijv. een boekhoudpakket. Wil je die links niet delen, gebruik dan `GET /invoice_pdf.php` (zelfde PDF, achter de key).\n- **Corrigeren:** klantgegevens op een factuur pas je aan via `GET`/`POST /invoice_correct.php`. Een al uitgegeven factuur wordt daarbij gecrediteerd en vervangen door een nieuwe factuur in dezelfde reeks; een nog niet uitgegeven Bol-factuur wordt op de bestaande factuur aangepast. Met `reverseCharge: true` verleg je in dezelfde stap de btw (intracommunautaire levering, 0%): het btw-nummer gaat via VIES, de oude factuur wordt gecrediteerd en de nieuwe factuur toont de regels netto; `totals.refund` is de btw die je aan de klant terugbetaalt. Met `preview: true` zie je vooraf de controles en bedragen, zonder dat er iets wordt aangemaakt.\n\n## Fouten\nFouten zijn altijd JSON: `{\"error\": \"machineleesbare_code\"}`, soms met extra velden (bijv. `max` of `index`). De relevante codes staan per endpoint gedocumenteerd.",
    "contact": { "name": "Reprix", "url": "https://reprix.nl" }
  },
  "servers": [
    { "url": "https://reprix.nl/api/v1", "description": "Productie" }
  ],
  "security": [
    { "bearerAuth": [] },
    { "apiKeyHeader": [] }
  ],
  "tags": [
    { "name": "Reeksen", "description": "Eigen ordernummer-reeksen (voorvoegsel + oplopende teller, per Bol-account). Bij het aanmaken van een order via een reeks schuift de teller automatisch door tot een vrij nummer." },
    { "name": "Templates", "description": "Factuurtemplates van een Bol-account (alleen id, naam en de default-markering). Het id is bruikbaar als templateId bij het aanmaken van een order." },
    { "name": "Orders", "description": "Handmatige orders/facturen aanmaken en orders (ook Bol-orders) opvragen." },
    { "name": "Facturen", "description": "Factuur-PDF's ophalen." }
  ],
  "paths": {
    "/series.php": {
      "get": {
        "tags": ["Reeksen"],
        "summary": "Ordernummer-reeksen opvragen",
        "operationId": "listSeries",
        "description": "Een reeks kan ook als factuurnummering dienen: geef bij POST /orders.php `invoiceSeriesId` mee.",
        "parameters": [ { "$ref": "#/components/parameters/retailerId" } ],
        "responses": {
          "200": {
            "description": "De reeksen van dit Bol-account.",
            "content": { "application/json": { "schema": {
              "type": "object",
              "properties": {
                "ok": { "type": "boolean", "example": true },
                "series": { "type": "array", "items": { "$ref": "#/components/schemas/Series" } }
              }
            }, "example": { "ok": true, "series": [ { "id": 1, "prefix": "WEB-", "nextNumber": 1021 } ] } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotLinked" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      },
      "post": {
        "tags": ["Reeksen"],
        "summary": "Reeks aanmaken",
        "operationId": "createSeries",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": {
            "type": "object",
            "required": ["retailerId", "prefix"],
            "properties": {
              "retailerId": { "$ref": "#/components/schemas/RetailerId" },
              "prefix": { "type": "string", "maxLength": 20, "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$", "description": "Voorvoegsel van de reeks, uniek per Bol-account.", "example": "WEB-" },
              "nextNumber": { "type": "integer", "minimum": 1, "maximum": 99999999, "default": 1, "description": "Startnummer van de teller.", "example": 1000 }
            }
          }, "example": { "retailerId": "1234567", "prefix": "WEB-", "nextNumber": 1000 } } }
        },
        "responses": {
          "201": {
            "description": "Reeks aangemaakt; de response bevat de volledige, verse reekslijst.",
            "content": { "application/json": { "schema": {
              "type": "object",
              "properties": {
                "ok": { "type": "boolean", "example": true },
                "series": { "type": "array", "items": { "$ref": "#/components/schemas/Series" } }
              }
            } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotLinked" },
          "409": { "description": "Er bestaat al een reeks met dit voorvoegsel voor dit account.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "series_exists" } } } },
          "422": { "description": "Ongeldige invoer. Codes: `invalid_prefix` (leeg, te lang of ongeldige tekens; extra veld `max`), `invalid_number` (buiten 1..99999999; extra veld `max`).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "invalid_prefix", "max": 20 } } } },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/templates.php": {
      "get": {
        "tags": ["Templates"],
        "summary": "Factuurtemplates opvragen",
        "description": "Alleen id, naam en de default-markering; de template-inhoud zelf is via de API niet opvraagbaar. Gebruik het id als `templateId` bij `POST /orders.php` om de factuur op dat template te renderen.",
        "operationId": "listTemplates",
        "parameters": [ { "$ref": "#/components/parameters/retailerId" } ],
        "responses": {
          "200": {
            "description": "De actieve templates van dit Bol-account, default eerst.",
            "content": { "application/json": { "schema": {
              "type": "object",
              "properties": {
                "ok": { "type": "boolean", "example": true },
                "templates": { "type": "array", "items": { "$ref": "#/components/schemas/Template" } }
              }
            }, "example": { "ok": true, "templates": [ { "id": 4, "name": "Bol NL-FR", "isDefault": true } ] } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotLinked" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/orders.php": {
      "post": {
        "tags": ["Orders"],
        "summary": "Order + factuur aanmaken",
        "description": "Maakt een handmatige order aan (zichtbaar in de Reprix-app met de badge 'Handmatig') en kent desgewenst meteen een factuurnummer toe. Prijzen zijn **inclusief btw**. Het ordernummer komt uit een eigen reeks (`seriesId`, teller schuift door tot een vrij nummer) óf wordt handmatig meegegeven (`orderNumber`, uniek per Bol-account). De factuurdatum mag niet in de toekomst liggen; de besteldatum van de order volgt de factuurdatum. De verwerking is direct en definitief, er is geen conceptfase: zodra de request slaagt bestaat de factuur echt en kan de klant hem meteen ontvangen. Met het optionele veld `discount` geef je een korting op orderniveau mee; Reprix verdeelt die pro rata over de regels zodat de btw-uitsplitsing klopt. Regels kunnen een eigen regelkorting dragen (items[].discount) en met `invoiceSeriesId` komt het factuurnummer uit een eigen reeks in plaats van de hoofdreeks. Met het optionele veld `seller` geef je afwijkende verkopersgegevens mee voor alleen deze factuur, bijvoorbeeld een ander btw-nummer; lege of ontbrekende velden houden de accountwaarde.",
        "operationId": "createOrder",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OrderCreateRequest" }, "example": {
            "retailerId": "1234567",
            "seriesId": 1,
            "invoiceDate": "2026-07-15",
            "invoiceSeries": "main",
            "language": "nl",
            "templateId": 4,
            "customer": {
              "company": "Voorbeeld BV",
              "firstName": "Jan",
              "surname": "Jansen",
              "email": "administratie@voorbeeld.nl",
              "street": "Hoofdstraat",
              "houseNumber": "1",
              "houseExt": "A",
              "zipcode": "1234 AB",
              "city": "Amsterdam",
              "country": "NL",
              "vatNumber": "NL123456789B01",
              "kvkNumber": "12345678",
              "orderReference": "PO-2026-001"
            },
            "items": [
              { "title": "Voorbeeldproduct", "ean": "8712345678906", "quantity": 2, "unitPrice": 12.50, "vatPercentage": 21 },
              { "title": "Tweedehands artikel (marge)", "quantity": 1, "unitPrice": 50, "isMargin": true }
            ],
            "discount": { "amount": 4, "description": "Kortingscode WELKOM10" },
            "seller": { "vat": "DE123456789" }
          } } }
        },
        "responses": {
          "201": {
            "description": "Order aangemaakt. `invoice` is null als `invoiceSeries` 'none' was of het minten mislukte; in dat laatste geval staat de code in `invoiceError` (de order zelf staat er dan gewoon en kan later alsnog een nummer krijgen via de app). Mogelijke `invoiceError`-codes: `numbering_not_active`, `number_cap_reached`, `has_invoice`, `order_not_invoiceable`, `invoice_mint_failed` (onverwachte serverfout bij het minten). Nieuw sinds 1.5.0: `plan_limit_reached` wanneer de maandlimiet van het Free-plan voor factuur-aanmaak is bereikt; de factuur kan na een upgrade of in de nieuwe maand alsnog via de app worden aangemaakt.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OrderCreateResponse" }, "example": {
              "ok": true,
              "orderId": "WEB-1021",
              "invoice": {
                "id": 5684,
                "number": "F2026-1487",
                "url": "https://reprix.nl/api/invoice_render.php?uid=…&rid=…&order=WEB-1021&inv=5684&k=…",
                "pdfUrl": "https://reprix.nl/api/invoice_render.php?uid=…&rid=…&order=WEB-1021&inv=5684&k=…&pdf=1"
              },
              "invoiceError": null
            } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "description": "`not_linked` (retailerId niet van jou) of `series_not_found` (onbekende seriesId).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "series_not_found" } } } },
          "409": { "description": "Het ordernummer bestaat al voor dit Bol-account.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "order_exists" } } } },
          "422": { "description": "Ongeldige invoer. Codes: `customer_required` (bedrijfsnaam of achternaam verplicht), `invalid_email`, `invalid_country`, `items_required` (1..100 regels), `invalid_item`, `item_title_required`, `invalid_quantity` (1..9999), `invalid_price` (0..999999), `invalid_vat` (0..99.99), `invalid_ean` (8-14 cijfers), `invalid_date`, `date_in_future`, `invalid_series` (invoiceSeries), `invalid_order_number`, `invalid_template`, `number_cap_reached`. Regel-fouten bevatten het extra veld `index` (0-based). Korting: `invalid_discount` (bedrag ≤ 0/niet numeriek, of label leeg/langer dan 120 tekens) en `discount_exceeds_total` (bedrag ≥ regeltotaal). Regelkorting: `invalid_item_discount` en `item_discount_exceeds_line` (met `index`). Factuurreeks: `invoice_series_not_found` (onbekend invoiceSeriesId) en `invoice_series_conflict` (invoiceSeries en invoiceSeriesId tegelijk).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "invalid_quantity", "index": 1 } } } },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      },
      "get": {
        "tags": ["Orders"],
        "summary": "Orders opvragen (lijst of één order)",
        "description": "Zonder `orderId`: een gepagineerde lijst (nieuwste eerst), inclusief Bol-orders. Met `orderId`: één order, met volledige adresblokken en álle factuurdocumenten (factuur/creditfactuur, chronologisch).",
        "operationId": "listOrders",
        "parameters": [
          { "$ref": "#/components/parameters/retailerId" },
          { "name": "orderId", "in": "query", "required": false, "schema": { "type": "string" }, "description": "Ordernummer; indien gezet wordt één order geretourneerd (`{ok, order}` in plaats van een lijst)." },
          { "name": "page", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 0, "default": 0 }, "description": "0-based paginanummer." },
          { "name": "size", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 }, "description": "Aantal orders per pagina." },
          { "name": "view", "in": "query", "required": false, "schema": { "type": "string", "enum": ["all", "bol", "custom"], "default": "all" }, "description": "`bol` = alleen Bol-orders, `custom` = alleen handmatig (via app of API) aangemaakte orders." },
          { "name": "q", "in": "query", "required": false, "schema": { "type": "string" }, "description": "Zoekterm; matcht op ordernummer, bedrijfsnaam en achternaam." }
        ],
        "responses": {
          "200": {
            "description": "Lijst (`{ok, orders, total, page, size}`) of, met `orderId`, één order (`{ok, order}`).",
            "content": { "application/json": { "schema": { "oneOf": [
              { "type": "object", "title": "Lijst", "properties": {
                "ok": { "type": "boolean" },
                "orders": { "type": "array", "items": { "$ref": "#/components/schemas/OrderListItem" } },
                "total": { "type": "integer", "description": "Totaal aantal orders na filtering." },
                "page": { "type": "integer" },
                "size": { "type": "integer" }
              } },
              { "type": "object", "title": "Eén order", "properties": {
                "ok": { "type": "boolean" },
                "order": { "$ref": "#/components/schemas/OrderDetail" }
              } }
            ] } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "description": "`not_linked` of, met `orderId`, `order_not_found`.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "order_not_found" } } } },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/invoice_correct.php": {
      "get": {
        "tags": [
          "Facturen"
        ],
        "summary": "Huidige klantgegevens op de factuur opvragen",
        "description": "Geeft terug wat er nu op de actieve (ongecrediteerde) factuur van de order staat: de klantgegevens (`details`), de oorspronkelijke gegevens uit de bron-payload (`original`, null bij handmatige orders) en `locked`. `locked` = de factuur is al uitgegeven (naar Bol geüpload, gemaild, als PDF gedownload of via de API opgehaald, of van een handmatige order): een correctie via POST levert dan altijd een creditfactuur plus een nieuwe factuur op. `reverseCharge`, `buyerVat` en `total` tonen of de actieve factuur al verlegd is, met welk btw-nummer, en het totaal dat erop staat.",
        "operationId": "getInvoiceCorrection",
        "parameters": [
          {
            "$ref": "#/components/parameters/retailerId"
          },
          {
            "name": "orderId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Ordernummer (Bol-orderId of het eigen ordernummer van een handmatige order)."
          }
        ],
        "responses": {
          "200": {
            "description": "De huidige gegevens op de actieve factuur.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoiceCorrectInfo"
                },
                "example": {
                  "ok": true,
                  "details": {
                    "company": "Voorbeeld BV",
                    "firstName": "Jan",
                    "surname": "Jansen",
                    "street": "Kerkstraat",
                    "houseNumber": "12",
                    "houseExt": "a",
                    "zipcode": "1234 AB",
                    "city": "Amsterdam",
                    "country": "NL",
                    "vatNumber": "",
                    "kvkNumber": ""
                  },
                  "original": null,
                  "locked": true,
                  "invoiceId": 5684,
                  "invoiceNumber": "F2026-1487",
                  "reverseCharge": false,
                  "buyerVat": null,
                  "total": 121
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "`not_linked` (retailerId niet van jou) of `order_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "order_not_found"
                }
              }
            }
          },
          "409": {
            "description": "`no_active_invoice`: de order heeft geen (ongecrediteerde) factuur.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "no_active_invoice"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Facturen"
        ],
        "summary": "Klantgegevens op de factuur corrigeren",
        "description": "Corrigeert de klantgegevens (naam, adres, btw- en KvK-nummer) op de actieve factuur van de order. Stuur het **volledige** klantblok mee: een weggelaten of leeg veld wordt leeg op het document. Bedrijfsnaam óf achternaam is verplicht.\n\n- **Nog niet uitgegeven factuur** (Bol-order die nog niet naar Bol is geüpload, gemaild, als PDF gedownload of via de API opgehaald): de gegevens komen op de bestaande factuur, het nummer blijft (`mode: edited`). Met `alwaysNew: true` forceer je ook hier een creditfactuur + nieuwe factuur.\n- **Al uitgegeven factuur** (naar Bol geüpload, gemaild, als PDF gedownload of via de API opgehaald, of een handmatige/API-order): in één transactie een creditfactuur voor de oude factuur plus een nieuwe factuur met de nieuwe gegevens, in dezelfde reeks (`mode: corrected`). Het oude document blijft in het archief.\n\nEen eventuele btw-verlegging en afwijkende verkopersgegevens van de oude factuur erven mee op de nieuwe. Net als bij het aanmaken is dit direct en definitief: de nieuwe nummers zijn meteen uitgegeven.\n\n**Btw verleggen** (`reverseCharge: true`): voor een zakelijke klant met een geldig EU-btw-nummer in een ander land dan dat van het account (intracommunautaire levering, 0%). `vatNumber` is dan verplicht en wordt via VIES gecontroleerd (`vat_invalid`, `vies_unavailable`). Altijd in één transactie een creditfactuur voor de oude factuur (met de oude btw) plus een verlegde factuur met de nieuwe klantgegevens; de regels staan daarop netto (regelbedrag gedeeld door 1 + tarief) en een afwijkend verkopers-btw-nummer van de oude factuur vervalt. `totals.refund` is het verschil: de btw die je aan de klant terugbetaalt. Is de actieve factuur al verlegd met hetzelfde nummer, dan komt `mode: unchanged` terug met het bestaande paar en wordt er niets aangemaakt, zodat een herhaalde aanroep veilig is. Met `preview: true` voer je alle controles uit en krijg je de bedragen zonder iets aan te maken (`mode: preview`, `credit` null).\n\nIs de factuur al deels gecrediteerd, dan weigert de correctie met `has_partial_credit`: een volledige creditfactuur zou dat deel dubbel crediteren.",
        "operationId": "correctInvoice",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InvoiceCorrectRequest"
              },
              "example": {
                "retailerId": "1234567",
                "orderId": "WEB-1021",
                "company": "Voorbeeld BV",
                "firstName": "Jan",
                "surname": "Jansen",
                "street": "Kerkstraat",
                "houseNumber": "12",
                "houseExt": "a",
                "zipcode": "1234 AB",
                "city": "Amsterdam",
                "country": "NL",
                "vatNumber": "NL123456789B01",
                "kvkNumber": ""
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Correctie verwerkt. `credit` is null bij `mode: edited`. `invoices` is de verse documentlijst van de order. Bij `reverseCharge` ook `vies` en `totals`; bij `preview` alleen `mode`, `invoice` (de factuur die gecrediteerd zou worden), `vies` en `totals`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoiceCorrectResponse"
                },
                "example": {
                  "ok": true,
                  "mode": "corrected",
                  "credit": {
                    "id": 5701,
                    "number": "F2026-1502",
                    "url": "https://reprix.nl/api/invoice_render.php?uid=…&rid=…&order=WEB-1021&inv=5701&k=…",
                    "pdfUrl": "https://reprix.nl/api/invoice_render.php?uid=…&rid=…&order=WEB-1021&inv=5701&k=…&pdf=1"
                  },
                  "invoice": {
                    "id": 5702,
                    "number": "F2026-1503",
                    "url": "https://reprix.nl/api/invoice_render.php?uid=…&rid=…&order=WEB-1021&inv=5702&k=…",
                    "pdfUrl": "https://reprix.nl/api/invoice_render.php?uid=…&rid=…&order=WEB-1021&inv=5702&k=…&pdf=1"
                  },
                  "invoiceNumber": "F2026-1503",
                  "invoiceUrl": "https://reprix.nl/api/invoice_render.php?uid=…&rid=…&order=WEB-1021&inv=5702&k=…&pdf=1",
                  "invoices": [
                    {
                      "id": 5684,
                      "kind": "invoice",
                      "number": "F2026-1487",
                      "date": "2026-08-30",
                      "reverseCharge": false,
                      "creditsNumber": null,
                      "credited": true,
                      "url": "https://reprix.nl/api/invoice_render.php?uid=…&rid=…&order=WEB-1021&inv=5684&k=…",
                      "pdfUrl": "https://reprix.nl/api/invoice_render.php?uid=…&rid=…&order=WEB-1021&inv=5684&k=…&pdf=1"
                    },
                    {
                      "id": 5701,
                      "kind": "credit",
                      "number": "F2026-1502",
                      "date": "2026-09-02",
                      "reverseCharge": false,
                      "creditsNumber": "F2026-1487",
                      "credited": false,
                      "url": "https://reprix.nl/api/invoice_render.php?uid=…&rid=…&order=WEB-1021&inv=5701&k=…",
                      "pdfUrl": "https://reprix.nl/api/invoice_render.php?uid=…&rid=…&order=WEB-1021&inv=5701&k=…&pdf=1"
                    },
                    {
                      "id": 5702,
                      "kind": "invoice",
                      "number": "F2026-1503",
                      "date": "2026-09-02",
                      "reverseCharge": false,
                      "creditsNumber": null,
                      "credited": false,
                      "url": "https://reprix.nl/api/invoice_render.php?uid=…&rid=…&order=WEB-1021&inv=5702&k=…",
                      "pdfUrl": "https://reprix.nl/api/invoice_render.php?uid=…&rid=…&order=WEB-1021&inv=5702&k=…&pdf=1"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "`not_linked` (retailerId niet van jou) of `order_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "order_not_found"
                }
              }
            }
          },
          "409": {
            "description": "`no_active_invoice` (geen ongecrediteerde factuur), `no_numbering` (facturering niet ingericht voor dit account), `has_partial_credit` (factuur al deels gecrediteerd), `vies_unavailable` (VIES niet bereikbaar, later opnieuw) of `totals_unavailable` (bedragen niet te bepalen).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "no_active_invoice"
                }
              }
            }
          },
          "422": {
            "description": "`field_too_long` (extra velden `field` en `max`), `number_cap_reached` (reeks vol, extra veld `max`), `vat_required`, `invalid_vat_format`, `vat_invalid` (VIES: ongeldig), `vat_same_country` (afnemer in het eigen land van het account, extra veld `country`), `order_has_margin` (margeregeling) of `preview_requires_reverse_charge`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "vat_invalid",
                  "vat": "BE0123456749"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/invoice_pdf.php": {
      "get": {
        "tags": ["Facturen"],
        "summary": "Factuur-PDF ophalen (stream)",
        "description": "Levert de factuur-PDF achter de API-key (zelfde bytes als de `pdfUrl`-links). Zonder `invoiceId` wordt het nieuwste factuurdocument van de order gebruikt; met `invoiceId` precies dat document (een order kan factuur + creditfactuur + nieuwe factuur hebben, de id's staan in `invoices[]` van `GET /orders.php?orderId=`). Ophalen is uitgeven: de eerste keer dat een factuur als PDF Reprix verlaat (via dit endpoint, een `pdfUrl`, een download in de app, een mailbijlage of de Bol-upload) wordt hij gerenderd (kan 1 tot enkele seconden duren) en definitief vastgelegd. Daarna krijg je altijd exact die bevroren bytes en is de factuur alleen nog te corrigeren met een creditfactuur plus een nieuwe factuur (`POST /invoice_correct.php`). Facturen van handmatige orders zijn bij uitgifte vastgelegd en komen altijd uit dat snapshot.",
        "operationId": "getInvoicePdf",
        "parameters": [
          { "$ref": "#/components/parameters/retailerId" },
          { "name": "orderId", "in": "query", "required": true, "schema": { "type": "string" }, "description": "Ordernummer." },
          { "name": "invoiceId", "in": "query", "required": false, "schema": { "type": "integer" }, "description": "Specifiek factuurdocument (id uit `invoices[]`)." }
        ],
        "responses": {
          "200": { "description": "De PDF (`Content-Disposition: inline`).", "content": { "application/pdf": { "schema": { "type": "string", "format": "binary" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "description": "`not_linked`, `order_not_found` of `invoice_not_found` (order heeft nog geen factuur).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "invoice_not_found" } } } },
          "422": { "description": "Document niet te bouwen, bijv. `doc_no_template` (geen factuurtemplate voor dit account) of `doc_no_items`.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "doc_no_template" } } } },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "502": { "description": "PDF-engine onbereikbaar of render mislukt (`pdf_failed`, extra veld `detail`). Probeer het later opnieuw.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "pdf_failed", "detail": "pdf_engine_unreachable" } } } }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "rxk_<40 hex tekens>", "description": "API-key als Bearer-token: `Authorization: Bearer rxk_…`" },
      "apiKeyHeader": { "type": "apiKey", "in": "header", "name": "X-Api-Key", "description": "Alternatief: de key in de `X-Api-Key`-header." }
    },
    "parameters": {
      "retailerId": {
        "name": "retailerId", "in": "query", "required": true,
        "schema": { "type": "string" },
        "description": "Bol-verkopersnummer van een gekoppeld account.",
        "example": "1234567"
      }
    },
    "responses": {
      "Unauthorized": { "description": "API-key ontbreekt, is ongeldig of is ingetrokken (`missing_api_key` / `invalid_api_key`).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "invalid_api_key" } } } },
      "NotLinked": { "description": "Dit retailerId is niet aan jouw Reprix-account gekoppeld.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "not_linked" } } } },
      "RateLimited": { "description": "Meer dan 60 requests per minuut voor dit Reprix-account. De `Retry-After`-header geeft aan wanneer je het opnieuw kunt proberen.", "headers": { "Retry-After": { "schema": { "type": "integer" }, "description": "Seconden tot het nieuwe venster." } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "rate_limited", "limit": 60 } } } }
    },
    "schemas": {
      "RetailerId": { "type": "string", "description": "Bol-verkopersnummer van een gekoppeld account.", "example": "1234567" },
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": { "error": { "type": "string", "description": "Machineleesbare foutcode." } },
        "additionalProperties": true,
        "description": "Standaard foutvorm; afhankelijk van de code kunnen extra velden aanwezig zijn (bijv. `max`, `index`, `detail`, `limit`)."
      },
      "Series": {
        "type": "object",
        "properties": {
          "id": { "type": "integer", "description": "Reeks-id, te gebruiken als seriesId bij POST /orders.php.", "example": 1 },
          "prefix": { "type": "string", "example": "WEB-" },
          "nextNumber": { "type": "integer", "description": "Volgende tellerstand; het eerstvolgende ordernummer is prefix + nextNumber.", "example": 1021 }
        }
      },
      "Template": {
        "type": "object",
        "properties": {
          "id": { "type": "integer", "description": "Template-id, te gebruiken als templateId bij POST /orders.php.", "example": 4 },
          "name": { "type": "string", "example": "Bol NL-FR" },
          "isDefault": { "type": "boolean", "description": "Standaardtemplate van het account (wordt gebruikt als je geen templateId meegeeft)." }
        }
      },
      "CustomerInput": {
        "type": "object",
        "description": "Klantgegevens (factuur- én verzendadres). Bedrijfsnaam óf achternaam is verplicht; de rest is optioneel.",
        "properties": {
          "company": { "type": "string", "maxLength": 160 },
          "firstName": { "type": "string", "maxLength": 120 },
          "surname": { "type": "string", "maxLength": 120 },
          "email": { "type": "string", "format": "email", "maxLength": 255 },
          "street": { "type": "string", "maxLength": 160 },
          "houseNumber": { "type": "string", "maxLength": 20 },
          "houseExt": { "type": "string", "maxLength": 20, "description": "Huisnummer-toevoeging." },
          "extraAddress": { "type": "string", "maxLength": 160, "description": "Extra adresregel." },
          "zipcode": { "type": "string", "maxLength": 16 },
          "city": { "type": "string", "maxLength": 120 },
          "country": { "type": "string", "minLength": 2, "maxLength": 2, "default": "NL", "description": "ISO-2 landcode.", "example": "NL" },
          "vatNumber": { "type": "string", "maxLength": 20, "description": "Btw-nummer van de klant (komt op de factuur)." },
          "kvkNumber": { "type": "string", "maxLength": 20 },
          "orderReference": { "type": "string", "maxLength": 120, "description": "Referentie van de klant (komt op de factuur)." }
        }
      },
      "ItemInput": {
        "type": "object",
        "required": ["title", "quantity", "unitPrice"],
        "properties": {
          "title": { "type": "string", "maxLength": 500, "description": "Omschrijving van de factuurregel." },
          "ean": { "type": "string", "pattern": "^\\d{8,14}$", "description": "Optioneel EAN (8-14 cijfers)." },
          "quantity": { "type": "integer", "minimum": 1, "maximum": 9999 },
          "unitPrice": { "type": "number", "minimum": 0, "maximum": 999999, "description": "Prijs per stuk, INCLUSIEF btw." },
          "vatPercentage": { "type": "number", "minimum": 0, "maximum": 99.99, "default": 21, "description": "Btw-percentage van deze regel. Genegeerd (0) bij isMargin." },
          "isMargin": { "type": "boolean", "default": false, "description": "Margeregeling (gebruikte goederen): 0% btw en de wettelijke margevermelding op de factuur." },
          "discount": { "type": "object", "required": ["amount", "description"], "description": "Regelkorting, INCLUSIEF btw. Het regeltotaal (total) wordt met dit bedrag verlaagd; de stuksprijs blijft de originele en het label staat bij de regel op de factuur. Combineerbaar met de korting op orderniveau.", "properties": { "amount": { "type": "number", "minimum": 0, "exclusiveMinimum": true, "maximum": 999999, "description": "Kortingsbedrag INCLUSIEF btw; groter dan 0 en kleiner dan het regelbedrag (quantity × unitPrice)." }, "description": { "type": "string", "minLength": 1, "maxLength": 120, "description": "Label dat bij de regel op de factuur komt, bijvoorbeeld de actienaam." } } }
        }
      },
      "DiscountInput": {
        "type": "object",
        "required": ["amount", "description"],
        "description": "Korting op orderniveau, INCLUSIEF btw. Reprix verdeelt het bedrag pro rata over de regels (in hele centen, sluitend op het bedrag), zodat de korting naar rato op elke btw-grondslag drukt en de btw-uitsplitsing altijd klopt. De regels houden hun originele stuksprijs; het factuurtotaal is het regeltotaal min de korting en de korting staat met het label zichtbaar op de factuur.",
        "properties": {
          "amount": { "type": "number", "minimum": 0, "exclusiveMinimum": true, "maximum": 999999, "description": "Kortingsbedrag INCLUSIEF btw, in euro's; groter dan 0 en kleiner dan het regeltotaal van de order." },
          "description": { "type": "string", "minLength": 1, "maxLength": 120, "description": "Label dat op de factuur komt, bijvoorbeeld de kortingscode." }
        }
      },
      "SellerInput": {
        "type": "object",
        "description": "Afwijkende verkopersgegevens voor alléén deze factuur (en een eventuele latere creditfactuur ervan). Alle velden optioneel; een leeg of ontbrekend veld houdt de waarde uit de verkopersgegevens van het Bol-account. Handig voor bijvoorbeeld een ander btw-nummer op facturen naar een specifiek land.",
        "properties": {
          "company": { "type": "string", "maxLength": 160 },
          "street": { "type": "string", "maxLength": 160 },
          "houseNumber": { "type": "string", "maxLength": 20 },
          "zipcode": { "type": "string", "maxLength": 16 },
          "city": { "type": "string", "maxLength": 120 },
          "country": { "type": "string", "maxLength": 60, "description": "Land als tekst (bijv. 'Nederland')." },
          "kvk": { "type": "string", "maxLength": 20 },
          "vat": { "type": "string", "maxLength": 32, "description": "Btw-nummer van de verkoper zoals het op deze factuur moet staan." },
          "email": { "type": "string", "format": "email", "maxLength": 255 }
        }
      },
      "OrderCreateRequest": {
        "type": "object",
        "required": ["retailerId", "customer", "items"],
        "description": "Geef het ordernummer op via `seriesId` (nummer uit eigen reeks) óf `orderNumber` (zelf gekozen, uniek per Bol-account); precies één van beide.",
        "properties": {
          "retailerId": { "$ref": "#/components/schemas/RetailerId" },
          "seriesId": { "type": "integer", "description": "Reeks waaruit het ordernummer komt (zie GET /series.php)." },
          "orderNumber": { "type": "string", "maxLength": 32, "pattern": "^[A-Za-z0-9][A-Za-z0-9._/-]*$", "description": "Zelf gekozen ordernummer (alternatief voor seriesId)." },
          "invoiceDate": { "type": "string", "format": "date", "description": "Factuurdatum (YYYY-MM-DD), niet in de toekomst. Leeg = vandaag." },
          "invoiceSeries": { "type": "string", "enum": ["main", "legacy", "none"], "default": "main", "description": "Factuurreeks: hoofdreeks, oude-ordersreeks, of geen factuur aanmaken." },
          "invoiceSeriesId": { "type": "integer", "description": "Eigen reeks (zie GET /series.php) als FACTUURnummering: het factuurnummer wordt prefix+teller uit deze reeks en de teller schuift door tot een vrij nummer (ook als de reeks daarnaast ordernummers uitgeeft). Alternatief voor invoiceSeries, precies één van beide meegeven. Creditfacturen nummeren zoals altijd uit de creditreeks." },
          "language": { "type": "string", "enum": ["nl", "fr"], "default": "nl", "description": "Taal van het factuurdocument." },
          "templateId": { "type": "integer", "description": "Factuurtemplate (zie GET /templates.php); leeg = standaardtemplate van het account." },
          "customer": { "$ref": "#/components/schemas/CustomerInput" },
          "items": { "type": "array", "minItems": 1, "maxItems": 100, "items": { "$ref": "#/components/schemas/ItemInput" } },
          "discount": { "$ref": "#/components/schemas/DiscountInput" },
          "seller": { "$ref": "#/components/schemas/SellerInput" }
        }
      },
      "InvoiceCorrectDetails": {
        "type": "object",
        "description": "Klantgegevens zoals ze op de factuur komen. Leeg = niet op het document.",
        "properties": {
          "company": {
            "type": "string",
            "maxLength": 160
          },
          "firstName": {
            "type": "string",
            "maxLength": 120
          },
          "surname": {
            "type": "string",
            "maxLength": 120
          },
          "street": {
            "type": "string",
            "maxLength": 160
          },
          "houseNumber": {
            "type": "string",
            "maxLength": 20
          },
          "houseExt": {
            "type": "string",
            "maxLength": 20,
            "description": "Huisnummer-toevoeging."
          },
          "zipcode": {
            "type": "string",
            "maxLength": 16
          },
          "city": {
            "type": "string",
            "maxLength": 120
          },
          "country": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "description": "ISO-2 landcode.",
            "example": "NL"
          },
          "vatNumber": {
            "type": "string",
            "maxLength": 20,
            "description": "Btw-nummer van de klant."
          },
          "kvkNumber": {
            "type": "string",
            "maxLength": 20,
            "description": "KvK-nummer van de klant."
          }
        }
      },
      "InvoiceCorrectInfo": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "details": {
            "allOf": [
              {
                "$ref": "#/components/schemas/InvoiceCorrectDetails"
              }
            ],
            "description": "Wat er nu op de actieve factuur staat (document-overrides winnen van de order)."
          },
          "original": {
            "allOf": [
              {
                "$ref": "#/components/schemas/InvoiceCorrectDetails"
              }
            ],
            "nullable": true,
            "description": "Oorspronkelijke gegevens uit de bron-payload (Bol-order), null bij handmatige orders."
          },
          "locked": {
            "type": "boolean",
            "description": "true = al uitgegeven (naar Bol geüpload, gemaild, gedownload, via de API opgehaald of een handmatige order); een correctie levert dan creditfactuur + nieuwe factuur op."
          },
          "invoiceId": {
            "type": "integer",
            "description": "Id van de actieve factuur."
          },
          "invoiceNumber": {
            "type": "string",
            "description": "Nummer van de actieve factuur."
          },
          "reverseCharge": {
            "type": "boolean",
            "description": "true = de actieve factuur is verlegd (0%, btw-nummer van de afnemer erop)."
          },
          "buyerVat": {
            "type": "string",
            "nullable": true,
            "description": "Via VIES gecontroleerd btw-nummer van de afnemer op een verlegde factuur, anders null.",
            "example": "BE0123456749"
          },
          "total": {
            "type": "number",
            "nullable": true,
            "description": "Totaal op de actieve factuur (zoals op het document), of null als het niet te bepalen is.",
            "example": 121
          }
        }
      },
      "InvoiceCorrectRequest": {
        "type": "object",
        "description": "Volledig klantblok plus retailerId en orderId. Ontbrekende velden worden leeg op het document.",
        "required": [
          "retailerId",
          "orderId"
        ],
        "properties": {
          "retailerId": {
            "$ref": "#/components/schemas/RetailerId"
          },
          "orderId": {
            "type": "string",
            "description": "Ordernummer (Bol-orderId of het eigen ordernummer van een handmatige order)."
          },
          "company": {
            "type": "string",
            "maxLength": 160
          },
          "firstName": {
            "type": "string",
            "maxLength": 120
          },
          "surname": {
            "type": "string",
            "maxLength": 120
          },
          "street": {
            "type": "string",
            "maxLength": 160
          },
          "houseNumber": {
            "type": "string",
            "maxLength": 20
          },
          "houseExt": {
            "type": "string",
            "maxLength": 20,
            "description": "Huisnummer-toevoeging."
          },
          "zipcode": {
            "type": "string",
            "maxLength": 16
          },
          "city": {
            "type": "string",
            "maxLength": 120
          },
          "country": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "description": "ISO-2 landcode.",
            "example": "NL"
          },
          "vatNumber": {
            "type": "string",
            "maxLength": 20,
            "description": "Btw-nummer van de klant."
          },
          "kvkNumber": {
            "type": "string",
            "maxLength": 20,
            "description": "KvK-nummer van de klant."
          },
          "alwaysNew": {
            "type": "boolean",
            "default": false,
            "description": "true = ook een nog niet uitgegeven factuur via creditfactuur + nieuwe factuur vervangen."
          },
          "reverseCharge": {
            "type": "boolean",
            "default": false,
            "description": "true = btw verleggen (intracommunautaire levering, 0%); vatNumber is dan verplicht en wordt via VIES gecontroleerd."
          },
          "preview": {
            "type": "boolean",
            "default": false,
            "description": "Alleen met reverseCharge: controles en bedragen zonder iets aan te maken."
          }
        }
      },
      "InvoiceCorrectResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "mode": {
            "type": "string",
            "enum": [
              "edited",
              "corrected",
              "unchanged",
              "preview"
            ],
            "description": "`edited`: bestaande factuur aangepast, nummer ongewijzigd. `corrected`: creditfactuur + nieuwe factuur aangemaakt. `unchanged`: al verlegd met dit btw-nummer, het bestaande paar komt terug en er is niets aangemaakt. `preview`: alleen controles en bedragen, niets aangemaakt."
          },
          "credit": {
            "allOf": [
              {
                "$ref": "#/components/schemas/InvoiceRef"
              }
            ],
            "nullable": true,
            "description": "De aangemaakte creditfactuur (null bij `edited` en `preview`)."
          },
          "invoice": {
            "allOf": [
              {
                "$ref": "#/components/schemas/InvoiceRef"
              }
            ],
            "description": "De (nieuwe of aangepaste) actieve factuur."
          },
          "invoiceNumber": {
            "type": "string",
            "description": "Nummer van de actieve factuur na de correctie."
          },
          "invoiceUrl": {
            "type": "string",
            "format": "uri",
            "description": "pdfUrl van de actieve factuur na de correctie."
          },
          "invoices": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InvoiceDocument"
            },
            "description": "Alle documenten van de order, chronologisch."
          },
          "vies": {
            "allOf": [
              {
                "$ref": "#/components/schemas/InvoiceCorrectVies"
              }
            ],
            "description": "VIES-uitkomst (alleen bij reverseCharge)."
          },
          "totals": {
            "allOf": [
              {
                "$ref": "#/components/schemas/InvoiceCorrectTotals"
              }
            ],
            "description": "Bedragen (alleen bij reverseCharge)."
          }
        }
      },
      "InvoiceCorrectVies": {
        "type": "object",
        "description": "VIES-controle van het btw-nummer van de afnemer.",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "valid"
            ],
            "description": "Altijd `valid` via de API (ongeldig of onbereikbaar geeft een fout)."
          },
          "vat": {
            "type": "string",
            "description": "Genormaliseerd btw-nummer, zoals het op de factuur komt.",
            "example": "BE0123456749"
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Naam volgens VIES, of null.",
            "example": "VOORBEELD BV"
          },
          "address": {
            "type": "string",
            "nullable": true,
            "description": "Adres volgens VIES, of null.",
            "example": "Kerkstraat 12, 2000 Antwerpen"
          },
          "requestId": {
            "type": "string",
            "nullable": true,
            "description": "Consultatienummer van VIES (bewijs van de controle), of null.",
            "example": "WAPIAAAAZ1x2y3z4"
          }
        }
      },
      "InvoiceCorrectTotals": {
        "type": "object",
        "description": "Bedragen van een verlegging (zoals op de documenten).",
        "properties": {
          "credit": {
            "type": "number",
            "nullable": true,
            "description": "Totaal van de creditfactuur (negatief, met de oude btw).",
            "example": -121
          },
          "invoice": {
            "type": "number",
            "nullable": true,
            "description": "Totaal van de verlegde factuur (netto).",
            "example": 100
          },
          "refund": {
            "type": "number",
            "nullable": true,
            "description": "Btw die de verkoper aan de klant terugbetaalt: creditbedrag min nieuw totaal.",
            "example": 21
          }
        }
      },
      "InvoiceRef": {
        "type": "object",
        "nullable": true,
        "properties": {
          "id": { "type": "integer", "description": "Factuurdocument-id (bruikbaar als invoiceId bij GET /invoice_pdf.php)." },
          "number": { "type": "string", "example": "F2026-1487" },
          "url": { "type": "string", "format": "uri", "description": "Permanente HMAC-link naar het document (werkt zonder API-key). Levert de bij uitgifte vastgelegde PDF." },
          "pdfUrl": { "type": "string", "format": "uri", "description": "Permanente HMAC-link naar de PDF (werkt zonder API-key), bij uitgifte vastgelegd." }
        }
      },
      "OrderCreateResponse": {
        "type": "object",
        "properties": {
          "ok": { "type": "boolean" },
          "orderId": { "type": "string", "description": "Het toegekende ordernummer.", "example": "WEB-1021" },
          "invoice": { "$ref": "#/components/schemas/InvoiceRef" },
          "invoiceError": { "type": "string", "nullable": true, "description": "Code als het factuurnummer niet gemint kon worden (de order staat er dan wél): numbering_not_active | number_cap_reached | has_invoice | order_not_invoiceable | invoice_mint_failed. | plan_limit_reached (maandlimiet Free-plan bereikt; factuur kan na upgrade of in de nieuwe maand alsnog)" }
        }
      },
      "OrderItem": {
        "type": "object",
        "properties": {
          "title": { "type": "string" },
          "ean": { "type": "string", "nullable": true },
          "quantity": { "type": "integer" },
          "unitPrice": { "type": "number", "nullable": true, "description": "Prijs per stuk incl. btw." },
          "vatPercentage": { "type": "number", "nullable": true },
          "isMargin": { "type": "boolean" },
          "discountAmount": { "type": "number", "nullable": true, "description": "Korting op deze regel incl. btw (al verrekend in total), of null." },
          "discountTitle": { "type": "string", "nullable": true, "description": "Label van de korting, of null." },
          "total": { "type": "number", "nullable": true, "description": "Regeltotaal incl. btw (ná korting)." }
        }
      },
      "InvoiceDocument": {
        "type": "object",
        "description": "Eén factuurdocument van een order (factuur of creditfactuur). Bij handmatige orders leveren url en pdfUrl de bij uitgifte vastgelegde PDF; bij Bol-orders ligt de PDF vast zodra hij Reprix voor het eerst verlaat (Bol-upload, mailbijlage, download in de app, `pdfUrl` of `GET /invoice_pdf.php`); vanaf dat moment levert ook `url` die vastgelegde PDF in plaats van de live HTML-weergave.",
        "properties": {
          "id": { "type": "integer" },
          "kind": { "type": "string", "enum": ["invoice", "credit"] },
          "number": { "type": "string" },
          "date": { "type": "string", "format": "date" },
          "reverseCharge": { "type": "boolean", "description": "Btw verlegd." },
          "creditsNumber": { "type": "string", "nullable": true, "description": "Bij een credit: het nummer van de gecrediteerde factuur." },
          "credited": { "type": "boolean", "description": "Bij een factuur: volledig gecrediteerd door een latere credit." },
          "url": { "type": "string", "format": "uri" },
          "pdfUrl": { "type": "string", "format": "uri" }
        }
      },
      "Address": {
        "type": "object",
        "properties": {
          "name": { "type": "string", "nullable": true },
          "firstName": { "type": "string", "nullable": true },
          "surname": { "type": "string", "nullable": true },
          "company": { "type": "string", "nullable": true },
          "email": { "type": "string", "nullable": true },
          "street": { "type": "string", "nullable": true },
          "houseNumber": { "type": "string", "nullable": true },
          "houseNumberExtension": { "type": "string", "nullable": true },
          "zipCode": { "type": "string", "nullable": true },
          "city": { "type": "string", "nullable": true },
          "country": { "type": "string", "nullable": true }
        }
      },
      "OrderDiscount": {
        "type": "object",
        "nullable": true,
        "description": "Korting op de order: de som van de regelkortingen met het bijbehorende label; null als er geen korting is. total en de regeltotalen zijn al ná korting.",
        "properties": {
          "amount": { "type": "number", "description": "Totale korting incl. btw." },
          "description": { "type": "string", "nullable": true, "description": "Label van de korting (bijvoorbeeld de kortingscode), of null." }
        }
      },
      "OrderListItem": {
        "type": "object",
        "properties": {
          "orderId": { "type": "string" },
          "placedAt": { "type": "string", "nullable": true, "description": "Besteldatum/-tijd (Amsterdamse wandtijd, YYYY-MM-DD HH:MM:SS)." },
          "status": { "type": "string", "enum": ["OPEN", "SHIPPED", "CANCELLED"], "nullable": true },
          "source": { "type": "string", "nullable": true, "description": "null = Bol-import, 'custom' = handmatig aangemaakt, 'invoice_request' = oude order via factuuraanvraag." },
          "customer": { "type": "object", "properties": {
            "name": { "type": "string", "nullable": true },
            "company": { "type": "string", "nullable": true },
            "city": { "type": "string", "nullable": true },
            "country": { "type": "string", "nullable": true }
          } },
          "total": { "type": "number", "nullable": true, "description": "Ordertotaal incl. btw." },
          "discount": { "allOf": [ { "$ref": "#/components/schemas/OrderDiscount" } ], "nullable": true },
          "items": { "type": "array", "items": { "$ref": "#/components/schemas/OrderItem" } },
          "invoice": { "allOf": [ { "$ref": "#/components/schemas/InvoiceDocument" } ], "nullable": true, "description": "De actieve (ongecrediteerde) factuur, of null." }
        }
      },
      "OrderDetail": {
        "allOf": [
          { "$ref": "#/components/schemas/OrderListItem" },
          { "type": "object", "properties": {
            "billing": { "allOf": [ { "$ref": "#/components/schemas/Address" } ], "description": "Factuuradres, aangevuld met vatNumber, kvkNumber en orderReference." },
            "shipping": { "$ref": "#/components/schemas/Address" },
            "invoices": { "type": "array", "items": { "$ref": "#/components/schemas/InvoiceDocument" }, "description": "Alle factuurdocumenten van de order, chronologisch (factuur → evt. creditfactuur → evt. nieuwe factuur)." }
          } }
        ]
      }
    }
  }
}
