openapi: 3.0.1
info:
  title: ABTProducts
  description: >-
    CRUD APIs for HTM products (based on the new domainmodel v2.6). Internal use
    only; external touchpoints can get read-only, filtered access via the HTM
    Service Engine, using a different endpoint.
  version: '1.0'
servers:
  - url: https://services.acc.api.htm.nl/abt/abtproducts/1.0
tags:
  - name: ABTProducts CRUD
    description: >-
      Internal use only; external touchpoints can get read-only, filtered access
      via the HTM Service Engine.
paths:
  /products:
    get:
      tags:
        - ABTProducts CRUD
      summary: Get a list of all defined HTM products
      description: |-
        Get a list of all HTM products that refer to a given parentProductId (or without parent, if not specified), without any other filters. \
        Only a few details are returned per product - use the `GET /products/{productId}` endpoint
        to get more details. \
        Two attributes are only returned if no touchpoint HTTP header is present: `isValid` and `isArchived`. \
        In the examples, it can be seen that (when a touchpoint HTTP header is present) 
        only products that have active sellingPeriods for touchpoints within the same 
        retailer as the calling retailer are returned. \
        Touchpoint 1 and 2 belong to retailer 1; touchpoint 3 and 4 belong to retailer 2.
      parameters:
        - name: parentProductId
          in: query
          required: false
          description: |-
           Only return products that refer to the given parentProductId. \
           Returns parent-products (i.e. products with parentProductId = null) if left empty.
          schema:
            type: integer
            example: 1
        - name: isArchived
          in: query
          required: false
          description: |-
            When omitted, return all products; when true, return only archived products (isArchived = true); 
            when false, return only non-archived products (isArchived = false).
          schema:
            type: boolean
        - name: onlyVouchers
          in: query
          required: false
          description: |-
            When omitted, defaults to false (i.e. return all normal products that are NOT voucher definitions); when true, return only voucher definitions
          schema:
            type: boolean
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductPayloadListResponse'
              examples:
                getListNoProducts:
                  summary: No products / Empty list
                  description: No products
                  value:
                    {
                        "Entries": []
                    }
                getListOneValidProductNoSellingPeriodsNoTouchpointId:
                  summary: List containing one (valid) product without any SellingPeriods (no touchpointId in HTTP-headers)
                  description: All products are returned, even if they have no active sellingPeriods or are invalid or archived
                  value:
                    {
                      "Entries": [
                        {
                          "productId": 1,
                          "isValid": true,
                          "isArchived": false,
                          "productName": "HTM pilot 90% korting",
                          "productDescription": "Reis met 90% korting gedurende de eerste F&F pilot!",
                          "productCategory": {
                            "productCategoryId": 1,
                            "isTravelProduct": true,
                            "name": "Kortingsabonnement"
                          },
                          "tokenTypes": [
                            {
                              "tokenTypeId": 1,
                              "name": "EMV"
                            }
                          ],
                          "sellableTouchpointIds": null,
                          "amountInclTax": null,
                          "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg",
                          "productPageUrl": "https://www.htm.nl/nog-onbekende-product-pagina"
                        }
                      ]
                    }
                getListOneInvalidProductNoSellingPeriodsNoTouchpointId:
                  summary: List containing one (invalid) product without any SellingPeriods (no touchpointId in HTTP-headers)
                  description: All products are returned, even if they have no active sellingPeriods or are invalid or archived
                  value:
                    {
                      "Entries": [
                        {
                          "productId": 1,
                          "isValid": false,
                          "isArchived": false,
                          "productName": "HTM pilot 90% korting",
                          "productDescription": "Reis met 90% korting gedurende de eerste F&F pilot!",
                          "productCategory": {
                            "productCategoryId": 1,
                            "isTravelProduct": true,
                            "name": "Kortingsabonnement"
                          },
                          "tokenTypes": [
                            {
                              "tokenTypeId": 1,
                              "name": "EMV"
                            }
                          ],
                          "sellableTouchpointIds": null,
                          "amountInclTax": null,
                          "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg",
                          "productPageUrl": "https://www.htm.nl/nog-onbekende-product-pagina"
                        }
                      ]
                    }
                getListOneProductNoTouchpointId:
                  summary: List containing one product with 4 currently active SellingPeriods (no touchpointId in HTTP-headers)
                  description: All products are returned, even if they have no active SellingPeriods or are invalid or archived
                  value:
                    {
                      "Entries": [
                        {
                          "productId": 2,
                          "isValid": true,
                          "isArchived": false,
                          "productName": "HTM dagkaart",
                          "productDescription": "Reis een dag met het openbaar vervoer bij HTM",
                          "productCategory": {
                            "productCategoryId": 5,
                            "isTravelProduct": false,
                            "name": "Barcode"
                          },
                          "tokenTypes": null,
                          "sellableTouchpointIds": [
                            1,
                            2,
                            3,
                            4
                          ],
                          "amountInclTax": null,
                          "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg",
                          "productPageUrl": "https://www.htm.nl/nog-onbekende-product-pagina"
                        }
                      ]
                    }
                getListOneProductTouchpointId3:
                  summary: List containing one product with 4 currently active SellingPeriods (touchpointId 3 in HTTP-headers)
                  description: Only products that have active sellingPeriods for touchpoint 3 and/or 4 (part of same retailer) are returned
                  value:
                    {
                      "Entries": [
                        {
                          "productId": 2,
                          "productName": "HTM dagkaart",
                          "productDescription": "Reis een dag met het openbaar vervoer bij HTM",
                          "productCategory": {
                            "productCategoryId": 5,
                            "isTravelProduct": false,
                            "name": "Barcode"
                          },
                          "tokenTypes": null,
                          "sellableTouchpointIds": [
                            3,
                            4
                          ],
                          "amountInclTax": 300,
                          "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg",
                          "productPageUrl": "https://www.htm.nl/nog-onbekende-product-pagina"
                        }
                      ]
                    }
                getListMultipleProductsNoTouchpointId:
                  summary: List containing multiple products (no touchpointId in HTTP-headers)
                  description: All products are returned, even if they have no active SellingPeriods or are invalid or archived
                  value:
                    {
                      "Entries": [
                        {
                          "productId": 1,
                          "isValid": true,
                          "isArchived": false,
                          "productName": "HTM pilot 90% korting",
                          "productDescription": "Reis met 90% korting gedurende de eerste F&F pilot!",
                          "productCategory": {
                            "productCategoryId": 1,
                            "isTravelProduct": true,
                            "name": "Kortingsabonnement"
                          },
                          "tokenTypes": [
                            {
                              "tokenTypeId": 1,
                              "name": "EMV"
                            }
                          ],
                          "sellableTouchpointIds": null,
                          "amountInclTax": null,
                          "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg",
                          "productPageUrl": "https://www.htm.nl/nog-onbekende-product-pagina"
                        },
                        {
                          "productId": 2,
                          "isValid": true,
                          "isArchived": false,
                          "productName": "HTM dagkaart",
                          "productDescription": "Reis een dag met het openbaar vervoer bij HTM",
                          "productCategory": {
                            "productCategoryId": 5,
                            "isTravelProduct": false,
                            "name": "Barcode"
                          },
                          "tokenTypes": null,
                          "sellableTouchpointIds": [
                            1,
                            2,
                            3,
                            4
                          ],
                          "amountInclTax": null,
                          "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg",
                          "productPageUrl": "https://www.htm.nl/nog-onbekende-product-pagina"
                        },
                        {
                          "productId": 3,
                          "isValid": true,
                          "isArchived": false,
                          "productName": "HTM 20% korting",
                          "productDescription": "Reis een maand lang met 20% korting!",
                          "productCategory": {
                            "productCategoryId": 1,
                            "isTravelProduct": true,
                            "name": "Kortingsabonnement"
                          },
                          "tokenTypes": [
                            {
                              "tokenTypeId": 1,
                              "name": "EMV"
                            }
                          ],
                          "sellableTouchpointIds": [
                            1,
                            2
                          ],
                          "amountInclTax": null,
                          "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg",
                          "productPageUrl": "https://www.htm.nl/nog-onbekende-product-pagina"
                        },
                        {
                          "productId": 4,
                          "isValid": true,
                          "isArchived": false,
                          "productName": "HTM 40% korting",
                          "productDescription": "Reis een maand lang met 40% korting!",
                          "productCategory": {
                            "productCategoryId": 1,
                            "isTravelProduct": true,
                            "name": "Kortingsabonnement"
                          },
                          "tokenTypes": [
                            {
                              "tokenTypeId": 1,
                              "name": "EMV"
                            }
                          ],
                          "sellableTouchpointIds": [
                            3,
                            4
                          ],
                          "amountInclTax": null,
                          "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg",
                          "productPageUrl": "https://www.htm.nl/nog-onbekende-product-pagina"
                        }
                      ]
                    }
                getListMultipleProductsTouchpointId1:
                  summary: List containing multiple products (touchpointId 3 in HTTP-headers)
                  description: Only products that have active SellingPeriods for touchpoint 3 and/or 4 (part of same retailer) are returned
                  value:
                    {
                      "Entries": [
                        {
                          "productId": 2,
                          "productName": "HTM dagkaart",
                          "productDescription": "Reis een dag met het openbaar vervoer bij HTM",
                          "productCategory": {
                            "productCategoryId": 5,
                            "isTravelProduct": false,
                            "name": "Barcode"
                          },
                          "tokenTypes": null,
                          "sellableTouchpointIds": [
                            3,
                            4
                          ],
                          "amountInclTax": 300,
                          "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg",
                          "productPageUrl": "https://www.htm.nl/nog-onbekende-product-pagina"
                        },
                        {
                          "productId": 4,
                          "productName": "HTM 40% korting",
                          "productDescription": "Reis een maand lang met 40% korting!",
                          "productCategory": {
                            "productCategoryId": 1,
                            "isTravelProduct": true,
                            "name": "Kortingsabonnement"
                          },
                          "tokenTypes": [
                            {
                              "tokenTypeId": 1,
                              "name": "EMV"
                            }
                          ],
                          "sellableTouchpointIds": [
                            3,
                            4
                          ],
                          "amountInclTax": 800,
                          "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg",
                          "productPageUrl": "https://www.htm.nl/nog-onbekende-product-pagina"
                        }
                      ]
                    }
        '400':
          description: '400'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/400Response'
        '401':
          description: '401'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/401Response'
        '404':
          description: '404'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/404Response'
        '500':
          description: '500'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/500Response'
    post:
      tags:
        - ABTProducts CRUD
      summary: >-
        Persist a new HTM product as defined in the request body, after
        successful validation
      description: >-
        Persist a new HTM product as defined in the raw JSON request body, but
        only after successful validation of the entire request body. When any
        (subset of) validation fails, nothing is persisted in the underlying
        database.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProductRequest'
            examples:
              postMinimalProduct:
                summary: >-
                  Minimum request body (to be used as minimal parent-template
                  for hierarchical products)
                value:
                  {
                    "productOwnerId": 1,
                    "isValid": false,
                    "isArchived": false,
                    "isSellableAtHtm": false
                  }
              postGboProduct:
                summary: Add new GBO product (non-renewable)
                value:
                  {
                    "fikoArticleNumber": "1234",
                    "isValid": true,
                    "isArchived": false,
                    "needsVoucher": false,
                    "gboPackageTemplateId": "30901",
                    "productName": "HTM pilot 90% korting",
                    "productDescription": "Reis met 90% korting gedurende de eerste F&F pilot!",
                    "validityPeriod": {
                      "fromInclusive": "2024-09-01T00:00:00.000",
                      "toInclusive": "2024-12-31T23:59:59.999"
                    },
                    "productTranslations": [
                      {
                        "language": "en",
                        "name": "HTM pilot 90% discount",
                        "description": "Travel with 90% discount during the first F&F pilot!"
                      }
                    ],
                    "productOwnerId": 1,
                    "marketSegmentIds": [
                      1
                    ],
                    "customerSegmentIds": [
                      5,
                      6
                    ],
                    "allowedGboAgeProfileIds": [],
                    "productCategoryId": 1,
                    "requiredCustomerLevelId": 1,
                    "incompatibleProducts": [
                      {
                        "incompatibleProductId": 1,
                        "description": "Kan niet combineren met zichzelf"
                      }
                    ],
                    "mandatoryCustomerDataItemIds": [
                      4,
                      7
                    ],
                    "tokenTypeIds": [
                      1
                    ],
                    "paymentMomentId": 1,
                    "validityDuration": "P7D",
                    "maxStartInFutureDuration": "P6W",
                    "isRenewable": false,
                    "sendInvoice": false,
                    "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg",
                    "productPageUrl": "https://www.htm.nl/nog-onbekende-product-pagina",
                    "termsUrl": "https://www.htm.nl/nog-onbekende-productvoorwaarden-pagina",
                    "isSellableAtHtm": true,
                    "needsSolvencyCheckConsumer": false,
                    "needsSolvencyCheckBusiness": false,
                    "sellingPeriods": [
                      {
                        "fromInclusive": "2024-09-01T00:00:00.000",
                        "toInclusive": "2024-12-31T23:59:59.999",
                        "salesTouchpointId": 3,
                        "sellingPrices": [
                          {
                            "amountExclTax": null,
                            "amountInclTax": 100,
                            "taxCode": "V09",
                            "taxPercentage": 9.0000,
                            "fromInclusive": "2024-09-01T00:00:00.000",
                            "toInclusive": "2024-12-31T23:59:59.999",
                            "internalPrice": 92.0000
                          }
                        ]
                      }
                    ],
                    "purchasePrices": [
                      {
                        "amountExclTax": null,
                        "amountInclTax": 0,
                        "taxCode": "V09",
                        "taxPercentage": 9.0000,
                        "fromInclusive": "2024-09-01T00:00:00.000",
                        "toInclusive": "2024-12-31T23:59:59.999"
                      }
                    ]
                  }
              postGboProductWithParent:
                summary: Add new GBO product with parent and layerInfo (non-renewable)
                value:
                  {
                    "parentProductId": 1,
                    "layerInfoId": 1,
                    "fikoArticleNumber": "1234",
                    "isValid": true,
                    "isArchived": false,
                    "needsVoucher": false,
                    "gboPackageTemplateId": "30901",
                    "productName": "HTM pilot 90% korting",
                    "productDescription": "Reis met 90% korting gedurende de eerste F&F pilot!",
                    "validityPeriod": {
                      "fromInclusive": "2024-09-01T00:00:00.000",
                      "toInclusive": "2024-12-31T23:59:59.999"
                    },
                    "productTranslations": [
                      {
                        "language": "en",
                        "name": "HTM pilot 90% discount",
                        "description": "Travel with 90% discount during the first F&F pilot!"
                      }
                    ],
                    "productOwnerId": 1,
                    "marketSegmentIds": [
                      1
                    ],
                    "customerSegmentIds": [
                      5,
                      6
                    ],
                    "allowedGboAgeProfileIds": [],
                    "productCategoryId": 1,
                    "requiredCustomerLevelId": 1,
                    "incompatibleProducts": [
                      {
                        "incompatibleProductId": 2,
                        "description": "Kan niet combineren met zichzelf"
                      }
                    ],
                    "mandatoryCustomerDataItemIds": [
                      4,
                      7
                    ],
                    "tokenTypeIds": [
                      1
                    ],
                    "paymentMomentId": 1,
                    "validityDuration": "P7D",
                    "maxStartInFutureDuration": "P6W",
                    "isRenewable": false,
                    "sendInvoice": false,
                    "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg",
                    "productPageUrl": "https://www.htm.nl/nog-onbekende-product-pagina",
                    "termsUrl": "https://www.htm.nl/nog-onbekende-productvoorwaarden-pagina",
                    "isSellableAtHtm": true,
                    "needsSolvencyCheckConsumer": false,
                    "needsSolvencyCheckBusiness": false,
                    "sellingPeriods": [
                      {
                        "fromInclusive": "2024-09-01T00:00:00.000",
                        "toInclusive": "2024-12-31T23:59:59.999",
                        "salesTouchpointId": 3,
                        "sellingPrices": [
                          {
                            "amountExclTax": null,
                            "amountInclTax": 100,
                            "taxCode": "V09",
                            "taxPercentage": 9.0000,
                            "fromInclusive": "2024-09-01T00:00:00.000",
                            "toInclusive": "2024-12-31T23:59:59.999",
                            "internalPrice": 92.0000
                          }
                        ]
                      }
                    ],
                    "purchasePrices": [
                      {
                        "amountExclTax": null,
                        "amountInclTax": 0,
                        "taxCode": "V09",
                        "taxPercentage": 9.0000,
                        "fromInclusive": "2024-09-01T00:00:00.000",
                        "toInclusive": "2024-12-31T23:59:59.999"
                      }
                    ]
                  }
              postGboProductMultiplePeriods:
                summary: >-
                  Add new GBO product (non-renewable, multiple sellingPeriods/prices per period)
                value:
                  {
                    "fikoArticleNumber": "1234",
                    "isValid": true,
                    "isArchived": false,
                    "needsVoucher": false,
                    "gboPackageTemplateId": "30901",
                    "productName": "HTM pilot 90% korting",
                    "productDescription": "Reis met 90% korting gedurende de eerste F&F pilot!",
                    "validityPeriod": {
                      "fromInclusive": "2024-09-01T00:00:00.000",
                      "toInclusive": "2025-12-31T23:59:59.999"
                    },
                    "productTranslations": [
                      {
                        "language": "en",
                        "name": "HTM pilot 90% discount",
                        "description": "Travel with 90% discount during the first F&F pilot!"
                      }
                    ],
                    "productOwnerId": 1,
                    "marketSegmentIds": [
                      1
                    ],
                    "customerSegmentIds": [
                      5,
                      6
                    ],
                    "allowedGboAgeProfileIds": [],
                    "productCategoryId": 1,
                    "requiredCustomerLevelId": 1,
                    "incompatibleProducts": [
                      {
                        "incompatibleProductId": 2,
                        "description": "Kan niet combineren met zichzelf"
                      }
                    ],
                    "mandatoryCustomerDataItemIds": [
                      4,
                      7
                    ],
                    "tokenTypeIds": [
                      1
                    ],
                    "paymentMomentId": 1,
                    "validityDuration": "P7D",
                    "maxStartInFutureDuration": "P6W",
                    "isRenewable": false,
                    "sendInvoice": false,
                    "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg",
                    "productPageUrl": "https://www.htm.nl/nog-onbekende-product-pagina",
                    "termsUrl": "https://www.htm.nl/nog-onbekende-productvoorwaarden-pagina",
                    "isSellableAtHtm": true,
                    "needsSolvencyCheckConsumer": false,
                    "needsSolvencyCheckBusiness": false,
                    "sellingPeriods": [
                      {
                        "fromInclusive": "2024-09-01T00:00:00.000",
                        "toInclusive": "2025-12-31T23:59:59.999",
                        "salesTouchpointId": 3,
                        "sellingPrices": [
                          {
                            "amountExclTax": null,
                            "amountInclTax": 100,
                            "taxCode": "V09",
                            "taxPercentage": 9.0000,
                            "fromInclusive": "2024-09-01T00:00:00.000",
                            "toInclusive": "2024-12-31T23:59:59.999",
                            "internalPrice": 92.0000
                          },
                          {
                            "amountExclTax": null,
                            "amountInclTax": 110,
                            "taxCode": "V09",
                            "taxPercentage": 9.0000,
                            "fromInclusive": "2025-01-01T00:00:00.000",
                            "toInclusive": "2025-12-31T23:59:59.999",
                            "internalPrice": 101.0000
                          }
                        ]
                      },
                      {
                        "fromInclusive": "2024-09-01T00:00:00.000",
                        "toInclusive": "2025-12-31T23:59:59.999",
                        "salesTouchpointId": 2,
                        "sellingPrices": [
                          {
                            "amountExclTax": null,
                            "amountInclTax": 100,
                            "taxCode": "V09",
                            "taxPercentage": 9.0000,
                            "fromInclusive": "2024-09-01T00:00:00.000",
                            "toInclusive": "2024-12-31T23:59:59.999",
                            "internalPrice": 92.0000
                          },
                          {
                            "amountExclTax": null,
                            "amountInclTax": 110,
                            "taxCode": "V09",
                            "taxPercentage": 9.0000,
                            "fromInclusive": "2025-01-01T00:00:00.000",
                            "toInclusive": "2025-12-31T23:59:59.999",
                            "internalPrice": 101.0000
                          }
                        ]
                      }
                    ],
                    "purchasePrices": [
                      {
                        "amountExclTax": null,
                        "amountInclTax": 0,
                        "taxCode": "V09",
                        "taxPercentage": 9.0000,
                        "fromInclusive": "2024-09-01T00:00:00.000",
                        "toInclusive": "2025-12-31T23:59:59.999"
                      }
                    ]
                  }
              postGboPadProduct:
                summary: >-
                  Add new GBO product (PAD required, renewable, allowedGboAgeProfiles, padBirthDate)
                value:
                  {
                    "fikoArticleNumber": "1234",
                    "isValid": true,
                    "isArchived": false,
                    "needsVoucher": false,
                    "gboPackageTemplateId": "33610",
                    "productName": "HTM Regio Vrij DH73",
                    "productDescription": "Voor een vast bedrag onbeperkt reizen met EBS, HTM en RET in het gekozen gebied in de regio Rotterdam Den Haag.",
                    "validityPeriod": {
                      "fromInclusive": "2024-09-01T00:00:00.000",
                      "toInclusive": "2024-12-31T23:59:59.999"
                    },
                    "productTranslations": [
                      {
                        "language": "en",
                        "name": "HTM Regio Free DH73",
                        "description": "For a fixed amount unlimited travel with EBS, HTM and RET in the chosen area in the Rotterdam The Hague region."
                      }
                    ],
                    "productOwnerId": 1,
                    "marketSegmentIds": [
                      1
                    ],
                    "customerSegmentIds": [
                      5,
                      6
                    ],
                    "allowedGboAgeProfileIds": [                      
                      2,
                      3,
                      4,
                      5
                    ],
                    "productCategoryId": 2,
                    "requiredCustomerLevelId": 3,
                    "requiredProducts": [
                      {
                        "requiredProductId": 1,
                        "description": "Basisproduct nodig"
                      }
                    ],
                    "incompatibleProducts": [
                      {
                        "incompatibleProductId": 3,
                        "description": "Kan niet combineren met zichzelf"
                      }
                    ],
                    "mandatoryCustomerDataItemIds": [
                      4,
                      8
                    ],
                    "requiredGboPersonalAttributeIds": [
                      1,
                      2,
                      3
                    ],
                    "tokenTypeIds": [
                      1,
                      2,
                      3
                    ],
                    "paymentMomentId": 1,
                    "serviceOptionIds": [],
                    "validityDuration": "P1M",
                    "maxStartInFutureDuration": "P6W",
                    "isRenewable": true,
                    "sendInvoice": true,
                    "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg",
                    "productPageUrl": "https://www.htm.nl/nog-onbekende-product-pagina",
                    "termsUrl": "https://www.htm.nl/nog-onbekende-productvoorwaarden-pagina",
                    "isSellableAtHtm": true,
                    "needsSolvencyCheckConsumer": false,
                    "needsSolvencyCheckBusiness": false,
                    "sellingPeriods": [
                      {
                        "fromInclusive": "2024-09-01T00:00:00.000",
                        "toInclusive": "2024-12-31T23:59:59.999",
                        "salesTouchpointId": 3,
                        "forbiddenPaymentMethodIds": [
                          3
                        ],
                        "sellingPrices": [
                          {
                            "amountExclTax": null,
                            "amountInclTax": 5900,
                            "taxCode": "V09",
                            "taxPercentage": 9.0000,
                            "fromInclusive": "2024-09-01T00:00:00.000",
                            "toInclusive": "2024-12-31T23:59:59.999",
                            "internalPrice": 5413.0000
                          }
                        ]
                      }
                    ],
                    "purchasePrices": [
                      {
                        "amountExclTax": null,
                        "amountInclTax": 0,
                        "taxCode": "V09",
                        "taxPercentage": 9.0000,
                        "fromInclusive": "2024-09-01T00:00:00.000",
                        "toInclusive": "2024-12-31T23:59:59.999"
                      }
                    ]
                  }
              postGboProductSupplement:
                summary: TODO - Add new GBO product (supplement with requirements)
                value:
                  {
                    "productName": "TODO"
                  }
              postTapConnectProduct:
                summary: TODO - Add new TapConnect product
                value:
                  {
                    "productName": "TODO"
                  }
              postIbanMandate:
                summary: TODO - Add new IBAN mandate (functional product)
                value:
                  {
                    "productName": "TODO"
                  }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateOrUpdateProductResponse'
        '400':
          description: '400'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/400Response'
        '401':
          description: '401'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/401Response'
        '404':
          description: '404'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/404Response'
        '500':
          description: '500'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/500Response'
  /products/{productId}:
    get:
      tags:
        - ABTProducts CRUD
      summary: Get full details of a single HTM product, specified by the productId
      description: >-
        Get full details of a single HTM product, specified by the productId,
        without any filtering. External consumers can get access to a filtered
        view via the HTM Service Engine, only receiving the information that is
        allowed to be seen by the touchpointId of the consumer.
      parameters:
        - name: productId
          in: path
          required: true
          style: simple
          description: Id of the product to get details for
          schema:
            type: integer
            example: 1
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductPayloadResponse'
              examples:
                getDetailsGboNonRenewable:
                  summary: GBO product (non-renewable)
                  value:
                    {
                      "productId": 1,
                      "parentProductId": null,
                      "layerInfo": null,
                      "fikoArticleNumber": "1234",
                      "isValid": true,
                      "isArchived": false,
                      "needsVoucher": false,
                      "gboPackageTemplateId": "30901",
                      "tapConnectProductCode": null,
                      "productName": "HTM pilot 90% korting",
                      "productDescription": "Reis met 90% korting gedurende de eerste F&F pilot!",
                      "validityPeriod": {
                        "validityPeriodId": 1,
                        "fromInclusive": "2024-09-01T00:00:00.000+00:00",
                        "toInclusive": "2024-12-31T23:59:59.999+00:00"
                      },
                      "productTranslations": [
                        {
                          "language": "en",
                          "name": "HTM pilot 90% discount",
                          "description": "Travel with 90% discount during the first F&F pilot!"
                        }
                      ],
                      "productOwner": {
                        "productOwnerId": 1,
                        "name": "Corneel Verstoep",
                        "organization": "HTM"
                      },
                      "marketSegments": [
                        {
                          "marketSegmentId": 1,
                          "name": "B2C"
                        }
                      ],
                      "customerSegments": [
                        {
                          "customerSegmentId": 5,
                          "name": "Student"
                        },
                        {
                          "customerSegmentId": 6,
                          "name": "Ooievaarspas-gerechtigde"
                        }
                      ],
                      "allowedGboAgeProfiles": null,
                      "productCategory": {
                        "productCategoryId": 1,
                        "isTravelProduct": true,
                        "name": "Kortingsabonnement"
                      },
                      "requiredCustomerLevel": {
                        "requiredCustomerLevelId": 1,
                        "name": "guest"
                      },
                      "requiredProducts": null,
                      "incompatibleProducts": [
                        {
                          "incompatibleProductId": 1,
                          "productName": "HTM pilot 90% korting",
                          "description": "Kan niet combineren met zichzelf"
                        }
                      ],
                      "mandatoryCustomerDataItems": [
                        {
                          "mandatoryCustomerDataItemId": 4,
                          "customerDataItem": "emailAddress"
                        },
                        {
                          "mandatoryCustomerDataItemId": 7,
                          "customerDataItem": "ovPayToken"
                        }
                      ],
                      "requiredGboPersonalAttributes": null,
                      "tokenTypes": [
                        {
                          "tokenTypeId": 1,
                          "name": "EMV"
                        }
                      ],
                      "paymentMoment": {
                        "paymentMomentId": 1,
                        "name": "prepaid"
                      },
                      "serviceOptions": null,
                      "validityDuration": "P7D",
                      "maxStartInFutureDuration": "P6W",
                      "isRenewable": false,
                      "sendInvoice": false,
                      "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg",
                      "productPageUrl": "https://www.htm.nl/nog-onbekende-product-pagina",
                      "termsUrl": "https://www.htm.nl/nog-onbekende-productvoorwaarden-pagina",
                      "isSellableAtHtm": true,
                      "needsSolvencyCheckConsumer": false,
                      "needsSolvencyCheckBusiness": false,
                      "sellingPeriods": [
                        {
                          "sellingPeriodId": 1,
                          "fromInclusive": "2024-09-01T00:00:00.000+00:00",
                          "toInclusive": "2024-12-31T23:59:59.999+00:00",
                          "salesTouchpoint": {
                            "salesTouchpointId": 3,
                            "name": "Website (Perplex)",
                            "isActive": true,
                            "retailer": {
                              "retailerId": 1001,
                              "name": "HTM externe touchpoints",
                              "street": "Koningin Julianaplein",
                              "number": "10",
                              "numberAddition": null,
                              "postalCode": "2595 AA",
                              "city": "Den Haag",
                              "country": "Nederland",
                              "emailAddress": "info@htm.nl",
                              "phoneNumber": "070 374 9002",
                              "taxId": "572309345923",
                              "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg"
                            }
                          },
                          "forbiddenPaymentMethods": null,
                          "sellingPrices": [
                            {
                              "sellingPriceId": 1,
                              "amountExclTax": null,
                              "amountInclTax": 100,
                              "taxCode": "V09",
                              "taxPercentage": 9.0000,
                              "fromInclusive": "2024-09-01T00:00:00.000+00:00",
                              "toInclusive": "2024-12-31T23:59:59.999+00:00",
                              "internalPrice": 92.0000
                            }
                          ]
                        }
                      ],
                      "purchasePrices": [
                        {
                          "purchasePriceId": 1,
                          "amountExclTax": null,
                          "taxCode": "V09",
                          "taxPercentage": 9.0000,
                          "amountInclTax": 0,
                          "fromInclusive": "2024-09-01T00:00:00.000+00:00",
                          "toInclusive": "2024-12-31T23:59:59.999+00:00"
                        }
                      ],
                      "auditTrail": [
                        {
                          "auditTrailId": 2,
                          "action": "update",
                          "user": "api",
                          "timestamp": "2024-09-03T08:39:38.000+00:00"
                        },
                        {
                          "auditTrailId": 1,
                          "action": "insert",
                          "user": "api",
                          "timestamp": "2024-09-03T08:38:24.000+00:00"
                        }
                      ]
                    }
                getDetailsGboProductMultiplePeriods:
                  summary: GBO product (non-renewable, multiple sellingPeriods/prices per period)
                  value:
                    {
                      "productId": 2,
                      "parentProductId": null,
                      "layerInfo": null,
                      "fikoArticleNumber": "1234",
                      "isValid": true,
                      "isArchived": false,
                      "needsVoucher": false,
                      "gboPackageTemplateId": "30901",
                      "tapConnectProductCode": null,
                      "productName": "HTM pilot 90% korting",
                      "productDescription": "Reis met 90% korting gedurende de eerste F&F pilot!",
                      "validityPeriod": {
                        "validityPeriodId": 2,
                        "fromInclusive": "2024-09-01T00:00:00.000+00:00",
                        "toInclusive": "2025-12-31T23:59:59.999+00:00"
                      },
                      "productTranslations": [
                        {
                          "language": "en",
                          "name": "HTM pilot 90% discount",
                          "description": "Travel with 90% discount during the first F&F pilot!"
                        }
                      ],
                      "productOwner": {
                        "productOwnerId": 1,
                        "name": "Corneel Verstoep",
                        "organization": "HTM"
                      },
                      "marketSegments": [
                        {
                          "marketSegmentId": 1,
                          "name": "B2C"
                        }
                      ],
                      "customerSegments": [
                        {
                          "customerSegmentId": 5,
                          "name": "Student"
                        },
                        {
                          "customerSegmentId": 6,
                          "name": "Ooievaarspas-gerechtigde"
                        }
                      ],
                      "allowedGboAgeProfiles": [
                        {
                          "gboAgeProfileId": 2,
                          "name": "Kind (4 t/m 11 jaar)",
                          "ageFromInclusive": 4,
                          "ageUntilInclusive": 11
                        },
                        {
                          "gboAgeProfileId": 3,
                          "name": "Jongere (12 t/m 18 jaar)",
                          "ageFromInclusive": 12,
                          "ageUntilInclusive": 18
                        }
                      ],
                      "productCategory": {
                        "productCategoryId": 1,
                        "isTravelProduct": true,
                        "name": "Kortingsabonnement"
                      },
                      "requiredCustomerLevel": {
                        "requiredCustomerLevelId": 1,
                        "name": "guest"
                      },
                      "requiredProducts": null,
                      "incompatibleProducts": [
                        {
                          "incompatibleProductId": 2,
                          "productName": "HTM pilot 90% korting",
                          "description": "Kan niet combineren met zichzelf"
                        }
                      ],
                      "mandatoryCustomerDataItems": [
                        {
                          "mandatoryCustomerDataItemId": 4,
                          "customerDataItem": "emailAddress"
                        },
                        {
                          "mandatoryCustomerDataItemId": 7,
                          "customerDataItem": "ovPayToken"
                        }
                      ],
                      "requiredGboPersonalAttributes": null,
                      "tokenTypes": [
                        {
                          "tokenTypeId": 1,
                          "name": "EMV"
                        }
                      ],
                      "paymentMoment": {
                        "paymentMomentId": 1,
                        "name": "prepaid"
                      },
                      "serviceOptions": null,
                      "validityDuration": "P7D",
                      "maxStartInFutureDuration": "P6W",
                      "isRenewable": false,
                      "sendInvoice": false,
                      "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg",
                      "productPageUrl": "https://www.htm.nl/nog-onbekende-product-pagina",
                      "termsUrl": "https://www.htm.nl/nog-onbekende-productvoorwaarden-pagina",
                      "isSellableAtHtm": true,
                      "needsSolvencyCheckConsumer": false,
                      "needsSolvencyCheckBusiness": false,
                      "sellingPeriods": [
                        {
                          "sellingPeriodId": 1,
                          "fromInclusive": "2024-09-01T00:00:00.000+00:00",
                          "toInclusive": "2025-12-31T23:59:59.999+00:00",
                          "salesTouchpoint": {
                            "salesTouchpointId": 3,
                            "name": "Website (Perplex)",
                            "isActive": true,
                            "retailer": {
                              "retailerId": 1001,
                              "name": "HTM externe touchpoints",
                              "street": "Koningin Julianaplein",
                              "number": "10",
                              "numberAddition": null,
                              "postalCode": "2595 AA",
                              "city": "Den Haag",
                              "country": "Nederland",
                              "emailAddress": "info@htm.nl",
                              "phoneNumber": "070 374 9002",
                              "taxId": "572309345923",
                              "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg"
                            }
                          },
                          "forbiddenPaymentMethods": null,
                          "sellingPrices": [
                            {
                              "sellingPriceId": 1,
                              "amountExclTax": null,
                              "taxCode": "V09",
                              "taxPercentage": 9.0000,
                              "amountInclTax": 100,
                              "fromInclusive": "2024-09-01T00:00:00.000+00:00",
                              "toInclusive": "2024-12-31T23:59:59.999+00:00",
                              "internalPrice": 92.0000
                            },
                            {
                              "sellingPriceId": 2,
                              "amountExclTax": null,
                              "taxCode": "V09",
                              "taxPercentage": 9.0000,
                              "amountInclTax": 110,
                              "fromInclusive": "2025-01-01T00:00:00.000+00:00",
                              "toInclusive": "2025-12-31T23:59:59.999+00:00",
                              "internalPrice": 101.0000
                            }
                          ]
                        },
                        {
                          "sellingPeriodId": 2,
                          "fromInclusive": "2024-09-01T00:00:00.000+00:00",
                          "toInclusive": "2025-12-31T23:59:59.999+00:00",
                          "salesTouchpoint": {
                            "salesTouchpointId": 2,
                            "name": "SMP (Team Incident Masters)",
                            "isActive": false,
                            "retailer": {
                              "retailerId": 1000,
                              "name": "HTM interne touchpoints",
                              "street": "Koningin Julianaplein",
                              "number": "10",
                              "numberAddition": null,
                              "postalCode": "2595 AA",
                              "city": "Den Haag",
                              "country": "Nederland",
                              "emailAddress": "info@htm.nl",
                              "phoneNumber": "070 374 9002",
                              "taxId": "572309345923",
                              "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg"
                            }
                          },
                          "forbiddenPaymentMethods": null,
                          "sellingPrices": [
                            {
                              "sellingPriceId": 3,
                              "amountExclTax": null,
                              "amountInclTax": 100,
                              "taxCode": "V09",
                              "taxPercentage": 9.0000,
                              "fromInclusive": "2024-09-01T00:00:00.000+00:00",
                              "toInclusive": "2024-12-31T23:59:59.999+00:00",
                              "internalPrice": 92.0000
                            },
                            {
                              "sellingPriceId": 4,
                              "amountExclTax": null,
                              "amountInclTax": 110,
                              "taxCode": "V09",
                              "taxPercentage": 9.0000,                              
                              "fromInclusive": "2025-01-01T00:00:00.000+00:00",
                              "toInclusive": "2025-12-31T23:59:59.999+00:00",
                              "internalPrice": 101.0000
                            }
                          ]
                        }
                      ],
                      "purchasePrices": [
                        {
                          "purchasePriceId": 1,
                          "amountExclTax": null,
                          "amountInclTax": 0,
                          "taxCode": "V09",
                          "taxPercentage": 9.0000,
                          "fromInclusive": "2024-09-01T00:00:00.000+00:00",
                          "toInclusive": "2025-12-31T23:59:59.999+00:00"
                        }
                      ],
                      "auditTrail": [
                        {
                          "auditTrailId": 2,
                          "action": "update",
                          "user": "api",
                          "timestamp": "2024-09-03T08:39:38.000+00:00"
                        },
                        {
                          "auditTrailId": 1,
                          "action": "insert",
                          "user": "api",
                          "timestamp": "2024-09-03T08:38:24.000+00:00"
                        }
                      ]
                    }                
                getDetailsGboPadProduct:
                  summary: GBO product (PAD required, layerInfo, allowedGboAgeProfiles, padBirthDate)
                  description: >-
                    This product has `layerInfo` defined, which means that this product is not a final fulfillable product, but that there are underlying variants 
                    that the customer has to choose between. The `layerInfo` defines what distinguishes the underlying variants - in this case the 
                    `allowedGboAgeProfiles` array contains different values for the underlying variants. Based on age information provided by the customer, 
                    the correct underlying variant can be selected.
                  value:
                    {
                      "productId": 3,
                      "parentProductId": null,
                      "layerInfo": {
                        "layerInfoId": 1,
                        "choiceKey": "allowedGboAgeProfiles",
                        "choiceLabel": "Wat is uw geboortedatum?",
                        "isCustomChoice": false
                      },
                      "fikoArticleNumber": "1234",
                      "isValid": true,
                      "isArchived": false,
                      "needsVoucher": false,
                      "gboPackageTemplateId": "33610",
                      "tapConnectProductCode": null,
                      "productName": "HTM Regio Vrij DH73",
                      "productDescription": "Voor een vast bedrag onbeperkt reizen met EBS, HTM en RET in het gekozen gebied in de regio Rotterdam Den Haag.",
                      "validityPeriod": {
                        "validityPeriodId": 3,
                        "fromInclusive": "2024-09-01T00:00:00.000+00:00",
                        "toInclusive": "2024-12-31T23:59:59.999+00:00"
                      },
                      "productTranslations": [
                        {
                          "language": "en",
                          "name": "HTM Regio Free DH73",
                          "description": "For a fixed amount unlimited travel with EBS, HTM and RET in the chosen area in the Rotterdam The Hague region."
                        }
                      ],
                      "productOwner": {
                        "productOwnerId": 1,
                        "name": "Corneel Verstoep",
                        "organization": "HTM"
                      },
                      "marketSegments": [
                        {
                          "marketSegmentId": 1,
                          "name": "B2C"
                        }
                      ],
                      "customerSegments": null,
                      "allowedGboAgeProfiles": [
                        {
                          "gboAgeProfileId": 2,
                          "name": "Kind (4 t/m 11 jaar)",
                          "ageFromInclusive": 4,
                          "ageToInclusive": 11
                        },
                        {
                          "gboAgeProfileId": 3,
                          "name": "Jongere (12 t/m 18 jaar)",
                          "ageFromInclusive": 12,
                          "ageToInclusive": 18
                        },
                        {
                          "gboAgeProfileId": 4,
                          "name": "Volwassene (19 t/m 64 jaar)",
                          "ageFromInclusive": 19,
                          "ageToInclusive": 64
                        },
                        {
                          "gboAgeProfileId": 5,
                          "name": "Oudere (65 jaar of ouder)",
                          "ageFromInclusive": 65,
                          "ageToInclusive": 999
                        }
                      ],
                      "productCategory": {
                        "productCategoryId": 2,
                        "isTravelProduct": true,
                        "name": "Afgekocht reisrecht"
                      },
                      "requiredCustomerLevel": {
                        "requiredCustomerLevelId": 3,
                        "name": "profile"
                      },
                      "requiredProducts": null,
                      "incompatibleProducts": [
                        {
                          "incompatibleProductId": 3,
                          "productName": "HTM Regio Vrij DH73",
                          "description": "Kan niet combineren met zichzelf"
                        }
                      ],
                      "mandatoryCustomerDataItems": [
                        {
                          "mandatoryCustomerDataItemId": 4,
                          "customerDataItem": "emailAddress"
                        },
                        {
                          "mandatoryCustomerDataItemId": 8,
                          "customerDataItem": "padBirthDate"
                        }
                      ],
                      "requiredGboPersonalAttributes": [
                        {
                          "requiredGboPersonalAttributeId": 1,
                          "name": "NAME"
                        },
                        {
                          "requiredGboPersonalAttributeId": 2,
                          "name": "BIRTHDATE"
                        },
                        {
                          "requiredGboPersonalAttributeId": 3,
                          "name": "PHOTO"
                        }
                      ],
                      "tokenTypes": [
                        {
                          "tokenTypeId": 1,
                          "name": "EMV"
                        },
                        {
                          "tokenTypeId": 2,
                          "name": "OVpas physical"
                        },
                        {
                          "tokenTypeId": 3,
                          "name": "OVpas digital"
                        }
                      ],
                      "paymentMoment": {
                        "paymentMomentId": 1,
                        "name": "prepaid"
                      },
                      "serviceOptions": [],
                      "validityDuration": "P1M",
                      "maxStartInFutureDuration": "P6W",
                      "isRenewable": null,
                      "sendInvoice": null,
                      "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg",
                      "productPageUrl": "https://www.htm.nl/nog-onbekende-product-pagina",
                      "termsUrl": "https://www.htm.nl/nog-onbekende-productvoorwaarden-pagina",
                      "isSellableAtHtm": true,
                      "needsSolvencyCheckConsumer": false,
                      "needsSolvencyCheckBusiness": false,
                      "sellingPeriods": [
                        {
                          "sellingPeriodId": 5,
                          "fromInclusive": "2024-09-01T00:00:00.000+00:00",
                          "toInclusive": "2024-12-31T23:59:59.999+00:00",
                          "salesTouchpoint": {
                            "salesTouchpointId": 3,
                            "name": "Website (Perplex)",
                            "isActive": true,
                            "retailer": {
                              "retailerId": 1001,
                              "name": "HTM externe touchpoints",
                              "street": "Koningin Julianaplein",
                              "number": "10",
                              "numberAddition": null,
                              "postalCode": "2595 AA",
                              "city": "Den Haag",
                              "country": "Nederland",
                              "emailAddress": "info@htm.nl",
                              "phoneNumber": "070 374 9002",
                              "taxId": "572309345923",
                              "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg"
                            }
                          },
                          "forbiddenPaymentMethods": [
                            {
                              "forbiddenPaymentMethodId": 3,
                              "name": "creditcard",
                              "issuer": "American Express"
                            }
                          ],
                          "sellingPrices": [
                            {
                              "sellingPriceId": 5,
                              "amountExclTax": null,
                              "amountInclTax": 5900,
                              "taxCode": "V09",
                              "taxPercentage": 9.0000,
                              "fromInclusive": "2024-09-01T00:00:00.000+00:00",
                              "toInclusive": "2024-12-31T23:59:59.999+00:00",
                              "internalPrice": 5413.0000
                            }
                          ]
                        }
                      ],
                      "purchasePrices": [
                        {
                          "purchasePriceId": 1,
                          "amountExclTax": null,
                          "taxCode": "V09",
                          "taxPercentage": 9.0000,
                          "amountInclTax": 0,
                          "fromInclusive": "2024-09-01T00:00:00.000+00:00",
                          "toInclusive": "2024-12-31T23:59:59.999+00:00"
                        }
                      ],
                      "auditTrail": [
                        {
                          "auditTrailId": 2,
                          "action": "update",
                          "user": "api",
                          "timestamp": "2024-09-03T08:39:38.000+00:00"
                        },
                        {
                          "auditTrailId": 1,
                          "action": "insert",
                          "user": "api",
                          "timestamp": "2024-09-03T08:38:24.000+00:00"
                        }
                      ]
                    }
                getDetailsFunctionalProductChangeIbanMandate:
                  summary: Functional product (change IBAN mandate)
                  value:
                    {
                      "productId": 143,
                      "parentProductId": null,
                      "layerInfo": null,
                      "fikoArticleNumber": "1234",
                      "isValid": true,
                      "isArchived": false,
                      "needsVoucher": false,
                      "gboPackageTemplateId": null,
                      "tapConnectProductCode": null,
                      "productName": "IBAN wijzigen functioneel product",
                      "productDescription": "IBAN wijzigen functioneel product",
                      "validityPeriod": {
                        "validityPeriodId": 254,
                        "fromInclusive": "2023-12-31T23:00:00.000+00:00",
                        "toInclusive": "2100-12-08T04:00:00.000+00:00"
                      },
                      "productTranslations": [
                        {
                          "language": "en",
                          "name": "Change IBAN functional product",
                          "description": "Change IBAN functional product"
                        }
                      ],
                      "productOwner": {
                        "productOwnerId": 17,
                        "name": "Corneel Verstoep",
                        "organization": "HTM"
                      },
                      "marketSegments": [
                        {
                          "marketSegmentId": 1,
                          "name": "B2C"
                        }
                      ],
                      "customerSegments": null,
                      "allowedGboAgeProfiles": null,
                      "productCategory": {
                        "productCategoryId": 7,
                        "isTravelProduct": false,
                        "name": "Functioneel product"
                      },
                      "requiredCustomerLevel": {
                        "requiredCustomerLevelId": 3,
                        "name": "profile"
                      },
                      "requiredProducts": null,
                      "incompatibleProducts": null,
                      "mandatoryCustomerDataItems": null,
                      "requiredGboPersonalAttributes": null,
                      "tokenTypes": null,
                      "paymentMoment": {
                        "paymentMomentId": 1,
                        "name": "prepaid"
                      },
                      "serviceOptions": [
                        {
                          "serviceOptionId": 4,
                          "action": "cancel_notAllowed",
                          "description": "Stopzetting is niet toegestaan (doorgaans in combinatie met refund_notAllowed)"
                        },
                        {
                          "serviceOptionId": 10,
                          "action": "refund_notAllowed",
                          "description": "Terugbetaling niet toegestaan (doorgaans in combinatie met cancel_notAllowed)"
                        }
                      ],
                      "validityDuration": null,
                      "maxStartInFutureDuration": null,
                      "isRenewable": null,
                      "sendInvoice": null,
                      "imageReference": "https://web.acc.cloud.htm.nl/media/leif2leu/htm-logo-mobile.svg",
                      "productPageUrl": "https://web.acc.cloud.htm.nl/webshop/onbekend/",
                      "termsUrl": "https://web.acc.cloud.htm.nl/reisproducten/productvoorwaarden/onbekend/",
                      "isSellableAtHtm": true,
                      "needsSolvencyCheckConsumer": false,
                      "needsSolvencyCheckBusiness": false,
                      "sellingPeriods": [
                        {
                          "sellingPeriodId": 204,
                          "fromInclusive": "2024-09-30T23:00:00.000+00:00",
                          "toInclusive": "2099-12-30T23:00:00.000+00:00",
                          "salesTouchpoint": {
                            "salesTouchpointId": 3,
                            "name": "Website (Perplex)",
                            "isActive": true,
                            "retailer": {
                              "retailerId": 1001,
                              "name": "HTM externe touchpoints",
                              "street": "Koningin Julianaplein",
                              "number": 10,
                              "numberAddition": null,
                              "postalCode": "2595 AA",
                              "city": "Den Haag",
                              "country": "Nederland",
                              "emailAddress": "info@htm.nl",
                              "phoneNumber": "070 374 9002",
                              "taxId": 572309345923,
                              "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg"
                            }
                          },
                          "forbiddenPaymentMethods": [
                            {
                              "forbiddenPaymentMethodId": 1,
                              "name": "creditcard",
                              "issuer": "Mastercard"
                            },
                            {
                              "forbiddenPaymentMethodId": 2,
                              "name": "creditcard",
                              "issuer": "Visa"
                            },
                            {
                              "forbiddenPaymentMethodId": 3,
                              "name": "creditcard",
                              "issuer": "American Express"
                            }
                          ],
                          "sellingPrices": [
                            {
                              "sellingPriceId": 187,
                              "taxCode": "V0",
                              "taxPercentage": 0.0000,
                              "amountExclTax": null,
                              "amountInclTax": 1,
                              "fromInclusive": "2024-09-30T23:00:00.000+00:00",
                              "toInclusive": "2099-12-30T23:00:00.000+00:00",
                              "internalPrice": 0.0000
                            }
                          ]
                        },
                        {
                          "sellingPeriodId": 448,
                          "fromInclusive": "2023-12-31T23:00:00.000+00:00",
                          "toInclusive": "2099-12-08T04:00:00.000+00:00",
                          "salesTouchpoint": {
                            "salesTouchpointId": 6,
                            "name": "SMP (Service Medewerker Portaal)",
                            "isActive": true,
                            "retailer": {
                              "retailerId": 1000,
                              "name": "HTM interne touchpoints",
                              "street": "Koningin Julianaplein",
                              "number": 10,
                              "numberAddition": null,
                              "postalCode": "2595 AA",
                              "city": "Den Haag",
                              "country": "Nederland",
                              "emailAddress": "info@htm.nl",
                              "phoneNumber": "070 374 9002",
                              "taxId": 572309345923,
                              "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg"
                            }
                          },
                          "forbiddenPaymentMethods": [
                            {
                              "forbiddenPaymentMethodId": 1,
                              "name": "creditcard",
                              "issuer": "Mastercard"
                            },
                            {
                              "forbiddenPaymentMethodId": 2,
                              "name": "creditcard",
                              "issuer": "Visa"
                            },
                            {
                              "forbiddenPaymentMethodId": 3,
                              "name": "creditcard",
                              "issuer": "American Express"
                            }
                          ],
                          "sellingPrices": [
                            {
                              "sellingPriceId": 383,
                              "taxCode": "V00",
                              "taxPercentage": 0.0000,
                              "amountExclTax": null,
                              "amountInclTax": 1,
                              "fromInclusive": "2025-01-31T23:00:00.000+00:00",
                              "toInclusive": "2030-12-08T04:00:00.000+00:00",
                              "internalPrice": 1.0000
                            }
                          ]
                        }
                      ],
                      "purchasePrices": null,
                      "auditTrail": [
                        {
                          "auditTrailId": 5124,
                          "action": "update",
                          "user": "api",
                          "timestamp": "2025-09-05T16:06:25.823+00:00"
                        }
                      ]
                    }
                getDetailsSaldoProduct:
                  summary: Saldo product
                  value:
                    {
                      "productId": 123,
                      "parentProductId": null,
                      "layerInfo": null,
                      "fikoArticleNumber": "1234",
                      "isValid": true,
                      "isArchived": false,
                      "needsVoucher": false,
                      "gboPackageTemplateId": null,
                      "tapConnectProductCode": null,
                      "productName": "OV-pas saldo (1 eurocent)",
                      "productDescription": "1 eurocent saldo voor de OV-pas ",
                      "validityPeriod": {
                        "validityPeriodId": 123,
                        "fromInclusive": "2023-12-31T23:00:00.000+00:00",
                        "toInclusive": "2100-12-08T04:00:00.000+00:00"
                      },
                      "productTranslations": [
                        {
                          "language": "en",
                          "name": "OV-pas credit (1 eurocent)",
                          "description": "1 eurocent credit for the OV-pas"
                        }
                      ],
                      "productOwner": {
                        "productOwnerId": 17,
                        "name": "Corneel Verstoep",
                        "organization": "HTM"
                      },
                      "marketSegments": [
                        {
                          "marketSegmentId": 1,
                          "name": "B2C"
                        }
                      ],
                      "customerSegments": null,
                      "allowedGboAgeProfiles": null,
                      "productCategory": {
                        "productCategoryId": 8,
                        "isTravelProduct": true,
                        "name": "Saldo"
                      },
                      "requiredCustomerLevel": {
                        "requiredCustomerLevelId": 1,
                        "name": "guest"
                      },
                      "requiredProducts": null,
                      "incompatibleProducts": null,
                      "mandatoryCustomerDataItems": null,
                      "requiredGboPersonalAttributes": null,
                      "tokenTypes": [
                        {
                          "tokenTypeId": 2,
                          "name": "OVPas physical"
                        },
                        {
                          "tokenTypeId": 3,
                          "name": "OVPas digital"
                        }
                      ],
                      "paymentMoment": {
                        "paymentMomentId": 1,
                        "name": "prepaid"
                      },
                      "serviceOptions": [
                        {
                          "serviceOptionId": 4,
                          "action": "cancel_notAllowed",
                          "description": "Stopzetting is niet toegestaan (doorgaans in combinatie met refund_notAllowed)"
                        },
                        {
                          "serviceOptionId": 10,
                          "action": "refund_notAllowed",
                          "description": "Terugbetaling niet toegestaan (doorgaans in combinatie met cancel_notAllowed)"
                        }
                      ],
                      "validityDuration": null,
                      "maxStartInFutureDuration": null,
                      "isRenewable": false,
                      "sendInvoice": false,
                      "imageReference": "https://web.acc.cloud.htm.nl/media/leif2leu/htm-logo-mobile.svg",
                      "productPageUrl": "https://web.acc.cloud.htm.nl/webshop/onbekend/",
                      "termsUrl": "https://web.acc.cloud.htm.nl/reisproducten/productvoorwaarden/onbekend/",
                      "isSellableAtHtm": true,
                      "needsSolvencyCheckConsumer": false,
                      "needsSolvencyCheckBusiness": false,
                      "sellingPeriods": [
                        {
                          "sellingPeriodId": 123,
                          "fromInclusive": "2024-09-30T23:00:00.000+00:00",
                          "toInclusive": "2099-12-30T23:00:00.000+00:00",
                          "salesTouchpoint": {
                            "salesTouchpointId": 3,
                            "name": "Website (Perplex)",
                            "isActive": true,
                            "retailer": {
                              "retailerId": 1001,
                              "name": "HTM externe touchpoints",
                              "street": "Koningin Julianaplein",
                              "number": 10,
                              "numberAddition": null,
                              "postalCode": "2595 AA",
                              "city": "Den Haag",
                              "country": "Nederland",
                              "emailAddress": "info@htm.nl",
                              "phoneNumber": "070 374 9002",
                              "taxId": 572309345923,
                              "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg"
                            }
                          },
                          "forbiddenPaymentMethods": null,
                          "sellingPrices": [
                            {
                              "sellingPriceId": 123,
                              "taxCode": null,
                              "taxPercentage": null,
                              "amountExclTax": null,
                              "amountInclTax": 1,
                              "fromInclusive": "2024-09-30T23:00:00.000+00:00",
                              "toInclusive": "2099-12-30T23:00:00.000+00:00",
                              "internalPrice": 0.0000
                            }
                          ]
                        }
                      ],
                      "purchasePrices": null,
                      "auditTrail": [
                        {
                          "auditTrailId": 5124,
                          "action": "update",
                          "user": "api",
                          "timestamp": "2025-09-05T16:06:25.823+00:00"
                        }
                      ]
                    }
                getDetailsGboProductSupplement:
                  summary: TODO - GBO product (supplement with requirements)
                  value:
                    {
                      "productName": "TODO"
                    }
                getDetailsTapConnectProduct:
                  summary: TODO - TapConnect product
                  value:
                    {
                      "productName": "TODO"
                    }
                
        '400':
          description: '400'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/400Response'
        '401':
          description: '401'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/401Response'
        '404':
          description: '404'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/404Response'
        '500':
          description: '500'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/500Response'
    put:
      tags:
        - ABTProducts CRUD
      summary: >-
        Replace the entire product (as specified by the productId) with the
        given raw JSON request body, after successful validation
      description: >-
        For nested JSON, including id's for reference tables indicates an update
        of an existing reference - no id's indicates a new reference
      parameters:
        - name: productId
          in: path
          required: true
          style: simple
          description: The id of the product to replace/update
          schema:
            type: integer
            example: 1
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateProductRequest'
            examples:
              updateMinimalProduct:
                summary: >-
                  Minimum request body (to be used as minimal parent-template
                  for hierarchical products)
                value:
                  {
                    "productOwnerId": 1,
                    "isValid": false,
                    "isArchived": false,
                    "isSellableAtHtm": false
                  }
              updateGboProduct:
                summary: Update existing GBO product (non-renewable)
                value:
                  {
                    "fikoArticleNumber": "1234",
                    "isValid": true,
                    "isArchived": false,
                    "gboPackageTemplateId": "30901",
                    "productName": "HTM pilot 90% korting",
                    "productDescription": "Reis met 90% korting gedurende de eerste F&F pilot!",
                    "validityPeriod": {
                      "validityPeriodId": 1,
                      "fromInclusive": "2024-09-01T00:00:00.000",
                      "toInclusive": "2024-12-31T23:59:59.999"
                    },
                    "productTranslations": [
                      {
                        "language": "en",
                        "name": "HTM pilot 90% discount",
                        "description": "Travel with 90% discount during the first F&F pilot!"
                      }
                    ],
                    "productOwnerId": 1,
                    "marketSegmentIds": [
                      1
                    ],
                    "customerSegmentIds": [
                      5,
                      6
                    ],
                    "allowedGboAgeProfileIds": [],
                    "productCategoryId": 1,
                    "requiredCustomerLevelId": 1,
                    "incompatibleProducts": [
                      {
                        "incompatibleProductId": 1,
                        "description": "Kan niet combineren met zichzelf"
                      }
                    ],
                    "mandatoryCustomerDataItemIds": [
                      4,
                      7
                    ],
                    "tokenTypeIds": [
                      1
                    ],
                    "paymentMomentId": 1,
                    "validityDuration": "P7D",
                    "maxStartInFutureDuration": "P6W",
                    "isRenewable": false,
                    "sendInvoice": false,
                    "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg",
                    "productPageUrl": "https://www.htm.nl/nog-onbekende-product-pagina",
                    "termsUrl": "https://www.htm.nl/nog-onbekende-productvoorwaarden-pagina",
                    "isSellableAtHtm": true,
                    "needsSolvencyCheckConsumer": false,
                    "needsSolvencyCheckBusiness": false,
                    "sellingPeriods": [
                      {
                        "sellingPeriodId": 1,
                        "fromInclusive": "2024-09-01T00:00:00.000",
                        "toInclusive": "2024-12-31T23:59:59.999",
                        "salesTouchpointId": 3,
                        "sellingPrices": [
                          {
                            "sellingPriceId": 1,
                            "amountExclTax": null,
                            "amountInclTax": 100,
                            "taxCode": "V09",
                            "taxPercentage": 9.0000,
                            "fromInclusive": "2024-09-01T00:00:00.000",
                            "toInclusive": "2024-12-31T23:59:59.999",
                            "internalPrice": 92.0000
                          }
                        ]
                      }
                    ],
                    "purchasePrices": [
                      {
                        "purchasePriceId": 1,
                        "amountExclTax": null,
                        "amountInclTax": 0,
                        "taxCode": "V09",
                        "taxPercentage": 9.0000,
                        "fromInclusive": "2024-09-01T00:00:00.000",
                        "toInclusive": "2024-12-31T23:59:59.999"
                      }
                    ]
                  }
              updateGboProductWithParent:
                summary: Update existing GBO product with parent and layerInfo (non-renewable)
                value:
                  {
                    "parentProductId": 1,
                    "layerInfoId": 1,
                    "fikoArticleNumber": "1234",
                    "isValid": true,
                    "isArchived": false,
                    "gboPackageTemplateId": "30901",
                    "productName": "HTM pilot 90% korting",
                    "productDescription": "Reis met 90% korting gedurende de eerste F&F pilot!",
                    "validityPeriod": {
                      "validityPeriodId": 1,
                      "fromInclusive": "2024-09-01T00:00:00.000",
                      "toInclusive": "2024-12-31T23:59:59.999"
                    },
                    "productTranslations": [
                      {
                        "language": "en",
                        "name": "HTM pilot 90% discount",
                        "description": "Travel with 90% discount during the first F&F pilot!"
                      }
                    ],
                    "productOwnerId": 1,
                    "marketSegmentIds": [
                      1
                    ],
                    "customerSegmentIds": [
                      5,
                      6
                    ],
                    "allowedGboAgeProfileIds": [],
                    "productCategoryId": 1,
                    "requiredCustomerLevelId": 1,
                    "incompatibleProducts": [
                      {
                        "incompatibleProductId": 2,
                        "description": "Kan niet combineren met zichzelf"
                      }
                    ],
                    "mandatoryCustomerDataItemIds": [
                      4,
                      7
                    ],
                    "tokenTypeIds": [
                      1
                    ],
                    "paymentMomentId": 1,
                    "validityDuration": "P7D",
                    "maxStartInFutureDuration": "P6W",
                    "isRenewable": false,
                    "sendInvoice": false,
                    "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg",
                    "productPageUrl": "https://www.htm.nl/nog-onbekende-product-pagina",
                    "termsUrl": "https://www.htm.nl/nog-onbekende-productvoorwaarden-pagina",
                    "isSellableAtHtm": true,
                    "needsSolvencyCheckConsumer": false,
                    "needsSolvencyCheckBusiness": false,
                    "sellingPeriods": [
                      {
                        "sellingPeriodId": 1,
                        "fromInclusive": "2024-09-01T00:00:00.000",
                        "toInclusive": "2024-12-31T23:59:59.999",
                        "salesTouchpointId": 3,
                        "sellingPrices": [
                          {
                            "sellingPriceId": 1,
                            "amountExclTax": null,
                            "amountInclTax": 100,
                            "taxCode": "V09",
                            "taxPercentage": 9.0000,
                            "fromInclusive": "2024-09-01T00:00:00.000",
                            "toInclusive": "2024-12-31T23:59:59.999",
                            "internalPrice": 92.0000
                          }
                        ]
                      }
                    ],
                    "purchasePrices": [
                      {
                        "purchasePriceId": 1,
                        "amountExclTax": null,
                        "amountInclTax": 0,
                        "taxCode": "V09",
                        "taxPercentage": 9.0000,
                        "fromInclusive": "2024-09-01T00:00:00.000",
                        "toInclusive": "2024-12-31T23:59:59.999"
                      }
                    ]
                  }
              advancedUpdateGboProduct:
                summary: >-
                  Advanced update (remove mandatoryCustomerDataItem references, add new second sellingPeriod)
                value:
                  {
                    "fikoArticleNumber": "1234",
                    "isValid": true,
                    "isArchived": false,
                    "gboPackageTemplateId": "30901",
                    "productName": "HTM pilot 90% korting",
                    "productDescription": "Reis met 90% korting gedurende de eerste F&F pilot!",
                    "validityPeriod": {
                      "validityPeriodId": 1,
                      "fromInclusive": "2024-09-01T00:00:00.000",
                      "toInclusive": "2024-12-31T23:59:59.999"
                    },
                    "productTranslations": [
                      {
                        "language": "en",
                        "name": "HTM pilot 90% discount",
                        "description": "Travel with 90% discount during the first F&F pilot!"
                      }
                    ],
                    "productOwnerId": 1,
                    "marketSegmentIds": [
                      1
                    ],
                    "customerSegmentIds": [
                      5,
                      6
                    ],
                    "allowedGboAgeProfileIds": [],
                    "productCategoryId": 1,
                    "requiredCustomerLevelId": 1,
                    "incompatibleProducts": [
                      {
                        "incompatibleProductId": 1,
                        "description": "Kan niet combineren met zichzelf"
                      }
                    ],
                    "mandatoryCustomerDataItemIds": null,
                    "tokenTypeIds": [
                      1
                    ],
                    "paymentMomentId": 1,
                    "validityDuration": "P7D",
                    "maxStartInFutureDuration": "P6W",
                    "isRenewable": false,
                    "sendInvoice": false,
                    "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg",
                    "productPageUrl": "https://www.htm.nl/nog-onbekende-product-pagina",
                    "termsUrl": "https://www.htm.nl/nog-onbekende-productvoorwaarden-pagina",
                    "isSellableAtHtm": true,
                    "needsSolvencyCheckConsumer": false,
                    "needsSolvencyCheckBusiness": false,
                    "sellingPeriods": [
                      {
                        "sellingPeriodId": 1,
                        "fromInclusive": "2024-09-01T00:00:00.000",
                        "toInclusive": "2024-12-31T23:59:59.999",
                        "salesTouchpointId": 3,
                        "sellingPrices": [
                          {
                            "sellingPriceId": 1,
                            "amountExclTax": null,
                            "taxCode": "V09",
                            "taxPercentage": 9.0000,
                            "amountInclTax": 100,
                            "fromInclusive": "2024-09-01T00:00:00.000",
                            "toInclusive": "2024-12-31T23:59:59.999",
                            "internalPrice": 92.0000
                          }
                        ]
                      },
                      {
                        "fromInclusive": "2024-09-01T00:00:00.000",
                        "toInclusive": "2025-12-31T23:59:59.999",
                        "salesTouchpointId": 2,
                        "sellingPrices": [
                          {
                            "amountExclTax": null,
                            "amountInclTax": 100,
                            "taxCode": "V09",
                            "taxPercentage": 9.0000,
                            "fromInclusive": "2024-09-01T00:00:00.000",
                            "toInclusive": "2024-12-31T23:59:59.999",
                            "internalPrice": 92.0000
                          },
                          {
                            "amountExclTax": null,
                            "amountInclTax": 110,
                            "taxCode": "V09",
                            "taxPercentage": 9.0000,
                            "fromInclusive": "2025-01-01T00:00:00.000",
                            "toInclusive": "2025-12-31T23:59:59.999",
                            "internalPrice": 101.0000
                          }
                        ]
                      }
                    ],
                    "purchasePrices": [
                      {
                        "purchasePriceId": 1,
                        "amountExclTax": null,
                        "amountInclTax": 0,
                        "taxCode": "V09",
                        "taxPercentage": 9.0000,
                        "fromInclusive": "2024-09-01T00:00:00.000",
                        "toInclusive": "2024-12-31T23:59:59.999"
                      }
                    ]
                  }
              updateGboProductMultiplePeriods:
                summary: >-
                  Update existing GBO product (non-renewable, multiple sellingPeriods/prices per period)
                value:
                  {
                    "fikoArticleNumber": "1234",
                    "isValid": true,
                    "isArchived": false,
                    "gboPackageTemplateId": "30901",
                    "productName": "HTM pilot 90% korting",
                    "productDescription": "Reis met 90% korting gedurende de eerste F&F pilot!",
                    "validityPeriod": {
                      "validityPeriodId": 1,
                      "fromInclusive": "2024-09-01T00:00:00.000",
                      "toInclusive": "2025-12-31T23:59:59.999"
                    },
                    "productTranslations": [
                      {
                        "language": "en",
                        "name": "HTM pilot 90% discount",
                        "description": "Travel with 90% discount during the first F&F pilot!"
                      }
                    ],
                    "productOwnerId": 1,
                    "marketSegmentIds": [
                      1
                    ],
                    "customerSegmentIds": [
                      5,
                      6
                    ],
                    "allowedGboAgeProfileIds": [],
                    "productCategoryId": 1,
                    "requiredCustomerLevelId": 1,
                    "incompatibleProducts": [
                      {
                        "incompatibleProductId": 2,
                        "description": "Kan niet combineren met zichzelf"
                      }
                    ],
                    "mandatoryCustomerDataItemIds": [
                      4,
                      7
                    ],
                    "tokenTypeIds": [
                      1
                    ],
                    "paymentMomentId": 1,
                    "validityDuration": "P7D",
                    "maxStartInFutureDuration": "P6W",
                    "isRenewable": false,
                    "sendInvoice": false,
                    "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg",
                    "productPageUrl": "https://www.htm.nl/nog-onbekende-product-pagina",
                    "termsUrl": "https://www.htm.nl/nog-onbekende-productvoorwaarden-pagina",
                    "isSellableAtHtm": true,
                    "needsSolvencyCheckConsumer": false,
                    "needsSolvencyCheckBusiness": false,
                    "sellingPeriods": [
                      {
                        "sellingPeriodId": 1,
                        "fromInclusive": "2024-09-01T00:00:00.000",
                        "toInclusive": "2025-12-31T23:59:59.999",
                        "salesTouchpointId": 3,
                        "sellingPrices": [
                          {
                            "sellingPriceId": 1,
                            "amountExclTax": null,
                            "amountInclTax": 100,
                            "taxCode": "V09",
                            "taxPercentage": 9.0000,
                            "fromInclusive": "2024-09-01T00:00:00.000",
                            "toInclusive": "2024-12-31T23:59:59.999",
                            "internalPrice": 92.0000
                          },
                          {
                            "sellingPriceId": 2,
                            "amountExclTax": null,
                            "amountInclTax": 110,
                            "taxCode": "V09",
                            "taxPercentage": 9.0000,
                            "fromInclusive": "2025-01-01T00:00:00.000",
                            "toInclusive": "2025-12-31T23:59:59.999",
                            "internalPrice": 101.0000
                          }
                        ]
                      },
                      {
                        "sellingPeriodId": 2,
                        "fromInclusive": "2024-09-01T00:00:00.000",
                        "toInclusive": "2025-12-31T23:59:59.999",
                        "salesTouchpointId": 2,
                        "sellingPrices": [
                          {
                            "sellingPriceId": 3,
                            "amountExclTax": null,
                            "amountInclTax": 100,
                            "taxCode": "V09",
                            "taxPercentage": 9.0000,
                            "fromInclusive": "2024-09-01T00:00:00.000",
                            "toInclusive": "2024-12-31T23:59:59.999",
                            "internalPrice": 92.0000
                          },
                          {
                            "sellingPriceId": 4,
                            "amountExclTax": null,
                            "amountInclTax": 110,
                            "taxCode": "V09",
                            "taxPercentage": 9.0000,
                            "fromInclusive": "2025-01-01T00:00:00.000",
                            "toInclusive": "2025-12-31T23:59:59.999",
                            "internalPrice": 101.0000
                          }
                        ]
                      }
                    ],
                    "purchasePrices": [
                      {
                        "purchasePriceId": 1,
                        "amountExclTax": null,
                        "amountInclTax": 0,
                        "taxCode": "V09",
                        "taxPercentage": 9.0000,
                        "fromInclusive": "2024-09-01T00:00:00.000",
                        "toInclusive": "2025-12-31T23:59:59.999"
                      }
                    ]
                  }
              updateGboPadProduct:
                summary: >-
                  Update existing GBO product (PAD required, renewable, allowedGboAgeProfiles, padBirthDate)
                value:
                  {
                    "fikoArticleNumber": "1234",
                    "isValid": true,
                    "isArchived": false,
                    "gboPackageTemplateId": "33610",
                    "productName": "HTM Regio Vrij DH73",
                    "productDescription": "Voor een vast bedrag onbeperkt reizen met EBS, HTM en RET in het gekozen gebied in de regio Rotterdam Den Haag.",
                    "validityPeriod": {
                      "validityPeriodId": 3,
                      "fromInclusive": "2024-09-01T00:00:00.000",
                      "toInclusive": "2024-12-31T23:59:59.999"
                    },
                    "productTranslations": [
                      {
                        "language": "en",
                        "name": "HTM Regio Free DH73",
                        "description": "For a fixed amount unlimited travel with EBS, HTM and RET in the chosen area in the Rotterdam The Hague region."
                      }
                    ],
                    "productOwnerId": 1,
                    "marketSegmentIds": [
                      1
                    ],
                    "customerSegmentIds": [
                      5,
                      6
                    ],
                    "allowedGboAgeProfileIds": [
                      2,
                      3,
                      4,
                      5
                    ],
                    "productCategoryId": 2,
                    "requiredCustomerLevelId": 3,
                    "requiredProducts": [
                      {
                        "requiredProductId": 1,
                        "description": "Basisproduct nodig"
                      }
                    ],
                    "incompatibleProducts": [
                      {
                        "incompatibleProductId": 3,
                        "description": "Kan niet combineren met zichzelf"
                      }
                    ],
                    "mandatoryCustomerDataItemIds": [
                      4,
                      8
                    ],
                    "requiredGboPersonalAttributeIds": [
                      1,
                      2,
                      3
                    ],
                    "tokenTypeIds": [
                      1,
                      2,
                      3
                    ],
                    "paymentMomentId": 1,
                    "serviceOptionIds": [],
                    "validityDuration": "P1M",
                    "maxStartInFutureDuration": "P6W",
                    "isRenewable": true,
                    "sendInvoice": true,
                    "imageReference": "https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg",
                    "productPageUrl": "https://www.htm.nl/nog-onbekende-product-pagina",
                    "termsUrl": "https://www.htm.nl/nog-onbekende-productvoorwaarden-pagina",
                    "isSellableAtHtm": true,
                    "needsSolvencyCheckConsumer": false,
                    "needsSolvencyCheckBusiness": false,
                    "sellingPeriods": [
                      {
                        "sellingPeriodId": 1,
                        "fromInclusive": "2024-09-01T00:00:00.000",
                        "toInclusive": "2024-12-31T23:59:59.999",
                        "salesTouchpointId": 3,
                        "forbiddenPaymentMethodIds": [
                          3
                        ],
                        "sellingPrices": [
                          {
                            "sellingPriceId": 1,
                            "amountExclTax": null,
                            "amountInclTax": 5900,
                            "taxCode": "V09",
                            "taxPercentage": 9.0000,
                            "fromInclusive": "2024-09-01T00:00:00.000",
                            "toInclusive": "2024-12-31T23:59:59.999",
                            "internalPrice": 5413.0000
                          }
                        ]
                      }
                    ],
                    "purchasePrices": [
                      {
                        "purchasePriceId": 1,
                        "amountExclTax": null,
                        "amountInclTax": 0,
                        "taxCode": "V09",
                        "taxPercentage": 9.0000,
                        "fromInclusive": "2024-09-01T00:00:00.000",
                        "toInclusive": "2024-12-31T23:59:59.999"
                      }
                    ]
                  }
              updateGboProductSupplement:
                summary: TODO - Update existing GBO product (supplement with requirements)
                value:
                  {
                    "productName": "TODO"
                  }
              updateTapConnectProduct:
                summary: TODO - Update existing TapConnect product
                value:
                  {
                    "productName": "TODO"
                  }
              updateIbanMandate:
                summary: TODO - Update existing IBAN mandate (functional product)
                value:
                  {
                    "productName": "TODO"
                  }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateOrUpdateProductResponse'
        '400':
          description: '400'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/400Response'
        '401':
          description: '401'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/401Response'
        '404':
          description: '404'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/404Response'
        '500':
          description: '500'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/500Response'
