> ## Documentation Index
> Fetch the complete documentation index at: https://help.polygon-one.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Verpackungseinheiten anlegen & aktualisieren

> Bulk upsert of packaging units, keyed on `internalRef`. A row may carry its additional sizes (`sizes[]`) and the additive `articleNumbers` / `componentCodes` directives in the same call. **A row without `internalRef` is always a create** — the server assigns the next `PU-<n>`, and there is currently no read endpoint for those generated references: read them back from the **Packaging units** list or its export before importing links or article links. Same fill-only column policy and `overwrite` gate as the component intake. At most 1000 rows per request (`400` otherwise). A request may additionally carry at most 1000 `sizes` entries across all its rows. Requires `packaging:write`; customer workspace with the PPWR module enabled — see the guide.



## OpenAPI

````yaml POST /api/ppwr/units
openapi: 3.1.0
info:
  title: Polygon One Compliance API
  description: >-
    API for integrating ERP systems with the Polygon One EUDR Compliance
    Platform. Allows management of Articles, Suppliers, Orders, and Due
    Diligence Statements (DDS). PPWR packaging master data, the SKU quantity
    feed and Mengenmeldung reports are covered by the PPWR groups.
  version: 1.0.0
servers:
  - url: https://app.polygon-one.com
    description: Production Server
security:
  - bearerAuth: []
paths:
  /api/ppwr/units:
    post:
      summary: Upsert Packaging Units
      description: >-
        Bulk upsert of packaging units, keyed on `internalRef`. A row may carry
        its additional sizes (`sizes[]`) and the additive `articleNumbers` /
        `componentCodes` directives in the same call. **A row without
        `internalRef` is always a create** — the server assigns the next
        `PU-<n>`, and there is currently no read endpoint for those generated
        references: read them back from the **Packaging units** list or its
        export before importing links or article links. Same fill-only column
        policy and `overwrite` gate as the component intake. At most 1000 rows
        per request (`400` otherwise). A request may additionally carry at most
        1000 `sizes` entries across all its rows. Requires `packaging:write`;
        customer workspace with the PPWR module enabled — see the guide.
      operationId: upsertPpwrUnits
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - type: array
                  items:
                    $ref: '#/components/schemas/PpwrUnitRow'
                  maxItems: 1000
                  description: Bare array form.
                - $ref: '#/components/schemas/PpwrUnitIntakeInput'
      responses:
        '200':
          $ref: '#/components/responses/PpwrIntakeResult'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/PpwrOverwriteRequired'
        '500':
          $ref: '#/components/responses/InternalError'
