{
  "info": {
    "name": "PREISmonitoring REST API – Lesezugriff",
    "_postman_id": "8f3c1d24-6b5a-4f19-9d2e-2a7c5b0e41a3",
    "description": "Token-Abruf und lesende Endpunkte des öffentlichen REST-Vertrags von PREISmonitoring.\n\nMaßgeblich ist die OpenAPI-Spezifikation unter {{baseUrl}}/openapi/openapi.yaml. Diese Collection enthält keine Zugangsdaten: setzen Sie loginIdentifier und password in einer Postman-Umgebung, nicht in der Collection. accessToken füllt Request 02 automatisch.\n\nFühren Sie die Requests von oben nach unten aus. 02 legt den Token ab, 04 den Item-Cursor für 05, 07 die orderId für 08 bis 11.\n\nEinen zeitlich begrenzten kostenlosen Zugang können Sie über https://www.preismonitoring.de/kontakt anfordern.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [{ "key": "token", "value": "{{accessToken}}", "type": "string" }]
  },
  "variable": [
    {
      "key": "baseUrl",
      "value": "",
      "type": "string",
      "description": "Basis-URL ohne abschließenden Schrägstrich. live: https://api.preismonitoring.de, staging: https://staging-frontend-3.preismonitoring.de/dev"
    },
    {
      "key": "loginIdentifier",
      "value": "",
      "type": "string",
      "description": "E-Mail oder userName. In einer Umgebung setzen, nicht hier."
    },
    {
      "key": "password",
      "value": "",
      "type": "string",
      "description": "Passwort. In einer Umgebung setzen, nicht hier."
    },
    {
      "key": "accessToken",
      "value": "",
      "type": "string",
      "description": "Wird von Request 02 gesetzt. Nicht manuell pflegen."
    },
    {
      "key": "itemsCursor",
      "value": "",
      "type": "string",
      "description": "Wird von Request 04 aus paging.nextCursor gesetzt."
    },
    {
      "key": "orderId",
      "value": "",
      "type": "string",
      "description": "Wird von Request 07 aus dem ersten sichtbaren Auftrag gesetzt."
    }
  ],
  "item": [
    {
      "name": "00 Health-Check",
      "request": {
        "auth": { "type": "noauth" },
        "method": "GET",
        "header": [],
        "url": { "raw": "{{baseUrl}}/healthz", "host": ["{{baseUrl}}"], "path": ["healthz"] },
        "description": "Erreichbarkeit ohne Authentifizierung. Liefert zusätzlich die Vertragsversion (contractVersion) und die Build-Version (build) des erreichten Servers — beides Diagnoseangaben, kein Signal zur Feature-Erkennung."
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Status 200', function () { pm.expect(pm.response.code).to.eql(200); });",
              "pm.test('status ist ok', function () { pm.expect(pm.response.json().status).to.eql('ok'); });",
          "pm.test('meldet Vertrags- und Build-Version', function () { const body = pm.response.json(); pm.expect(body.contractVersion, 'contractVersion').to.be.a('string').and.not.empty; pm.expect(body.build, 'build').to.be.a('string').and.not.empty; });"
            ]
          }
        }
      ]
    },
    {
      "name": "01 OpenAPI-Vertrag abrufen",
      "request": {
        "auth": { "type": "noauth" },
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/openapi/openapi.yaml",
          "host": ["{{baseUrl}}"],
          "path": ["openapi", "openapi.yaml"]
        },
        "description": "Der maßgebliche REST-Vertrag, maschinenlesbar."
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Status 200', function () { pm.expect(pm.response.code).to.eql(200); });",
              "pm.test('ist eine OpenAPI-Datei', function () { pm.expect(pm.response.text()).to.include('openapi:'); });"
            ]
          }
        }
      ]
    },
    {
      "name": "02 Access Token",
      "request": {
        "auth": { "type": "noauth" },
        "method": "POST",
        "header": [{ "key": "Content-Type", "value": "application/json" }],
        "body": {
          "mode": "raw",
          "raw": "{\n  \"loginIdentifier\": \"{{loginIdentifier}}\",\n  \"password\": \"{{password}}\"\n}",
          "options": { "raw": { "language": "json" } }
        },
        "url": {
          "raw": "{{baseUrl}}/v1/auth/token",
          "host": ["{{baseUrl}}"],
          "path": ["v1", "auth", "token"]
        },
        "description": "Tauscht Login-Identifier und Passwort gegen einen Bearer-Token. Der Token wird in accessToken abgelegt und von allen folgenden Requests verwendet. Administrative Konten erhalten hier bewusst keinen Token."
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Status 200', function () { pm.expect(pm.response.code).to.eql(200); });",
              "pm.test('accessToken vorhanden', function () { pm.expect(pm.response.json().accessToken).to.be.a('string'); });",
              "pm.collectionVariables.set('accessToken', pm.response.json().accessToken);"
            ]
          }
        }
      ]
    },
    {
      "name": "03 Sites",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/v1/sites?limit=5",
          "host": ["{{baseUrl}}"],
          "path": ["v1", "sites"],
          "query": [
            { "key": "limit", "value": "5" },
            { "key": "site", "value": "", "disabled": true, "description": "Auf einen Site-Schlüssel einschränken." }
          ]
        },
        "description": "Beobachtete Shops und Plattformen im freigegebenen Scope."
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Status 200', function () { pm.expect(pm.response.code).to.eql(200); });",
              "pm.test('data und paging vorhanden', function () { var b = pm.response.json(); pm.expect(b.data).to.be.an('array'); pm.expect(b.paging).to.be.an('object'); });"
            ]
          }
        }
      ]
    },
    {
      "name": "04 Items (Seite 1)",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/v1/items?limit=1",
          "host": ["{{baseUrl}}"],
          "path": ["v1", "items"],
          "query": [
            { "key": "limit", "value": "1" },
            { "key": "itemId", "value": "", "disabled": true },
            { "key": "pzn", "value": "", "disabled": true },
            { "key": "ean", "value": "", "disabled": true },
            { "key": "asin", "value": "", "disabled": true },
            { "key": "asinLocale", "value": "", "disabled": true }
          ]
        },
        "description": "Erste Seite der sichtbaren Produkte. limit=1 erzwingt eine Folgeseite, damit Request 05 den Cursor zeigen kann."
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Status 200', function () { pm.expect(pm.response.code).to.eql(200); });",
              "pm.test('data und paging vorhanden', function () { var b = pm.response.json(); pm.expect(b.data).to.be.an('array'); pm.expect(b.paging).to.be.an('object'); });",
              "pm.collectionVariables.set('itemsCursor', pm.response.json().paging.nextCursor || '');"
            ]
          }
        }
      ]
    },
    {
      "name": "05 Items (Folgeseite über Cursor)",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/v1/items?limit=1&cursor={{itemsCursor}}",
          "host": ["{{baseUrl}}"],
          "path": ["v1", "items"],
          "query": [
            { "key": "limit", "value": "1" },
            { "key": "cursor", "value": "{{itemsCursor}}" }
          ]
        },
        "description": "Blättern mit dem Cursor aus Request 04. Der Cursor ist opak und wird unverändert übernommen. Fehlt paging.nextCursor in einer Antwort, war es die letzte Seite."
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Status 200', function () { pm.expect(pm.response.code).to.eql(200); });",
              "pm.test('data vorhanden', function () { pm.expect(pm.response.json().data).to.be.an('array'); });"
            ]
          }
        }
      ]
    },
    {
      "name": "06 Items – ASIN-Locales",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/v1/items/asin-locales",
          "host": ["{{baseUrl}}"],
          "path": ["v1", "items", "asin-locales"]
        },
        "description": "Die im Scope tatsächlich vorkommenden ASIN-Locales."
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Status 200', function () { pm.expect(pm.response.code).to.eql(200); });"
            ]
          }
        }
      ]
    },
    {
      "name": "07 Orders",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/v1/orders?limit=5",
          "host": ["{{baseUrl}}"],
          "path": ["v1", "orders"],
          "query": [
            { "key": "limit", "value": "5" },
            { "key": "orderId", "value": "", "disabled": true },
            { "key": "alias", "value": "", "disabled": true },
            { "key": "includeMonitoring", "value": "true", "disabled": true },
            { "key": "includeCoverage", "value": "true", "disabled": true }
          ]
        },
        "description": "Sichtbare Aufträge. Legt orderId für die folgenden Requests ab — und zwar den Auftrag mit den wenigsten Items. Grund: die Anreicherung der Detail- und Results-Antworten hat eine Obergrenze; bei sehr großen Aufträgen antwortet der Detailendpunkt mit 400 ORDER_COVERAGE_SCOPE_TOO_BROAD oder läuft in einen Timeout. Für einen Durchlauf der Collection ist der kleinste Auftrag deshalb die verlässliche Wahl."
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Status 200', function () { pm.expect(pm.response.code).to.eql(200); });",
              "pm.test('data und paging vorhanden', function () { var b = pm.response.json(); pm.expect(b.data).to.be.an('array'); pm.expect(b.paging).to.be.an('object'); });",
              "var rows = pm.response.json().data || [];",
              "var smallest = '';",
              "var fewestItems = -1;",
              "for (var i = 0; i < rows.length; i += 1) {",
              "  var count = rows[i].itemCount || 0;",
              "  if (fewestItems < 0 || count < fewestItems) { fewestItems = count; smallest = rows[i].id || ''; }",
              "}",
              "if (smallest) { pm.collectionVariables.set('orderId', smallest); }"
            ]
          }
        }
      ]
    },
    {
      "name": "08 Order – Detail",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/v1/orders/{{orderId}}",
          "host": ["{{baseUrl}}"],
          "path": ["v1", "orders", "{{orderId}}"]
        },
        "description": "Detail zu einem sichtbaren Auftrag."
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Status 200', function () { pm.expect(pm.response.code).to.eql(200); });",
              "pm.test('qualitySummary ist widerspruchsfrei markiert', function () { var q = pm.response.json().data.qualitySummary; pm.expect(q).to.be.an('object'); pm.expect(q.configuredObservations).to.be.a('number'); pm.expect(q.sourceCountsConsistent).to.be.a('boolean'); });"
            ]
          }
        }
      ]
    },
    {
      "name": "08b Order – Beobachtungsqualität",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/v1/orders/{{orderId}}/observation-quality?limit=5",
          "host": ["{{baseUrl}}"],
          "path": ["v1", "orders", "{{orderId}}", "observation-quality"],
          "query": [
            { "key": "limit", "value": "5" },
            { "key": "site", "value": "", "disabled": true },
            { "key": "reasonCode", "value": "SUCCESSFUL", "disabled": true }
          ]
        },
        "description": "Aktueller Reporter-Snapshot je konfiguriertem Produkt/Site-Paar. Technische Retry-Versuche erzeugen keine zusätzlichen Einträge."
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Status 200', function () { pm.expect(pm.response.code).to.eql(200); });",
              "pm.test('data und paging vorhanden', function () { var b = pm.response.json(); pm.expect(b.data).to.be.an('array'); pm.expect(b.paging).to.be.an('object'); });",
              "pm.test('neutrale Ergebniscodes', function () { var rows = pm.response.json().data || []; rows.forEach(function (r) { pm.expect(r.reasonCode).to.be.oneOf(['SUCCESSFUL','NOT_YET_PROCESSED','ACCESS_TEMPORARILY_RESTRICTED','AMBIGUOUS_MATCH','PRODUCT_NOT_FOUND','INSUFFICIENT_INFORMATION','PRICE_NOT_DETERMINED','PRODUCT_UNAVAILABLE','COLLECTION_ERROR','UNKNOWN']); }); });"
            ]
          }
        }
      ]
    },
    {
      "name": "09 Order – Exports",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/v1/orders/{{orderId}}/exports?limit=5",
          "host": ["{{baseUrl}}"],
          "path": ["v1", "orders", "{{orderId}}", "exports"],
          "query": [
            { "key": "limit", "value": "5" },
            { "key": "exportName", "value": "", "disabled": true }
          ]
        },
        "description": "Konfigurierte Exporte des Auftrags."
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Status 200', function () { pm.expect(pm.response.code).to.eql(200); });"
            ]
          }
        }
      ]
    },
    {
      "name": "10 Order – Export-Historie",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/v1/orders/{{orderId}}/exports/history?limit=5",
          "host": ["{{baseUrl}}"],
          "path": ["v1", "orders", "{{orderId}}", "exports", "history"],
          "query": [
            { "key": "limit", "value": "5" },
            { "key": "dateFrom", "value": "", "disabled": true },
            { "key": "dateTo", "value": "", "disabled": true },
            { "key": "exportName", "value": "", "disabled": true },
            { "key": "name", "value": "", "disabled": true }
          ]
        },
        "description": "Vergangene Exportläufe des Auftrags."
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Status 200', function () { pm.expect(pm.response.code).to.eql(200); });"
            ]
          }
        }
      ]
    },
    {
      "name": "11 Order – Import-Historie",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/v1/orders/{{orderId}}/imports/history?limit=5",
          "host": ["{{baseUrl}}"],
          "path": ["v1", "orders", "{{orderId}}", "imports", "history"],
          "query": [
            { "key": "limit", "value": "5" },
            { "key": "dateFrom", "value": "", "disabled": true },
            { "key": "dateTo", "value": "", "disabled": true },
            { "key": "name", "value": "", "disabled": true }
          ]
        },
        "description": "Vergangene Importläufe des Auftrags."
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Status 200', function () { pm.expect(pm.response.code).to.eql(200); });"
            ]
          }
        }
      ]
    },
    {
      "name": "10a Order – Neuester Exportlauf",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/v1/orders/{{orderId}}/exports/history/latest",
          "host": ["{{baseUrl}}"],
          "path": ["v1", "orders", "{{orderId}}", "exports", "history", "latest"]
        },
        "description": "Neuester passender Exportlauf. Gibt 404 mit HISTORY_ARTIFACT_NOT_FOUND zurück, wenn für den gültigen Auftrag kein Artefakt vorhanden ist."
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Status 200 oder 404', function () { pm.expect(pm.response.code).to.be.oneOf([200, 404]); });",
              "pm.test('bei 200 enthält Antwort data', function () { if (pm.response.code === 200) { pm.expect(pm.response.json().data).to.be.an('object'); } });",
              "pm.test('bei 404 spezifischer Fehlercode', function () { if (pm.response.code === 404) { pm.expect(pm.response.json().code).to.eql('HISTORY_ARTIFACT_NOT_FOUND'); } });"
            ]
          }
        }
      ]
    },
    {
      "name": "10b Order – Export herunterladen",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/v1/orders/{{orderId}}/exports/history/download?latest=true",
          "host": ["{{baseUrl}}"],
          "path": ["v1", "orders", "{{orderId}}", "exports", "history", "download"],
          "query": [{ "key": "latest", "value": "true" }]
        },
        "description": "Lädt das Artefakt des neuesten passenden Exportlaufs herunter. Gibt 404 mit HISTORY_ARTIFACT_NOT_FOUND beziehungsweise 409 mit HISTORY_ARTIFACT_CONTENT_MISSING zurück."
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Status 200, 404 oder 409', function () { pm.expect(pm.response.code).to.be.oneOf([200, 404, 409]); });",
              "pm.test('bei 200 Content-Disposition vorhanden', function () { if (pm.response.code === 200) { pm.expect(pm.response.headers.get('Content-Disposition')).to.be.a('string').and.not.empty; } });",
              "pm.test('bei 404 spezifischer Fehlercode', function () { if (pm.response.code === 404) { pm.expect(pm.response.json().code).to.eql('HISTORY_ARTIFACT_NOT_FOUND'); } });",
              "pm.test('bei 409 fehlender Archivinhalt', function () { if (pm.response.code === 409) { pm.expect(pm.response.json().code).to.eql('HISTORY_ARTIFACT_CONTENT_MISSING'); } });"
            ]
          }
        }
      ]
    },
    {
      "name": "11a Order – Imports",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/v1/orders/{{orderId}}/imports?limit=5",
          "host": ["{{baseUrl}}"],
          "path": ["v1", "orders", "{{orderId}}", "imports"],
          "query": [
            { "key": "limit", "value": "5" },
            { "key": "name", "value": "", "disabled": true }
          ]
        },
        "description": "Konfigurierte Importe des Auftrags."
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Status 200', function () { pm.expect(pm.response.code).to.eql(200); });",
              "pm.test('data und paging vorhanden', function () { var b = pm.response.json(); pm.expect(b.data).to.be.an('array'); pm.expect(b.paging).to.be.an('object'); });"
            ]
          }
        }
      ]
    },
    {
      "name": "11b Order – Neuester Importlauf",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/v1/orders/{{orderId}}/imports/history/latest",
          "host": ["{{baseUrl}}"],
          "path": ["v1", "orders", "{{orderId}}", "imports", "history", "latest"]
        },
        "description": "Neuester passender Importlauf. Gibt 404 mit HISTORY_ARTIFACT_NOT_FOUND zurück, wenn für den gültigen Auftrag kein Artefakt vorhanden ist."
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Status 200 oder 404', function () { pm.expect(pm.response.code).to.be.oneOf([200, 404]); });",
              "pm.test('bei 200 enthält Antwort data', function () { if (pm.response.code === 200) { pm.expect(pm.response.json().data).to.be.an('object'); } });",
              "pm.test('bei 404 spezifischer Fehlercode', function () { if (pm.response.code === 404) { pm.expect(pm.response.json().code).to.eql('HISTORY_ARTIFACT_NOT_FOUND'); } });"
            ]
          }
        }
      ]
    },
    {
      "name": "11c Order – Import herunterladen",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/v1/orders/{{orderId}}/imports/history/download?latest=true",
          "host": ["{{baseUrl}}"],
          "path": ["v1", "orders", "{{orderId}}", "imports", "history", "download"],
          "query": [{ "key": "latest", "value": "true" }]
        },
        "description": "Lädt das Artefakt des neuesten passenden Importlaufs herunter. Gibt 404 mit HISTORY_ARTIFACT_NOT_FOUND beziehungsweise 409 mit HISTORY_ARTIFACT_CONTENT_MISSING zurück."
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Status 200, 404 oder 409', function () { pm.expect(pm.response.code).to.be.oneOf([200, 404, 409]); });",
              "pm.test('bei 200 Content-Disposition vorhanden', function () { if (pm.response.code === 200) { pm.expect(pm.response.headers.get('Content-Disposition')).to.be.a('string').and.not.empty; } });",
              "pm.test('bei 404 spezifischer Fehlercode', function () { if (pm.response.code === 404) { pm.expect(pm.response.json().code).to.eql('HISTORY_ARTIFACT_NOT_FOUND'); } });",
              "pm.test('bei 409 fehlender Archivinhalt', function () { if (pm.response.code === 409) { pm.expect(pm.response.json().code).to.eql('HISTORY_ARTIFACT_CONTENT_MISSING'); } });"
            ]
          }
        }
      ]
    },
    {
      "name": "12 Results – Fähigkeiten",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/v1/results/capabilities",
          "host": ["{{baseUrl}}"],
          "path": ["v1", "results", "capabilities"]
        },
        "description": "Welche Filter, Sortierungen und Zusatzblöcke der Results-Endpunkt in dieser Umgebung unterstützt. Lesen Sie das vor dem Bauen von Abfragen."
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Status 200', function () { pm.expect(pm.response.code).to.eql(200); });"
            ]
          }
        }
      ]
    },
    {
      "name": "13 Results",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/v1/results?limit=5",
          "host": ["{{baseUrl}}"],
          "path": ["v1", "results"],
          "query": [
            { "key": "limit", "value": "5" },
            { "key": "cursor", "value": "", "disabled": true },
            { "key": "orderId", "value": "", "disabled": true },
            { "key": "itemId", "value": "", "disabled": true },
            { "key": "pzn", "value": "", "disabled": true },
            { "key": "ean", "value": "", "disabled": true },
            { "key": "asin", "value": "", "disabled": true },
            { "key": "asinLocale", "value": "", "disabled": true },
            { "key": "site", "value": "", "disabled": true },
            { "key": "globalScope", "value": "true", "disabled": true },
            { "key": "category", "value": "", "disabled": true },
            { "key": "brand", "value": "", "disabled": true },
            { "key": "manufacturer", "value": "", "disabled": true },
            { "key": "merchant", "value": "", "disabled": true },
            { "key": "isPrime", "value": "true", "disabled": true },
            { "key": "isBuyBox", "value": "true", "disabled": true },
            { "key": "coverageState", "value": "", "disabled": true },
            { "key": "capturedFrom", "value": "", "disabled": true },
            { "key": "capturedTo", "value": "", "disabled": true },
            { "key": "sort", "value": "desc", "disabled": true },
            { "key": "timezone", "value": "", "disabled": true },
            { "key": "latestPerPair", "value": "true", "disabled": true },
            { "key": "latestObservationPerPair", "value": "true", "disabled": true },
            { "key": "includeCoverage", "value": "true", "disabled": true },
            { "key": "includeOrderIds", "value": "true", "disabled": true },
            { "key": "includeCustomerData", "value": "true", "disabled": true },
            { "key": "includeExportFields", "value": "true", "disabled": true },
            { "key": "includeIdentity", "value": "true", "disabled": true }
          ]
        },
        "description": "Preis- und Verfügbarkeitsbeobachtungen. Die deaktivierten Parameter zeigen den vertraglichen Filterumfang; aktivieren Sie nur, was Sie brauchen. globalScope=true ist ein expliziter Cross-Site-Read und benötigt capturedFrom sowie capturedTo ohne Produkt-, Order- oder Site-Scope; das Fenster ist auf 168 Stunden begrenzt. Mit includeExportFields=true enthält customerReferencePrices alle nach autorisiertem Mandat, Result-Währung und exakter oder generischer Site passenden Referenzpreise; referencePrice und referencePriceMoney erscheinen nur bei genau einem Treffer. Außerdem kann isPrime als boolesche Quellbeobachtung für kanonische Amazon-Ländermärkte erscheinen. Das Feld fehlt bei anderen Shops oder unbekanntem Wert und ist unabhängig von isBuyBox. productRating ist ausschließlich eine beobachtete kanonische-Idealo-DE-Antwort: Gültige Werte von 0 bis 5 werden auf eine Nachkommastelle gerundet; bei anderen Plattformen, einschließlich Idealo AT und Idealo All Results DE, fehlt das Feld. Ohne Filter kann die Abfrage in großen Scopes mehrere Sekunden dauern."
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Status 200', function () { pm.expect(pm.response.code).to.eql(200); });",
              "pm.test('data und paging vorhanden', function () { var b = pm.response.json(); pm.expect(b.data).to.be.an('array'); pm.expect(b.paging).to.be.an('object'); });",
              "pm.test('Offer flags are Boolean when present', function () { pm.response.json().data.forEach(function (row) { ['isPrime', 'isBuyBox'].forEach(function (field) { if (Object.prototype.hasOwnProperty.call(row, field)) { pm.expect(row[field]).to.be.a('boolean'); } }); }); });",
              "pm.test('productRating is in range and rounded when present', function () { pm.response.json().data.forEach(function (row) { if (Object.prototype.hasOwnProperty.call(row, 'productRating')) { var rating = row.productRating; pm.expect(rating.value).to.be.within(0, 5); pm.expect(Math.abs(rating.value * 10 - Math.round(rating.value * 10))).to.be.below(1e-9); pm.expect(rating.scale).to.eql(5); } }); });",
              "pm.test('customer reference singular fields are unambiguous', function () { pm.response.json().data.forEach(function (row) { var refs = row.customerReferencePrices || []; if (Object.prototype.hasOwnProperty.call(row, 'referencePrice')) { pm.expect(refs).to.have.lengthOf(1); pm.expect(row.referencePrice).to.eql(refs[0].amountMinor); } }); });"
            ]
          }
        }
      ]
    },
    {
      "name": "14 Results – Stream",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/v1/results/stream?orderId={{orderId}}",
          "host": ["{{baseUrl}}"],
          "path": ["v1", "results", "stream"],
          "query": [
            { "key": "orderId", "value": "{{orderId}}" },
            { "key": "sort", "value": "desc", "disabled": true },
            { "key": "site", "value": "", "disabled": true },
            { "key": "globalScope", "value": "true", "disabled": true },
            { "key": "category", "value": "", "disabled": true },
            { "key": "brand", "value": "", "disabled": true },
            { "key": "manufacturer", "value": "", "disabled": true },
            { "key": "merchant", "value": "", "disabled": true },
            { "key": "isPrime", "value": "true", "disabled": true },
            { "key": "isBuyBox", "value": "true", "disabled": true },
            { "key": "capturedFrom", "value": "", "disabled": true },
            { "key": "capturedTo", "value": "", "disabled": true },
            { "key": "latestObservationPerPair", "value": "true", "disabled": true },
            { "key": "includeExportFields", "value": "true", "disabled": true }
          ]
        },
        "description": "Dieselbe Ergebnismenge als Stream, für große Abrufe ohne Seitenschleife. Mit includeExportFields=true enthält customerReferencePrices alle nach autorisiertem Mandat, Result-Währung und exakter oder generischer Site passenden Referenzpreise; referencePrice und referencePriceMoney erscheinen nur bei genau einem Treffer. Außerdem kann isPrime als boolesche Quellbeobachtung für kanonische Amazon-Ländermärkte erscheinen. Das Feld fehlt bei anderen Shops oder unbekanntem Wert und ist unabhängig von isBuyBox. productRating ist ausschließlich eine beobachtete kanonische-Idealo-DE-Antwort: Gültige Werte von 0 bis 5 werden auf eine Nachkommastelle gerundet; bei anderen Plattformen, einschließlich Idealo AT und Idealo All Results DE, fehlt das Feld.\n\nDer Request ist hier bewusst auf einen Auftrag eingegrenzt. Ohne Eingrenzung liefert der Stream alles, was Ihr Konto sehen darf, und läuft entsprechend lange — in einem Scope mittlerer Größe deutlich länger als eine Minute. `limit` begrenzt die Gesamtmenge des Streams nicht. Für einen bewusst globalen Read setzen Sie `globalScope=true` mit beiden Grenzen capturedFrom/capturedTo (höchstens 168 Stunden) und deaktivieren orderId sowie jeden weiteren Produkt-, Order- und Site-Scope — sonst antwortet der Endpunkt mit 400. Der Response-Header X-Results-Scope markiert den Modus.\n\nEin Zeitfenster über capturedFrom/capturedTo verlangt ohne globalScope zusätzlich einen Scope (itemId, pzn, ean, asin, orderId oder site); ohne Scope antwortet der Endpunkt mit 400 und nennt genau das."
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Status 200', function () { pm.expect(pm.response.code).to.eql(200); });",
              "pm.test('Offer flags are Boolean when present', function () { pm.response.text().split(/\\r?\\n/).filter(Boolean).forEach(function (line) { var row = JSON.parse(line); ['isPrime', 'isBuyBox'].forEach(function (field) { if (Object.prototype.hasOwnProperty.call(row, field)) { pm.expect(row[field]).to.be.a('boolean'); } }); }); });",
              "pm.test('productRating is in range and rounded when present', function () { pm.response.text().split(/\\r?\\n/).filter(Boolean).forEach(function (line) { var row = JSON.parse(line); if (Object.prototype.hasOwnProperty.call(row, 'productRating')) { var rating = row.productRating; pm.expect(rating.value).to.be.within(0, 5); pm.expect(Math.abs(rating.value * 10 - Math.round(rating.value * 10))).to.be.below(1e-9); pm.expect(rating.scale).to.eql(5); } }); });"
            ]
          }
        }
      ]
    }
  ]
}