components:
  schemas:
    LayerInfoResponse:
      type: object
      description: >-
        Gives information on the choice that the customer has to make, to enable the touchpoint 
        to proceed further "down the product-tree" by selecting (PATCH-ing) the correct product-variant.
        LayerInfo is not a mandatory product-attribute, but it should always be present on all products for which 
        there are underlying products, i.e. for which `GET /products?parentProductId=...` returns a non-empty list.  
        When no LayerInfo is present, the touchpoint can conclude that the product is a final fulfillable product. \

        **PMT should ensure that all non-leaf-node products (i.e. products that have underlying products) have a `layerInfoId` 
        referenced. If a product is found to be in violation of this rule, its attribtue `isValid` should be set to `false`.**
      required:
        - layerInfoId
        - choiceKey
        - choiceLabel
        - isCustomChoice 
      properties:
        layerInfoId:
          example: 1
          type: integer
        choiceKey:
          example: isRenewable
          type: string
          description: >-
            Contains the JSON Key of the product-attribute that the customer has to make some 
            choice on (determined by PMT), so that te correct product-variant (one of the direct child-products) can be 
            selected by the touchpoint. For some decisions (like region), there is no product attribute, and thus 
            `isCustomChoice` will be set to `true`, and `choiceKey` can then be set to any string on which touchpoints 
            can also trigger behaviour if desired (think "region picker tool"). Therefore, reuse of `choiceKeys` should be 
            the goal, so touchoints can keep their triggers simple and prevent duplication of 
            similar `choiceKeys` to trigger the same behaviour.
        choiceLabel:
          example: Kies voor een doorlopend abonnement of een enkele termijn
          type: string
          description: >-
            Contains a human-readable label for the choice that the customer has to make - this label 
            should be something that is easy to understand for the customer. Only one label (and thus, one language) 
            is supported; label translations should be handled by the touchpoint.
        isCustomChoice:
          example: false
          type: boolean
          description: >-
            Indicates if the choice is a custom choice. If `false`, the PMT should validate that the `choiceKey` is a
            differentiating attribute for the underlying product-variants. When the attribute denoted by the `choiceKey` is
            the same for all underlying variants, PMT validation will fail and the product will become invalid (`isValid == false`) 
            until either the underlying products are updated, or a `LayerInfo` with `isCustomChoice == true` is configured.
    GboAgeProfileResponse:
      type: object
      required:
        - gboAgeProfileId
        - name
        - ageFromInclusive
        - ageUntilInclusive
      properties:
        gboAgeProfileId:
          type: integer
          example: 1
        name:
          type: string
          example: Kind (4 t/m 11 jaar)
        ageFromInclusive:
          type: integer
          description: The minimum age that the GBO age profile applies to
          example: 4
        ageUntilInclusive:
          type: integer
          description: The maximum age that the GBO age profile applies to
          example: 11
    ProductTranslationResponse:
      type: object
      required:
        - language
      properties:
        language:
          type: string
          example: en
        name:
          type: string
          example: HTM F&F 90% korting
        description:
          type: string
          example: >-
            Travel with 90% discount for a week during the first HTM F&F OVpay
            pilot!
    SellingPriceResponse:
      type: object
      required:
        - sellingPriceId
        - amountInclTax
        - fromInclusive
        - toInclusive
        - internalPrice
      properties:
        sellingPriceId:
          type: integer
          example: 1
        amountExclTax:
          type: integer
          example: null
        taxCode:
          type: string
          example: 'V09'
        taxPercentage:
          type: number
          example: 9.0000
        amountInclTax:
          type: integer
          example: 908
        fromInclusive:
          type: string
          format: date-time-offset
          example: '2022-03-01T00:00:00.000+00:00'
        toInclusive:
          type: string
          format: date-time-offset
          example: '2022-06-01T00:00:00.000+00:00'
        internalPrice:
          type: number
          example: 908.1234
    PurchasePriceResponse:
      type: object
      required:
        - purchasePriceId
        - amountInclTax
        - fromInclusive
        - toInclusive
      properties:
        purchasePriceId:
          type: integer
          example: 1
        amountExclTax:
          type: integer
          example: null
        taxCode:
          type: string
          example: 'V09'
        taxPercentage:
          type: number
          example: 9.0000
        amountInclTax:
          type: integer
          example: 908
        fromInclusive:
          type: string
          format: date-time-offset
          example: '2022-03-01T00:00:00.000+00:00'
        toInclusive:
          type: string
          format: date-time-offset
          example: '2022-06-01T00:00:00.000+00:00'
    SalesTouchpointResponse:
      type: object
      required:
        - salesTouchpointId
        - name
        - isActive
        - retailer
      properties:
        salesTouchpointId:
          type: integer
          example: 1
        name:
          type: string
          example: HTM App
        isActive:
          type: boolean
          example: true
        retailer:
          type: object
          required:
            - retailerId
            - name
          properties:
            retailerId:
              type: integer
              example: 1001
            name:
              type: string
              example: HTM
            street:
              type: string
              example: Kon. Julianaplein
            number:
              type: string
              example: '10'
            numberAddition:
              type: string
              example: a
            postalCode:
              type: string
              example: 2595 AA
            city:
              type: string
              example: Den Haag
            country:
              type: string
              example: Nederland
            emailAddress:
              type: string
              format: email
              example: info@htm.nl
            phoneNumber:
              type: string
              example: '09004864636'
            taxId:
              type: string
              example: '09004864636'
            imageReference:
              type: string
              example: https://htm.azure.net/abt/retailers/htm.svg
    ProductPayloadListResponse:
      type: object
      required:
        - Entries
      properties:
        Entries:
          type: array
          items:
            type: object
            required:
              - productId
              - productName
              - productDescription
              - productCategory
              - tokenTypes
              - sellableTouchpointIds
              - amountInclTax
              - imageReference
              - productPageUrl
            properties:
              productId:
                type: integer
                example: 1
              isValid:
                type: boolean
                description: Not returned for external touchpoints
                example: true
              isArchived:
                type: boolean
                description: Not returned for external touchpoints
                example: false
              productName:
                type: string
                example: HTM pilot 90% korting
              productDescription:
                type: string
                example: Reis met 90% korting gedurende de eerste F&F pilot!
              productCategory:
                type: object
                required:
                  - productCategoryId
                  - isTravelProduct
                  - name
                properties:
                  productCategoryId:
                    type: integer
                    example: 1
                  isTravelProduct:
                    type: boolean
                    example: true
                  name:
                    type: string
                    example: Kortingsabonnement
              tokenTypes:
                type: array
                items:
                  type: object
                  required:
                    - tokenTypeId
                    - name
                  properties:
                    tokenTypeId:
                      type: integer
                      example: 1
                    name:
                      type: string
                      example: EMV
              sellableTouchpointIds:
                type: array
                items:
                  type: integer
                  example: 3
              amountInclTax:
                type: number
                example: 100
              imageReference:
                type: string
                example: https://www.htm.nl/media/leif2leu/htm-logo-mobile.svg
              productPageUrl:
                type: string
                format: uri
                example: https://www.htm.nl/nog-onbekende-product-pagina
    ProductPayloadResponse:
      type: object
      required:
        - productId
        - productOwner
        - isValid
        - isArchived
        - isSellableAtHtm
      properties:
        productId:
          type: integer
          example: 1
        parentProductId:
          type: integer
          example: 1
        layerInfo: 
          $ref: '#/components/schemas/LayerInfoResponse'
        fikoArticleNumber:
          type: string
          description: The article number of the product in FIKO
          example: 1234
        isValid:
          type: boolean
          description: Indicates if the product is valid - if false, the Service Engine will prevent touchpoints from seeing this product
          example: true
        isArchived:
          type: boolean
          description: Indicates if the product is archived - if true, the Service Engine will prevent touchpoints from seeing this product
          example: false
        needsVoucher:
          type: boolean
          description: Indicates if the product needs a voucher (with this productId as requiredProductId) to be bought - if false, this is optional, but still allowed
          example: false
        gboPackageTemplateId:
          type: string
          example: '30901'
        tapConnectProductCode:
          type: string
          example: 1234AB
        productName:
          type: string
          example: HTM pilot 90% korting
        productDescription:
          type: string
          example: Reis met 90% korting gedurende de eerste F&F pilot!
        validityPeriod:
          type: object
          required:
            - validityPeriodId
            - fromInclusive
            - toInclusive
          properties:
            validityPeriodId:
              type: integer
              example: 1
            fromInclusive:
              type: string
              format: date-time-offset
              example: '2024-09-03T10:01:34.000+00:00'
            toInclusive:
              type: string
              format: date-time-offset
              example: '2024-09-03T23:59:59.999+00:00'
        productTranslations:
          type: array
          items:
            $ref: '#/components/schemas/ProductTranslationResponse'
        productOwner:
          type: object
          required:
            - productOwnerId
            - name
          properties:
            productOwnerId:
              type: integer
              example: 1
            name:
              type: string
              example: John Doe
            organization:
              type: string
              example: HTM
        marketSegmentse:
          type: array
          items:
            type: object
            required:
              - marketSegmentId
              - name
            properties:
              marketSegmentId:
                type: integer
                example: 1
              name:
                type: string
                example: B2C
        customerSegments:
          type: array
          items:
            type: object
            required:
              - customerSegmentId
              - name
            properties:
              customerSegmentId:
                type: integer
                example: 1
              name:
                type: string
                example: Kind (4-11)
        allowedGboAgeProfiles:
          type: array
          items:
            type: object
            $ref: '#/components/schemas/GboAgeProfileResponse'
        productCategory:
          type: object
          required:
            - productCategoryId
            - name
            - isTravelProduct
          properties:
            productCategoryId:
              type: integer
              example: 1
            name:
              type: string
              example: Kortingsabonnement
            isTravelProduct:
              type: boolean
              example: true
        requiredCustomerLevel:
          type: object
          required:
            - requiredCustomerLevelId
            - name
          properties:
            requiredCustomerLevelId:
              type: integer
              example: 1
            name:
              type: string
              example: guest
        requiredProducts:
          type: array
          items:
            type: object
            required:
              - requiredProductId
              - productName
            properties:
              requiredProductId:
                type: integer
                example: 2
              productName:
                type: string
                example: Product name
              description:
                type: string
                description: Reason why the product is required
                example: Description
        incompatibleProducts:
          type: array
          items:
            type: object
            required:
              - incompatibleProductId
              - productName
            properties:
              incompatibleProductId:
                type: integer
                example: 4
              productName:
                type: string
                example: Product name
              description:
                type: string
                description: >-
                  Reason why the product is incompatible with the selected
                  product
                example: Description
        mandatoryCustomerDataItems:
          type: array
          items:
            type: object
            required:
              - mandatoryCustomerDataItemId
              - customerDataItem
            properties:
              mandatoryCustomerDataItemId:
                type: integer
                example: 3
              customerDataItem:
                type: string
                example: dateOfBirth
        requiredGboPersonalAttributes:
          type: array
          description: >-
            List of required GBO personal attributes (PAD) - should not be
            user-editable, but automatically populated by PMT backend, based on
            the selected gboPackageTemplateId (if any)
          items:
            type: object
            required:
              - requiredGboPersonalAttributeId
              - name
            properties:
              requiredGboPersonalAttributeId:
                type: integer
                example: 2
              name:
                type: string
                example: BIRTHDATE
        tokenTypes:
          type: array
          items:
            type: object
            required:
              - tokenTypeId
              - name
            properties:
              tokenTypeId:
                type: integer
                example: 1
              name:
                type: string
                example: EMV
        paymentMoment:
          type: object
          required:
            - paymentMomentId
            - name
          properties:
            paymentMomentId:
              type: integer
              example: 1
            name:
              type: string
              example: prepaid
        serviceOptions:
          type: array
          items:
            type: object
            required:
              - serviceOptionId
              - action
            properties:
              serviceOptionId:
                type: integer
                example: 1
              action:
                type: string
                example: cancellableTermAhead
              description:
                type: string
                example: Per termijn vooruit opzegbaar
        validityDuration:
          type: string
          format: duration
          example: P1M
        maxStartInFutureDuration:
          type: string
          format: duration
          example: P6W
        isRenewable:
          type: boolean
          example: true
        sendInvoice:
          type: boolean
          example: false
        imageReference:
          type: string
          format: uri
          example: https://htm.azure.net/abt/products/product_1.svg
        productPageUrl:
          type: string
          format: uri
          example: https://www.htm.nl/nog-onbekende-product-pagina
        termsUrl:
          type: string
          format: uri
          example: https://www.htm.nl/nog-onbekende-productvoorwaarden-pagina
        isSellableAtHtm:
          type: boolean
          example: true
        needsSolvencyCheckConsumer:
          type: boolean
          example: false
        needsSolvencyCheckBusiness:
          type: boolean
          example: false
        sellingPeriods:
          type: array
          items:
            type: object
            required:
              - sellingPeriodId
              - fromInclusive
              - toInclusive
              - salesTouchpoint
            properties:
              sellingPeriodId:
                type: integer
                example: 1
              fromInclusive:
                type: string
                format: date-time-offset
                example: '2022-03-01T00:00:00.000+00:00'
              toInclusive:
                type: string
                format: date-time-offset
                example: '2022-06-01T00:00:00.000+00:00'
              salesTouchpoint:
                $ref: '#/components/schemas/SalesTouchpointResponse'
              forbiddenPaymentMethods:
                type: array
                items:
                  type: object
                  required:
                    - forbiddenPaymentMethodId
                    - name
                  properties:
                    forbiddenPaymentMethodId:
                      type: integer
                      example: 3
                    name:
                      type: string
                      example: creditcard
                    issuer:
                      type: string
                      example: American Express
              sellingPrices:
                type: array
                items:
                  $ref: '#/components/schemas/SellingPriceResponse'
        purchasePrices:
          type: array
          items:
            $ref: '#/components/schemas/PurchasePriceResponse'
        auditTrail:
          type: array
          items:
            type: object
            required:
              - auditTrailId
              - action
              - user
              - timestamp
            properties:
              auditTrailId:
                type: integer
                example: 1
              action:
                type: string
                example: insert
              user:
                type: string
                example: username
              timestamp:
                type: string
                format: date-time-offset
                example: '2024-09-03T10:01:34.000+00:00'
    CreateProductRequest:
      required:
        - productOwnerId
        - isValid
        - isArchived
        - isSellableAtHtm
      type: object
      properties:
        parentProductId:
          type: integer
          description: The ID of the parent product (if any)
          example: 1
        layerInfoId:
          type: integer
          description: Only relevant when `parentProductId != null`; the ID of the layerInfo reference, giving information on why this layer of product-variants exists (PMT should enforce that this is filled for all products for which `parentProductId != null`)
          example: 1
        fikoArticleNumber:
          type: string
          description: The article number of the product in FIKO
          example: 1234
        isValid:
          type: boolean
          description: Indicates if the product is valid - if false, the Service Engine will prevent touchpoints from seeing this product
          example: true
        isArchived:
          type: boolean
          description: Indicates if the product is archived - if true, the Service Engine will prevent touchpoints from seeing this product
          example: false
        needsVoucher:
          type: boolean
          description: Indicates if the product needs a voucher (with this productId as requiredProductId) to be bought - if false, this is optional, but still allowed
          example: false
        gboPackageTemplateId:
          type: string
          description: >-
            The ID of the GBO SalesPackage that should be instantiated after
            buying this product
          example: '30901'
        tapConnectProductCode:
          type: string
          description: >-
            The productCode of the TapConnect product that should be issued
            after buying this product
          example: 1234AB
        productName:
          type: string
          description: The name of the product
          example: Test product name
        productDescription:
          type: string
          description: The description of the product
          example: Test product description
        validityPeriod:
          required:
            - fromInclusive
            - toInclusive
          type: object
          description: The validity period of the product
          properties:
            fromInclusive:
              type: string
              format: date-time
              description: >-
                LocalDateTime (without offset) - The start of the validity
                period
              example: '2022-03-01T00:00:00.000'
            toInclusive:
              type: string
              format: date-time
              description: LocalDateTime (without offset) - The end of the validity period
              example: '2022-06-01T00:00:00.000'
        productTranslations:
          type: array
          description: Translations of the product name and description
          items:
            type: object
            required:
              - language
              - name
              - description
            properties:
              language:
                type: string
                example: en
              name:
                type: string
                example: HTM F&F 90% korting
              description:
                type: string
                example: >-
                  Travel with 90% discount for a week during the first HTM F&F
                  OVpay pilot!
        productOwnerId:
          type: integer
          description: The ID of the owner of the product
          example: 1
        marketSegmentIds:
          type: array
          description: The IDs of the market segments that the product is targeted towards
          items:
            type: integer
            example: 1
        customerSegmentIds:
          type: array
          description: >-
            The IDs of the customer segments that the product is targeted
            towards
          items:
            type: integer
            example: 1
        allowedGboAgeProfileIds:
          type: array
          description: The IDs of the allowed GBO age profiles
          items:
            type: integer
            example: 1
        productCategoryId:
          type: integer
          description: The ID of the category the product belongs to
          example: 1
        requiredCustomerLevelId:
          type: integer
          description: >-
            The ID of the required customer level to be allowed to purchase the
            product
        requiredProducts:
          type: array
          required:
            - requiredProductId
          description: >-
            The IDs of products that should already be owned by the customer
            (for travel products, the validity of the given productIds should
            fully overlap on the customer's travel medium)
          items:
            type: object
            properties:
              requiredProductId:
                type: integer
                example: 1
              description:
                type: string
                description: Description of why the product is required
                example: Benodigd basisproduct
        incompatibleProducts:
          type: array
          required:
            - incompatibleProductId
          description: >-
            The IDs of products that can not be active at the same time as this
            product (for travel products, none of the listed productIds should
            have overlapping validity with this product on the customer's travel
            medium)
          items:
            type: object
            properties:
              incompatibleProductId:
                type: integer
                example: 1
              description:
                type: string
                description: Description of why the product is incompatible
                example: Kan niet combineren met zichzelf
        mandatoryCustomerDataItemIds:
          type: array
          description: >-
            The IDs of the customer data items that should be filled in by the
            customer
          items:
            type: integer
            example: 1
        requiredGboPersonalAttributeIds:
          type: array
          description: >-
            GBO specific; the IDs of the GBO Personal Attribute Data (PAD)
            elements that should be present (or filled during the order flow) on
            the customer's chosen travel medium. This attribute should not be
            user-editable, but only be filled by PMT, based on the chosen
            gboPackageTemplateId!
          items:
            type: integer
            example: 1
        tokenTypeIds:
          type: array
          description: The IDs of the token types that the product can be instantiated on
          items:
            type: integer
            example: 1
        paymentMomentId:
          type: integer
          description: The ID of the payment moment for the product
          example: 1
        serviceOptionIds:
          type: array
          description: The IDs of the service options that are available for this product
          items:
            type: integer
            example: 1
        validityDuration:
          type: string
          format: duration
          description: >-
            The validity duration of the product. Should only contain one single
            unit of duration (D, W, M, Y)
          example: P7D
        maxStartInFutureDuration:
          type: string
          format: duration
          description: >-
            The maximum amount of time in the future allowed for the start of
            the product validity. Should only contain one single unit of
            duration (D, W, M, Y)
          example: P6W
        isRenewable:
          type: boolean
          description: Indicates if the product is renewable
          example: false
        sendInvoice:
          type: boolean
          description: >-
            Indicates if an invoice should be sent to the customer after
            ordering this product
          example: false
        imageReference:
          type: string
          format: uri
          description: A reference/URL to the image of the product
          example: https://www.htm.nl/nog-onbekende-productafbeelding
        productPageUrl:
          type: string
          format: uri
          description: >-
            The URL of the product page, to be used by touchpoints which are not
            allowed to sell the product, to redirect the customer. If the
            product is sold via multiple touchpoints, this URL should point to
            the preferred touchpoint for sales
          example: https://www.htm.nl/nog-onbekende-productpagina
        termsUrl:
          type: string
          format: uri
          description: >-
            The URL for the webpage or document containing terms for this
            product
          example: https://www.htm.nl/nog-onbekende-productvoorwaarden
        isSellableAtHtm:
          type: boolean
          description: >-
            Indicates if the product is sellable at HTM. If false, no
            sellingPrices/Periods should be defined - this product may only be
            used for tracking/reporting purposed, or internal use
        needsSolvencyCheckConsumer:
          type: boolean
          description: >-
            Indicates if a solvency check is required for consumers purchasing
            the product
        needsSolvencyCheckBusiness:
          type: boolean
          description: >-
            Indicates if a solvency check is required for businesses purchasing
            the product
        sellingPeriods:
          type: array
          description: >-
            The sellingPeriods of the product. Can contain multiple periods for
            the same or different touchpoints, as long as there is no overlap
            for the same touchpoint
          items:
            type: object
            required:
              - fromInclusive
              - toInclusive
              - salesTouchpointId
            properties:
              fromInclusive:
                type: string
                format: date-time
                description: LocalDateTime (without offset) - The start date of the period
                example: '2020-01-01T00:00:00.000'
              toInclusive:
                type: string
                format: date-time
                description: LocalDateTime (without offset) - The end date of the period
                example: '2020-01-01T00:00:00.000'
              salesTouchpointId:
                type: integer
                description: The ID of the touchpoint this period applies to
                example: 1
              forbiddenPaymentMethodIds:
                type: array
                description: >-
                  The IDs of the payment methods that are not allowed to be used
                  for this touchpoint and period
                items:
                  type: integer
                  example: 1
              sellingPrices:
                type: array
                description: >-
                  The sellingPrices of this product for the touchpoint defined
                  in this sellingPeriod. Can contain multiple prices, as long as
                  there is no overlap in validity
                items:
                  type: object
                  required:
                    - amountInclTax
                    - fromInclusive
                    - toInclusive
                    - internalPrice
                  properties:
                    amountExclTax:
                      type: integer
                      example: null
                    taxCode:
                      type: string
                      example: 'V09'
                    taxPercentage:
                      type: number
                      example: 9.0000
                    amountInclTax:
                      type: integer
                      example: 908
                    fromInclusive:
                      type: string
                      format: date-time
                      description: >-
                        LocalDateTime (without offset) - The start date of the
                        price period
                      example: '2022-03-01T00:00:00.000'
                    toInclusive:
                      type: string
                      format: date-time
                      description: >-
                        LocalDateTime (without offset) - The end date of the
                        price period
                      example: '2022-06-01T00:00:00.000'
                    internalPrice:
                      type: number
                      example: 908.1234
        purchasePrices:
          type: array
          description: >-
            The purchasePrices of the product. Can contain multiple prices, as
            long as there is no overlap in validity
          items:
            type: object
            required:
              - amountInclTax
              - fromInclusive
              - toInclusive
            properties:
              amountExclTax:
                type: integer
                example: null
              taxCode:
                type: string
                example: 'V09'
              taxPercentage:
                type: number
                example: 9.0000
              amountInclTax:
                type: integer
                example: 908
              fromInclusive:
                type: string
                format: date-time
                description: >-
                  LocalDateTime (without offset) - The start date of the price
                  period
                example: '2022-03-01T00:00:00.000'
              toInclusive:
                type: string
                format: date-time
                description: >-
                  LocalDateTime (without offset) - The end date of the price
                  period
                example: '2022-06-01T00:00:00.000'
    UpdateProductRequest:
      required:
        - productOwnerId
        - isValid
        - isArchived
        - isSellableAtHtm
      type: object
      properties:
        parentProductId:
          type: integer
          description: The ID of the parent product (if any)
          example: 1
        layerInfoId:
          type: integer
          description: Only relevant when `parentProductId != null`; the ID of the layerInfo reference, giving information on why this layer of product-variants exists (PMT should enforce that this is filled for all products for which `parentProductId != null`)
          example: 1
        fikoArticleNumber:
          type: string
          description: The article number of the product in FIKO
          example: 1234
        isValid:
          type: boolean
          description: Indicates if the product is valid - if false, the Service Engine will prevent touchpoints from seeing this product
          example: true
        isArchived:
          type: boolean
          description: Indicates if the product is archived - if true, the Service Engine will prevent touchpoints from seeing this product
          example: false
        needsVoucher:
          type: boolean
          description: Indicates if the product needs a voucher (with this productId as requiredProductId) to be bought - if false, this is optional, but still allowed
          example: false
        gboPackageTemplateId:
          type: string
          description: >-
            The ID of the GBO SalesPackage that should be instantiated after
            buying this product
          example: '30901'
        tapConnectProductCode:
          type: string
          description: >-
            The productCode of the TapConnect product that should be issued
            after buying this product
          example: 1234AB
        productName:
          type: string
          description: The name of the product
          example: Test product name
        productDescription:
          type: string
          description: The description of the product
          example: Test product description
        validityPeriod:
          required:
            - fromInclusive
            - toInclusive
          type: object
          description: The validity period of the product
          properties:
            validityPeriodId:
              type: integer
              description: >-
                Including a validityPeriodId will update the existing
                validityPeriod for this product - if omitted, a new
                validityPeriod will be created
              example: 1
            fromInclusive:
              type: string
              format: date-time
              description: >-
                LocalDateTime (without offset) - The start of the validity
                period
              example: '2022-03-01T00:00:00.000'
            toInclusive:
              type: string
              format: date-time
              description: LocalDateTime (without offset) - The end of the validity period
              example: '2022-06-01T00:00:00.000'
        productTranslations:
          type: array
          description: Translations of the product name and description
          items:
            type: object
            required:
              - language
              - name
              - description
            properties:
              language:
                type: string
                example: en
              name:
                type: string
                example: HTM F&F 90% korting
              description:
                type: string
                example: >-
                  Travel with 90% discount for a week during the first HTM F&F
                  OVpay pilot!
        productOwnerId:
          type: integer
          description: The ID of the owner of the product
          example: 1
        marketSegmentIds:
          type: array
          description: The IDs of the market segments that the product is targeted towards
          items:
            type: integer
            example: 1
        customerSegmentIds:
          type: array
          description: >-
            The IDs of the customer segments that the product is targeted
            towards
          items:
            type: integer
            example: 1
        allowedGboAgeProfileIds:
          type: array
          description: The IDs of the allowed GBO age profiles
          items:
            type: integer
            example: 1
        productCategoryId:
          type: integer
          description: The ID of the category the product belongs to
          example: 1
        requiredCustomerLevelId:
          type: integer
          description: >-
            The ID of the required customer level to be allowed to purchase the
            product
        requiredProducts:
          type: array
          required:
            - requiredProductId
          description: >-
            The IDs of products that should already be owned by the customer
            (for travel products, the validity of the given productIds should
            fully overlap on the customer's travel medium)
          items:
            type: object
            properties:
              requiredProductId:
                type: integer
                example: 1
              description:
                type: string
                description: Description of why the product is required
                example: Benodigd basisproduct
        incompatibleProducts:
          type: array
          required:
            - incompatibleProductId
          description: >-
            The IDs of products that can not be active at the same time as this
            product (for travel products, none of the listed productIds should
            have overlapping validity with this product on the customer's travel
            medium)
          items:
            type: object
            properties:
              incompatibleProductId:
                type: integer
                example: 1
              description:
                type: string
                description: Description of why the product is incompatible
                example: Kan niet combineren met zichzelf
        mandatoryCustomerDataItemIds:
          type: array
          description: >-
            The IDs of the customer data items that should be filled in by the
            customer
          items:
            type: integer
            example: 1
        requiredGboPersonalAttributeIds:
          type: array
          description: >-
            GBO specific; the IDs of the GBO Personal Attribute Data (PAD)
            elements that should be present (or filled during the order flow) on
            the customer's chosen travel medium. This attribute should not be
            user-editable, but only be filled by PMT, based on the chosen
            gboPackageTemplateId!
          items:
            type: integer
            example: 1
        tokenTypeIds:
          type: array
          description: The IDs of the token types that the product can be instantiated on
          items:
            type: integer
            example: 1
        paymentMomentId:
          type: integer
          description: The ID of the payment moment for the product
          example: 1
        serviceOptionIds:
          type: array
          description: The IDs of the service options that are available for this product
          items:
            type: integer
            example: 1
        validityDuration:
          type: string
          format: duration
          description: >-
            The validity duration of the product. Should only contain one single
            unit of duration (D, W, M, Y)
          example: P7D
        maxStartInFutureDuration:
          type: string
          format: duration
          description: >-
            The maximum amount of time in the future allowed for the start of
            the product validity. Should only contain one single unit of
            duration (D, W, M, Y)
          example: P6W
        isRenewable:
          type: boolean
          description: Indicates if the product is renewable
          example: false
        sendInvoice:
          type: boolean
          description: >-
            Indicates if an invoice should be sent to the customer after
            ordering this product
          example: false
        imageReference:
          type: string
          format: uri
          description: A reference/URL to the image of the product
          example: https://www.htm.nl/nog-onbekende-productafbeelding
        productPageUrl:
          type: string
          format: uri
          description: >-
            The URL of the product page, to be used by touchpoints which are not
            allowed to sell the product, to redirect the customer. If the
            product is sold via multiple touchpoints, this URL should point to
            the preferred touchpoint for sales
          example: https://www.htm.nl/nog-onbekende-productpagina
        termsUrl:
          type: string
          format: uri
          description: >-
            The URL for the webpage or document containing terms for this
            product
          example: https://www.htm.nl/nog-onbekende-productvoorwaarden
        isSellableAtHtm:
          type: boolean
          description: >-
            Indicates if the product is sellable at HTM. If false, no
            sellingPrices/Periods should be defined - this product may only be
            used for tracking/reporting purposed, or internal use
        needsSolvencyCheckConsumer:
          type: boolean
          description: >-
            Indicates if a solvency check is required for consumers purchasing
            the product
        needsSolvencyCheckBusiness:
          type: boolean
          description: >-
            Indicates if a solvency check is required for businesses purchasing
            the product
        sellingPeriods:
          type: array
          description: >-
            The sellingPeriods of the product. Can contain multiple periods for
            the same or different touchpoints, as long as there is no overlap
            for the same touchpoint
          items:
            type: object
            required:
              - fromInclusive
              - toInclusive
              - salesTouchpointId
            properties:
              sellingPeriodId:
                type: integer
                description: >-
                  Including a sellingPeriodId will update an existing
                  sellingPeriod for this product - if omitted, a new
                  sellingPeriod will be created
                example: 1
              fromInclusive:
                type: string
                format: date-time
                description: LocalDateTime (without offset) - The start date of the period
                example: '2020-01-01T00:00:00.000'
              toInclusive:
                type: string
                format: date-time
                description: LocalDateTime (without offset) - The end date of the period
                example: '2020-01-01T00:00:00.000'
              salesTouchpointId:
                type: integer
                description: The ID of the touchpoint this period applies to
                example: 1
              forbiddenPaymentMethodIds:
                type: array
                description: >-
                  The IDs of the payment methods that are not allowed to be used
                  for this touchpoint and period
                items:
                  type: integer
                  example: 1
              sellingPrices:
                type: array
                description: >-
                  The sellingPrices of this product for the touchpoint defined
                  in this sellingPeriod. Can contain multiple prices, as long as
                  there is no overlap in validity
                items:
                  type: object
                  required:
                    - amountInclTax
                    - fromInclusive
                    - toInclusive
                    - internalPrice
                  properties:
                    sellingPeriodId:
                      type: integer
                      description: >-
                        Including a sellingPriceId will update an existing
                        sellingPrice for this product - if omitted, a new
                        sellingPrice will be created
                      example: 1
                    amountExclTax:
                      type: integer
                      example: null
                    amountInclTax:
                      type: integer
                      example: 908
                    taxCode:
                      type: string
                      example: V09
                    taxPercentage:
                      type: number
                      example: 9.0000
                    fromInclusive:
                      type: string
                      format: date-time
                      description: >-
                        LocalDateTime (without offset) - The start date of the
                        period
                      example: '2022-03-01T00:00:00.000'
                    toInclusive:
                      type: string
                      format: date-time
                      description: >-
                        LocalDateTime (without offset) - The end date of the
                        period
                      example: '2022-06-01T00:00:00.000'
                    internalPrice:
                      type: number
                      example: 908.1234
        purchasePrices:
          type: array
          description: >-
            The purchasePrices of the product. Can contain multiple prices, as
            long as there is no overlap in validity
          items:
            type: object
            required:
              - amountInclTax
              - fromInclusive
              - toInclusive
            properties:
              purchasePriceId:
                type: integer
                description: >-
                  Including a purchasePriceId will update an existing
                  purchasePrice for this product - if omitted, a new
                  purchasePrice will be created
                example: 1
              amountExclTax:
                type: integer
                example: null
              amountInclTax:
                type: integer
                example: 908
              taxCode:
                type: string
                example: V09
              taxPercentage:
                type: number
                example: 9.0000
              fromInclusive:
                type: string
                format: date-time
                description: LocalDateTime (without offset) - The start date of the period
                example: '2022-03-01T00:00:00.000'
              toInclusive:
                type: string
                format: date-time
                description: LocalDateTime (without offset) - The end date of the period
                example: '2022-06-01T00:00:00.000'
    CreateOrUpdateProductResponse:
      type: object
      properties:
        productId:
          type: integer
          example: 22
    400Response:
      type: object
      properties:
        code:
          type: string
          example: '400'
        type:
          type: string
        message:
          type: string
          example: Bad Request
        description:
          type: string
          example: '''2023-02-01 00:00:00'' is not a valid Datetime'
        apiErrorCode:
          type: string
          example: htm.api.err.40xxx
    401Response:
      type: object
      properties:
        code:
          type: string
          example: '900901'
        type:
          type: string
        message:
          type: string
          example: Invalid Credentials
        description:
          type: string
          example: >-
            Invalid Credentials. Make sure you have provided the correct
            security credentials
        apiErrorCode:
          type: string
          example: htm.api.err.40xxx
    403Response:
      type: object
      properties:
        code:
          type: string
          example: '900901'
        type:
          type: string
        message:
          type: string
          example: Retailer not authorized
        description:
          type: string
          example: The retailer is not allowed to access resource for requested
        apiErrorCode:
          type: string
          example: htm.api.err.40xxx touchpoint
    404Response:
      type: object
      properties:
        code:
          type: string
          example: '404'
        type:
          type: string
          example: Status report
        message:
          type: string
          example: Not Found
        description:
          type: string
          example: The requested resource is not available.
        apiErrorCode:
          type: string
          example: htm.api.err.40xxx
    500Response:
      type: object
      properties:
        code:
          type: string
          example: '500'
        type:
          type: string
        message:
          type: string
          example: Internal Server Error
        description:
          type: string
        apiErrorCode:
          type: string
          example: htm.api.err.40xxx