components:
  schemas:
    PpwrUnitRow:
      type: object
      description: >-
        One packaging-unit row for `POST /api/ppwr/units`. Upsert key:
        `internalRef`. **A row without `internalRef` can match nothing and is
        therefore ALWAYS a create** — the server names the unit with the next
        generated `PU-<n>`; replaying such a row creates another unit. 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.
      required:
        - name
        - packagingType
        - role
        - notes
      properties:
        name:
          type: string
          description: Name of the packaging unit.
        internalRef:
          type: string
          maxLength: 120
          description: >-
            Your reference for the unit — the upsert key, unique within your
            company. Omit it only for rows you never intend to replay (see the
            schema description).
        packagingType:
          type: string
          enum:
            - sales
            - grouped
            - transport
            - ecommerce
            - service
        role:
          type: string
          enum:
            - manufacturer
            - importer
            - distributor
            - authorised_representative
            - supplier
            - fulfilment_service_provider
        supplierName:
          type:
            - string
            - 'null'
          description: >-
            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.
        format:
          type:
            - string
            - 'null'
          description: Packaging form (free text).
        recyclabilityGrade:
          type:
            - string
            - 'null'
          enum:
            - A
            - B
            - C
            - non_recyclable
            - null
          description: A supplied grade is stored as supplier-declared.
        gtin:
          type:
            - string
            - 'null'
          pattern: ^(\d{8}|\d{12}|\d{13}|\d{14})$
          description: GTIN of the unit's PRIMARY size.
        widthMm:
          type:
            - number
            - string
          description: Width of the primary size, in mm.
        heightMm:
          type:
            - number
            - string
          description: Height of the primary size, in mm.
        depthMm:
          type:
            - number
            - string
          description: Depth of the primary size, in mm.
        sizeReference:
          type: string
          maxLength: 120
          description: >-
            The flat spelling of a size: several rows sharing one `internalRef`,
            each with its own `sizeReference`, fold into ONE unit with N sizes.
            Blank (or equal to `internalRef`) marks the UNIT row. Prefer
            `sizes[]`.
        sizeLabel:
          type:
            - string
            - 'null'
          maxLength: 120
          description: >-
            Human name of the size. Only a SIZE row may carry it — on a unit row
            it fails as `conflictingSizeRow`.
        marketMemberStates:
          type:
            - array
            - string
          items:
            type: string
            pattern: ^[A-Za-z]{2}$
          description: >-
            ISO alpha-2 codes (case-insensitive, stored upper-case). A blank
            column imports as an empty list and is NOT back-filled from your
            PPWR default.
        notes:
          type: string
          minLength: 1
          description: >-
            **Required** — description and intended use of the packaging. A row
            without it fails as `descriptionRequired`, including fill-only
            updates.
        placedUnderOwnBrand:
          type:
            - boolean
            - string
          description: >-
            Blank = `false`; on an update a blank/absent value leaves the stored
            value unchanged.
        modifiesConformity:
          type:
            - boolean
            - string
          description: >-
            Blank = `false`; on an update a blank/absent value leaves the stored
            value unchanged.
        reusable:
          type:
            - boolean
            - string
          description: >-
            Art. 11 reusable packaging. Blank = `false`; on an update a
            blank/absent value leaves the stored value unchanged.
        articleNumbers:
          type:
            - array
            - string
          items:
            type: string
          description: >-
            Links the unit to existing articles by internal article number. On a
            SIZE row the link is to that size. **An unmatched number is skipped
            silently** (counted in `articleNumbersUnmatched`), never created and
            never a row error — unlike `POST /api/ppwr/article-links`, which
            errors.
        componentCodes:
          type:
            - array
            - string
          items:
            type: string
          description: >-
            Builds the bill of materials with quantity 1. An unknown code
            auto-creates a minimal stub component (`other`, name = code, counted
            in `componentStubsCreated`). A code naming **two or more** live
            components fails the whole row with `ambiguousComponentMatch` — the
            cell has no supplier column to disambiguate with, and the oldest
            match is never picked. `POST /api/ppwr/links` reports the same key,
            so it is not an escape hatch: make the code unique instead.
        sizes:
          type: array
          items:
            $ref: '#/components/schemas/PpwrSizeInput'
          maxItems: 49
          description: >-
            The unit's ADDITIONAL sizes — the primary way to send sizes. On a
            CREATE the row is one transaction: a refused size
            (`duplicateFormatReference`, `formatCapReached`) rolls the unit back
            too. On a row matching an EXISTING unit each size is upserted on its
            own; sizes the payload omits are never deleted.
    PpwrUnitIntakeInput:
      type: object
      description: >-
        Envelope form for the unit intake. At most 1000 `sizes` entries across
        all rows.
      required:
        - rows
      properties:
        rows:
          type: array
          items:
            $ref: '#/components/schemas/PpwrUnitRow'
          maxItems: 1000
        overwrite:
          type: boolean
          default: false
          description: >-
            Authorises overwriting values that are already filled (including
            supplier- and AI-attested ones). Without it a batch containing any
            such change is refused with `409 overwriteRequired` and **nothing**
            is written.
    PpwrSizeInput:
      type: object
      description: >-
        One additional size of a packaging unit, sent inside a unit row's
        `sizes` array. The unit row's own `gtin` and dimensions are its FIRST
        (primary) size, so at most 49 further sizes fit here.
      required:
        - reference
      properties:
        reference:
          type: string
          minLength: 1
          maxLength: 120
          description: The size's own identifier, unique within your company.
        gtin:
          type:
            - string
            - 'null'
          pattern: ^(\d{8}|\d{12}|\d{13}|\d{14})$
          description: GTIN-8/12/13/14.
        label:
          type:
            - string
            - 'null'
          maxLength: 120
          description: Human name of the size, e.g. `500 g`.
        widthMm:
          type:
            - number
            - string
          description: Width in mm.
        heightMm:
          type:
            - number
            - string
          description: Height in mm.
        depthMm:
          type:
            - number
            - string
          description: Depth in mm.
    PpwrIntakeResult:
      type: object
      description: >-
        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.
      required:
        - imported
        - updated
        - skipped
        - failed
        - invalid
      properties:
        imported:
          type: integer
          description: ENTITIES created (units, components, links) — never sizes.
        updated:
          type: array
          items:
            type: string
          description: >-
            Identifiers of rows whose existing match changed. A size created on
            an existing unit appears as `<unit ref> / <size ref>`.
        skipped:
          type: array
          items:
            type: string
          description: Identifiers of rows that matched with nothing to change.
        failed:
          type: array
          items:
            $ref: '#/components/schemas/PpwrRowError'
          description: >-
            Rows the upsert engine rejected (unknown reference, ambiguous match,
            …). Its `error` values are stable machine keys.
        invalid:
          type: array
          items:
            $ref: '#/components/schemas/PpwrIndexedRowError'
          description: >-
            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.
        sizesCreated:
          type: integer
          description: 'Unit intake only: sizes created for the rows'' units.'
        articleNumbersUnmatched:
          type: integer
          description: >-
            Unit intake only: distinct `articleNumbers` no live article matched.
            Skipped, never a row error.
        articleLinksCreated:
          type: integer
          description: 'Unit intake only: unit⇄article links created from `articleNumbers`.'
        componentLinksCreated:
          type: integer
          description: >-
            Unit intake only: unit⇄component links created from
            `componentCodes`.
        componentStubsCreated:
          type: integer
          description: >-
            Unit intake only: minimal placeholder components created for unknown
            `componentCodes`.
    ErrorResponse:
      type: object
      properties:
        error:
          type: string
        details:
          description: Additional error details (e.g. validation issues)
      required:
        - error
    PpwrOverwriteRequired:
      type: object
      description: >-
        The batch contains at least one change that would overwrite an
        already-filled value. Nothing was written — resubmit with `overwrite:
        true`, or drop those rows.
      required:
        - error
        - confirmUpdateCount
      properties:
        error:
          type: string
          enum:
            - overwriteRequired
        confirmUpdateCount:
          type: integer
          description: How many rows in the batch need confirmation.
    PpwrRowError:
      type: object
      description: >-
        A row the upsert engine rejected. Carries no `index` — the engine
        reports by identifier.
      properties:
        identifier:
          type: string
        error:
          type: string
          description: 'Stable machine key (clamped: anything unexpected becomes `dbError`).'
    PpwrIndexedRowError:
      type: object
      description: >-
        A row rejected before the upsert engine ran, reported by its position in
        the request.
      properties:
        index:
          type: integer
          description: 0-based position of the row in the request.
        identifier:
          type: string
        error:
          type: string
          description: >-
            Usually a stable machine key; a field with no custom message falls
            back to the validator's own English text.
  responses:
    PpwrIntakeResult:
      description: Intake result (partial accept — see the schema).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/PpwrIntakeResult'
    BadRequest:
      description: Invalid request data
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Unauthorized:
      description: Invalid or missing API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Forbidden:
      description: Insufficient permissions for this resource
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    PpwrOverwriteRequired:
      description: Unconfirmed overwrite — nothing was written.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/PpwrOverwriteRequired'
    InternalError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API Key
      description: >-
        API key generated in Settings > API Keys. Include as `Authorization:
        Bearer <key>`.

````