curl --request POST \
--url https://app.polygon-one.com/api/ppwr/components \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
[
{
"code": "<string>",
"name": "<string>",
"materials": [],
"supplierName": "<string>",
"massGrams": 123,
"recycledContentPct": 123,
"contactSensitive": true,
"foodContact": true,
"heavyMetalsMgPerKg": 123,
"heavyMetalsMethod": "<string>",
"pfasCompliant": true,
"pfasMethod": "<string>",
"pfasSumPpb": 123,
"pfasTotalFluorineMgPerKg": 123,
"notes": "<string>"
}
]
'import requests
url = "https://app.polygon-one.com/api/ppwr/components"
payload = [
{
"code": "<string>",
"name": "<string>",
"materials": [],
"supplierName": "<string>",
"massGrams": 123,
"recycledContentPct": 123,
"contactSensitive": True,
"foodContact": True,
"heavyMetalsMgPerKg": 123,
"heavyMetalsMethod": "<string>",
"pfasCompliant": True,
"pfasMethod": "<string>",
"pfasSumPpb": 123,
"pfasTotalFluorineMgPerKg": 123,
"notes": "<string>"
}
]
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify([
{
code: '<string>',
name: '<string>',
materials: [],
supplierName: '<string>',
massGrams: 123,
recycledContentPct: 123,
contactSensitive: true,
foodContact: true,
heavyMetalsMgPerKg: 123,
heavyMetalsMethod: '<string>',
pfasCompliant: true,
pfasMethod: '<string>',
pfasSumPpb: 123,
pfasTotalFluorineMgPerKg: 123,
notes: '<string>'
}
])
};
fetch('https://app.polygon-one.com/api/ppwr/components', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.polygon-one.com/api/ppwr/components",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
[
'code' => '<string>',
'name' => '<string>',
'materials' => [
],
'supplierName' => '<string>',
'massGrams' => 123,
'recycledContentPct' => 123,
'contactSensitive' => true,
'foodContact' => true,
'heavyMetalsMgPerKg' => 123,
'heavyMetalsMethod' => '<string>',
'pfasCompliant' => true,
'pfasMethod' => '<string>',
'pfasSumPpb' => 123,
'pfasTotalFluorineMgPerKg' => 123,
'notes' => '<string>'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.polygon-one.com/api/ppwr/components"
payload := strings.NewReader("[\n {\n \"code\": \"<string>\",\n \"name\": \"<string>\",\n \"materials\": [],\n \"supplierName\": \"<string>\",\n \"massGrams\": 123,\n \"recycledContentPct\": 123,\n \"contactSensitive\": true,\n \"foodContact\": true,\n \"heavyMetalsMgPerKg\": 123,\n \"heavyMetalsMethod\": \"<string>\",\n \"pfasCompliant\": true,\n \"pfasMethod\": \"<string>\",\n \"pfasSumPpb\": 123,\n \"pfasTotalFluorineMgPerKg\": 123,\n \"notes\": \"<string>\"\n }\n]")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.polygon-one.com/api/ppwr/components")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("[\n {\n \"code\": \"<string>\",\n \"name\": \"<string>\",\n \"materials\": [],\n \"supplierName\": \"<string>\",\n \"massGrams\": 123,\n \"recycledContentPct\": 123,\n \"contactSensitive\": true,\n \"foodContact\": true,\n \"heavyMetalsMgPerKg\": 123,\n \"heavyMetalsMethod\": \"<string>\",\n \"pfasCompliant\": true,\n \"pfasMethod\": \"<string>\",\n \"pfasSumPpb\": 123,\n \"pfasTotalFluorineMgPerKg\": 123,\n \"notes\": \"<string>\"\n }\n]")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.polygon-one.com/api/ppwr/components")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "[\n {\n \"code\": \"<string>\",\n \"name\": \"<string>\",\n \"materials\": [],\n \"supplierName\": \"<string>\",\n \"massGrams\": 123,\n \"recycledContentPct\": 123,\n \"contactSensitive\": true,\n \"foodContact\": true,\n \"heavyMetalsMgPerKg\": 123,\n \"heavyMetalsMethod\": \"<string>\",\n \"pfasCompliant\": true,\n \"pfasMethod\": \"<string>\",\n \"pfasSumPpb\": 123,\n \"pfasTotalFluorineMgPerKg\": 123,\n \"notes\": \"<string>\"\n }\n]"
response = http.request(request)
puts response.read_body{
"imported": 123,
"updated": [
"<string>"
],
"skipped": [
"<string>"
],
"failed": [
{
"identifier": "<string>",
"error": "<string>"
}
],
"invalid": [
{
"index": 123,
"identifier": "<string>",
"error": "<string>"
}
],
"sizesCreated": 123,
"articleNumbersUnmatched": 123,
"articleLinksCreated": 123,
"componentLinksCreated": 123,
"componentStubsCreated": 123
}{
"error": "<string>",
"details": "<unknown>"
}{
"error": "<string>",
"details": "<unknown>"
}{
"error": "<string>",
"details": "<unknown>"
}{
"error": "overwriteRequired",
"confirmUpdateCount": 123
}{
"error": "<string>",
"details": "<unknown>"
}Komponenten anlegen & aktualisieren
Bulk upsert of packaging components — the same engine, row schema, validation and idempotency as the in-app CSV/Excel importer. Upsert key: code plus the resolved supplier. Fill-only by default: an empty cell never clears a stored value, a cell that fills an empty field is applied automatically, and a cell that would overwrite a non-empty (including supplier- or AI-attested) value needs overwrite: true — without it the whole batch is refused with 409 and nothing is written. Provenance and evidence columns are never writable through this API. At most 1000 rows per request (400 otherwise). Requires packaging:write; customer workspace with the PPWR module enabled — see the guide.
curl --request POST \
--url https://app.polygon-one.com/api/ppwr/components \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
[
{
"code": "<string>",
"name": "<string>",
"materials": [],
"supplierName": "<string>",
"massGrams": 123,
"recycledContentPct": 123,
"contactSensitive": true,
"foodContact": true,
"heavyMetalsMgPerKg": 123,
"heavyMetalsMethod": "<string>",
"pfasCompliant": true,
"pfasMethod": "<string>",
"pfasSumPpb": 123,
"pfasTotalFluorineMgPerKg": 123,
"notes": "<string>"
}
]
'import requests
url = "https://app.polygon-one.com/api/ppwr/components"
payload = [
{
"code": "<string>",
"name": "<string>",
"materials": [],
"supplierName": "<string>",
"massGrams": 123,
"recycledContentPct": 123,
"contactSensitive": True,
"foodContact": True,
"heavyMetalsMgPerKg": 123,
"heavyMetalsMethod": "<string>",
"pfasCompliant": True,
"pfasMethod": "<string>",
"pfasSumPpb": 123,
"pfasTotalFluorineMgPerKg": 123,
"notes": "<string>"
}
]
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify([
{
code: '<string>',
name: '<string>',
materials: [],
supplierName: '<string>',
massGrams: 123,
recycledContentPct: 123,
contactSensitive: true,
foodContact: true,
heavyMetalsMgPerKg: 123,
heavyMetalsMethod: '<string>',
pfasCompliant: true,
pfasMethod: '<string>',
pfasSumPpb: 123,
pfasTotalFluorineMgPerKg: 123,
notes: '<string>'
}
])
};
fetch('https://app.polygon-one.com/api/ppwr/components', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.polygon-one.com/api/ppwr/components",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
[
'code' => '<string>',
'name' => '<string>',
'materials' => [
],
'supplierName' => '<string>',
'massGrams' => 123,
'recycledContentPct' => 123,
'contactSensitive' => true,
'foodContact' => true,
'heavyMetalsMgPerKg' => 123,
'heavyMetalsMethod' => '<string>',
'pfasCompliant' => true,
'pfasMethod' => '<string>',
'pfasSumPpb' => 123,
'pfasTotalFluorineMgPerKg' => 123,
'notes' => '<string>'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.polygon-one.com/api/ppwr/components"
payload := strings.NewReader("[\n {\n \"code\": \"<string>\",\n \"name\": \"<string>\",\n \"materials\": [],\n \"supplierName\": \"<string>\",\n \"massGrams\": 123,\n \"recycledContentPct\": 123,\n \"contactSensitive\": true,\n \"foodContact\": true,\n \"heavyMetalsMgPerKg\": 123,\n \"heavyMetalsMethod\": \"<string>\",\n \"pfasCompliant\": true,\n \"pfasMethod\": \"<string>\",\n \"pfasSumPpb\": 123,\n \"pfasTotalFluorineMgPerKg\": 123,\n \"notes\": \"<string>\"\n }\n]")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.polygon-one.com/api/ppwr/components")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("[\n {\n \"code\": \"<string>\",\n \"name\": \"<string>\",\n \"materials\": [],\n \"supplierName\": \"<string>\",\n \"massGrams\": 123,\n \"recycledContentPct\": 123,\n \"contactSensitive\": true,\n \"foodContact\": true,\n \"heavyMetalsMgPerKg\": 123,\n \"heavyMetalsMethod\": \"<string>\",\n \"pfasCompliant\": true,\n \"pfasMethod\": \"<string>\",\n \"pfasSumPpb\": 123,\n \"pfasTotalFluorineMgPerKg\": 123,\n \"notes\": \"<string>\"\n }\n]")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.polygon-one.com/api/ppwr/components")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "[\n {\n \"code\": \"<string>\",\n \"name\": \"<string>\",\n \"materials\": [],\n \"supplierName\": \"<string>\",\n \"massGrams\": 123,\n \"recycledContentPct\": 123,\n \"contactSensitive\": true,\n \"foodContact\": true,\n \"heavyMetalsMgPerKg\": 123,\n \"heavyMetalsMethod\": \"<string>\",\n \"pfasCompliant\": true,\n \"pfasMethod\": \"<string>\",\n \"pfasSumPpb\": 123,\n \"pfasTotalFluorineMgPerKg\": 123,\n \"notes\": \"<string>\"\n }\n]"
response = http.request(request)
puts response.read_body{
"imported": 123,
"updated": [
"<string>"
],
"skipped": [
"<string>"
],
"failed": [
{
"identifier": "<string>",
"error": "<string>"
}
],
"invalid": [
{
"index": 123,
"identifier": "<string>",
"error": "<string>"
}
],
"sizesCreated": 123,
"articleNumbersUnmatched": 123,
"articleLinksCreated": 123,
"componentLinksCreated": 123,
"componentStubsCreated": 123
}{
"error": "<string>",
"details": "<unknown>"
}{
"error": "<string>",
"details": "<unknown>"
}{
"error": "<string>",
"details": "<unknown>"
}{
"error": "overwriteRequired",
"confirmUpdateCount": 123
}{
"error": "<string>",
"details": "<unknown>"
}Autorisierungen
API key generated in Settings > API Keys. Include as Authorization: Bearer <key>.
Body
- object[]
- object
One packaging-component row for POST /api/ppwr/components. Upsert key: code + resolved supplier. Cells are coerced exactly as the in-app CSV/Excel importer coerces them, so a JSON-native value and its string spelling are equivalent (12 = "12", ["plastic"] = "plastic;paper_board", true = "yes"/"ja"/"1"). An empty or omitted field is a no-op — it never clears a stored value. Cross-field rules (each rejects the row with the named key): a recycledContentPct above 0 requires recycledContentSource and recycledContentMethod (recycledProvenanceRequired); foodContact requires contactSensitive (foodContactImpliesContactSensitive); the five PFAS fields are only accepted on a food-contact component (pfasOnlyFoodContact).
Your component code — the upsert key, compared byte-exact after trimming (KAR-01 ≠ kar-01).
120Component name.
At least one material. Also accepts a ;- or ,-delimited string.
1plastic, paper_board, glass, metal, wood, composite, other Resolved to one of your suppliers by fuzzy name match (case, umlauts, punctuation and legal-form tokens are ignored). A name that matches two or more distinct suppliers fails the row with ambiguousSupplierName — the import never guesses. A name that matches none (and a blank cell) resolves to no supplier: the record lands in your own bucket without a row error, and the supplier is never auto-created.
Polymer type (plastics only).
pet, hdpe, ldpe, pp, ps, pvc, other, null Mass of ONE piece of this component, in grams.
Recycled content, 0–100.
pcr = post-consumer, pir = post-industrial.
pcr, pir, null physical, mass_balance, null Contact-sensitive packaging. Blank = false.
Food-contact packaging. Blank = false. Implies contactSensitive.
Sum of the four regulated heavy metals, mg/kg.
Provenance of the heavy-metals claim.
test_report, supplier_declaration, not_substantiated, null Annex VII d) measurement standard (free text, e.g. CEN/CR 13695-1). Accepted on every component.
Tri-state: blank = unknown. Food-contact components only.
Provenance of the PFAS claim. Food-contact components only.
test_report, supplier_declaration, not_substantiated, null PFAS measurement method (free text). Food-contact components only.
Measured PFAS sum in ppb. Food-contact components only.
Total fluorine in mg/kg — the screening route. Food-contact components only.
Free-text notes.
Antwort
Intake result (partial accept — see the schema).
Result of a synchronous PPWR intake. Partial accept: valid rows commit even when other rows are invalid or failed — the one exception is the 409 overwrite gate, which is all-or-nothing. Idempotent: replaying an identical request changes nothing (matched rows return as skipped), except unit rows without internalRef, which are always creates.
ENTITIES created (units, components, links) — never sizes.
Identifiers of rows whose existing match changed. A size created on an existing unit appears as <unit ref> / <size ref>.
Identifiers of rows that matched with nothing to change.
Rows the upsert engine rejected (unknown reference, ambiguous match, …). Its error values are stable machine keys.
Show child attributes
Show child attributes
Rows rejected by schema validation before the engine ran. Most error values are stable machine keys, but a field with no custom message falls back to the validator's own English text — match on the key, fall back to displaying the string.
Show child attributes
Show child attributes
Unit intake only: sizes created for the rows' units.
Unit intake only: distinct articleNumbers no live article matched. Skipped, never a row error.
Unit intake only: unit⇄article links created from articleNumbers.
Unit intake only: unit⇄component links created from componentCodes.
Unit intake only: minimal placeholder components created for unknown componentCodes.