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

# Create product instance

> **Idempotent create** — a replay of the same `Idempotency-Key` returns the
original response (`200`); otherwise a new product instance is created
(`201`). Your `external_id` must be unique: reusing one returns `409`. See
[External IDs](/external-ids).

`annual_data[].production_output` and `economic_value_per_unit` are
production data: without `edit:processes` they are ignored rather than
rejected, and the instance is created without them.




## OpenAPI

````yaml POST /v2/product-instances
openapi: 3.1.0
info:
  title: Emidat API
  version: '2.0'
  description: >
    The Emidat API v2 lets manufacturers create products and supplier products

    programmatically — designed for ERP and system integrations.


    Authentication uses a simple API key (`X-API-Key` header). Keys are issued

    per manufacturer and managed in the Emidat dashboard.


    Keys follow the format `emidat-{key_id}-{secret}`, where `key_id` is a

    random public handle.


    **Onboarding steps** — performed once before creating products:

    - Plant and production process setup (via Emidat UI)

    - Your material codes → Emidat material types and plant codes → Emidat
    plants are mapped in your integration middleware during setup; at runtime
    you send your own codes and the middleware resolves them

    - Create supplier companies via `POST /v2/supplier-companies` — every
    supplier plant needs one, as `supplier_company_id` is required

    - Create supplier plants via `POST /v2/supplier-plants`, referencing a
    supplier company by its Emidat `id`

    - Create supplier products using your own `external_id`; reference them in a
    product's BOM by their Emidat `id` (from the create response, or resolved
    via `GET …?external_id=`)

    - (Optional) Create silos to merge same-type supplier products from
    different suppliers — reference by their Emidat `id` in a product's BOM


    **Preview endpoints** — the `Product Instances` and `Prechain Products` tags

    are in preview. They are live and supported, but their contract is not
    frozen:

    request and response shapes, field names, and error codes may still change
    in

    ways that break existing integrations, possibly without a deprecation
    period.
servers:
  - url: https://api.emidat.com
    description: Production
security:
  - ApiKeyAuth: []
paths:
  /v2/product-instances:
    post:
      tags:
        - Product Instances
      summary: Create product instance
      description: >
        **Idempotent create** — a replay of the same `Idempotency-Key` returns
        the

        original response (`200`); otherwise a new product instance is created

        (`201`). Your `external_id` must be unique: reusing one returns `409`.
        See

        [External IDs](/external-ids).


        `annual_data[].production_output` and `economic_value_per_unit` are

        production data: without `edit:processes` they are ignored rather than

        rejected, and the instance is created without them.
      operationId: v2_product_instances_create
      parameters:
        - $ref: '#/components/parameters/IdempotencyKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProductInstance'
            example:
              external_id: CEM-42.5R-25KG-MUNICH
              plant_id: 11111111-1111-1111-1111-111111111111
              name: CEM 42.5 R, 25 kg bag (Munich)
              elementary_id: 22222222-2222-2222-2222-222222222222
              production_process_id: 77777777-7777-7777-7777-777777777777
              unit: KG
              produced_from: 2025
              produced_to: null
              tech_specs:
                compressive_strength: 42.5
                density: 3100
              annual_data:
                - year: 2025
                  production_output: 120000
              economic_value_per_unit: 0.35
      responses:
        '200':
          description: Returned unchanged — an `Idempotency-Key` replay.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductInstance'
        '201':
          description: Instance created.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductInstance'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          description: >-
            The `external_id` is already in use by another active entity, or the
            `Idempotency-Key` was reused with a different request body.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Validation error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          $ref: '#/components/responses/InternalServerError'
components:
  parameters:
    IdempotencyKeyHeader:
      name: Idempotency-Key
      in: header
      required: false
      schema:
        type: string
      description: >
        Optional client-generated key (e.g. a UUID) that makes a create

        idempotent — the only mechanism that does so. A replay carrying the same

        key returns the original response unchanged (`200`); reusing a key with
        a

        different request body is rejected with `409`. Keys are scoped to your

        manufacturer and retained for 6h. See [External IDs](/external-ids).
  schemas:
    CreateProductInstance:
      type: object
      required:
        - plant_id
        - name
        - elementary_id
        - unit
        - produced_from
      properties:
        external_id:
          anyOf:
            - type: string
            - type: 'null'
          description: >
            Your own code for this instance (its SKU / BOM node id). Optional.
            When

            provided it is unique among your active instances — creating another

            with the same code returns `409` (see [External
            IDs](/external-ids)).
          example: CEM-42.5R-25KG-MUNICH
        plant_id:
          type: string
          format: uuid
          description: The plant's Emidat UUID.
        name:
          type: string
          example: CEM 42.5 R, 25 kg bag (Munich)
        elementary_id:
          type: string
          format: uuid
          description: Emidat material-type UUID — determines the product type.
        production_process_id:
          anyOf:
            - type: string
              format: uuid
            - type: 'null'
          description: The production process's Emidat UUID. Optional.
        unit:
          type: string
          enum:
            - M
            - M2
            - M3
            - KG
            - T
            - L
            - KWH
            - MJ
            - UNIT
            - METRIC TON*KM
          example: KG
        produced_from:
          type: integer
          minimum: 2020
          maximum: 2100
          description: First year this instance is valid.
          example: 2025
        produced_to:
          anyOf:
            - type: integer
              minimum: 2020
              maximum: 2100
            - type: 'null'
          description: Last valid year, or null if open-ended.
        tech_specs:
          anyOf:
            - type: object
              additionalProperties: true
            - type: 'null'
          description: >
            Technical specifications as key/value pairs. Which keys apply
            depends

            on the instance's `elementary_id` — cement, for example, takes

            `compressive_strength` and `density`.
          example:
            compressive_strength: 42.5
            density: 3100
        annual_data:
          anyOf:
            - type: array
              items:
                type: object
                required:
                  - year
                  - production_output
                properties:
                  year:
                    type: integer
                    minimum: 2021
                    description: >-
                      From 2021 through the current calendar year. The ongoing
                      year is accepted; its data is partial until the year ends.
                    example: 2025
                  production_output:
                    anyOf:
                      - type: number
                        exclusiveMinimum: 0
                      - type: 'null'
                    description: >
                      Total production output for the year, in the product's
                      declared

                      `unit`. `null` declares the year without an output — that
                      year

                      stays out of the LCA until a value is supplied.
                    example: 120000
            - type: 'null'
          description: |
            Production output per year; drives the computable LCA years. Each
            year must fall inside the instance's production period.
        economic_value_per_unit:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          description: |
            Economic value in EUR per declared product unit. Optional allocation
            input; omit it or pass `null` when no economic value is available.
          example: 0.35
    ProductInstance:
      type: object
      required:
        - id
        - external_id
        - plant_id
        - name
        - elementary_id
        - production_process_id
        - unit
        - produced_from
        - produced_to
        - tech_specs
        - annual_data
        - economic_value_per_unit
        - status
        - created_at
      properties:
        id:
          type: string
          format: uuid
        external_id:
          anyOf:
            - type: string
            - type: 'null'
          description: Your own code for this instance, as provided on creation.
        plant_id:
          type: string
          format: uuid
          description: >-
            The plant's Emidat UUID. Always present — set at creation and
            immutable.
        name:
          type: string
        elementary_id:
          type: string
          format: uuid
          description: Emidat material-type UUID — determines the product type.
        production_process_id:
          anyOf:
            - type: string
              format: uuid
            - type: 'null'
          description: The production process's Emidat UUID, or null if none.
        unit:
          type: string
          enum:
            - M
            - M2
            - M3
            - KG
            - T
            - L
            - KWH
            - MJ
            - UNIT
            - METRIC TON*KM
          example: KG
        produced_from:
          type: integer
          description: First year this instance is valid.
          example: 2025
        produced_to:
          anyOf:
            - type: integer
            - type: 'null'
          description: Last valid year, or null if open-ended.
        tech_specs:
          anyOf:
            - type: object
              additionalProperties: true
            - type: 'null'
          description: >
            Technical specifications as key/value pairs. Which keys apply
            depends

            on the instance's `elementary_id` — cement, for example, takes

            `compressive_strength` and `density`.
          example:
            compressive_strength: 42.5
            density: 3100
        annual_data:
          type: array
          description: Production output per year.
          items:
            type: object
            required:
              - year
              - production_output
            properties:
              year:
                type: integer
                minimum: 2021
                example: 2025
              production_output:
                anyOf:
                  - type: number
                    exclusiveMinimum: 0
                  - type: 'null'
                description: >
                  Total production output for the year, in the product's
                  declared

                  `unit`. `null` declares the year without an output — that year

                  stays out of the LCA until a value is supplied. Reads return

                  `null` without `view:processes`, whatever is stored; writes

                  are ignored without `edit:processes`.
                example: 120000
        economic_value_per_unit:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          description: |
            Economic value in EUR per declared product unit, or `null` when it
            has not been provided. Reads return `null` without
            `view:processes`, whatever is stored; writes are ignored without
            `edit:processes`.
          example: 0.35
        status:
          type: string
          enum:
            - incomplete
            - draft
          description: >
            Whether the instance is modelled far enough to run an LCA. Derived
            on

            every read, never set directly.


            `incomplete` — something structural is still missing: `tech_specs`
            is

            empty, `production_process_id` is null, or the material type needs a

            BOM (`PUT …/bom`) and none is set. `draft` — all of that is present.


            `draft` is about structure only. It does not mean any year is

            computable: per-year gaps such as missing production data surface as

            `blocked_by` entries on the LCA results, not here.
          example: draft
        created_at:
          type: string
          format: date-time
    Error:
      type: object
      required:
        - msg
        - err_code
      properties:
        msg:
          type: string
          description: Human-readable explanation, for logs and operators. Do not parse it.
          example: supplier product requires a supplier plant
        err_code:
          type: string
          description: >
            Stable, machine-readable error code — branch on this, never on
            `msg`.

            The set may grow over time; treat an unrecognized code as

            non-retryable and surface it to an operator. The enum lists every

            code the platform can emit; only a subset is reachable per

            endpoint — see [Errors](/errors) for the actionable catalog.


            `LcaRecomputeJob.err_code` reports failures from this same `err_*`

            namespace. `LcaBlocker.err_code` does not — those values are a

            separate, disjoint set.
          example: err_supplier_product_missing_supplier_plant
          enum:
            - err_not_defined
            - err_not_found
            - err_not_valid
            - err_not_authenticated
            - err_permission_denied
            - err_declaration_not_completed
            - err_declaration_not_regenerable
            - err_epd_document_already_exists
            - err_invalid_product_ids
            - err_generate_products_limit
            - err_plant_missing_product_category
            - err_plant_incomplete_address
            - err_plant_incomplete_company_fields
            - err_plant_categories_not_covering_processes
            - err_plant_categories_not_covering_active_products
            - err_plant_invalid_category_id
            - err_plant_custom_electricity_mix_not_allowed
            - err_plant_electricity_mix_mismatch
            - err_plant_negative_electricity_values
            - err_plant_electricity_without_mix
            - err_electricity_plant_production_mismatch
            - err_lca_mass_balance
            - err_lca_allocation_data_incomplete
            - err_lca_production_output_mismatch
            - err_product_mass_larger_than_recipe_mass
            - err_product_category_not_allowed_at_plant
            - err_water_not_scoped_but_wastewater_is_scoped
            - err_more_wastewater_than_water
            - err_project_no_products
            - err_invalid_product_quantity
            - err_duplicate_product
            - err_product_missing_gwp_total
            - err_elementary_deactivated
            - err_product_missing_supplier
            - err_epd_unusable
            - err_epd_service
            - err_lca_update_not_queued
            - err_not_ready_for_epd
            - err_material_not_hazardous
            - err_material_no_limited_lifetime
            - err_invalid_lifetime_years
            - err_invalid_secondary_percentage_range
            - err_secondary_percentage_not_customizable
            - err_moisture_content_not_bio_based
            - err_invalid_moisture_content_range
            - err_declaration_not_exists
            - err_declaration_not_under_review
            - err_no_manufacturer
            - err_wrong_email_domain
            - err_user_exists
            - err_inviter_email_not_found
            - err_composite_used_in_supplier_products
            - err_composite_name_exists
            - err_plant_io_invalid_field
            - err_plant_io_field_not_switchable
            - err_plant_io_value_not_scoped
            - err_field_is_plant_level
            - err_negative_io_value
            - err_plant_io_mixed_allocation_groups
            - err_inies_sworn_statement_exists
            - err_supplier_in_use
            - err_supplier_name_exists
            - err_supplier_plant_in_use
            - err_supplier_plant_name_exists
            - err_supplier_product_in_recipe
            - err_supplier_product_in_silo
            - err_supplier_product_missing_supplier_plant
            - err_silo_in_use
            - err_silo_name_exists
            - err_elementary_unknown_matching
            - err_product_document_not_deletable
            - err_product_has_declarations
            - err_product_instance_in_recipe
            - err_prechain_product_in_recipe
            - err_prechain_product_exists
            - err_ccf_exists
            - err_ccf_not_completed
            - err_external_id_exists
            - err_bulk_upload_invalid_file
            - err_material_type_not_found
            - err_material_type_not_configured
            - err_unit_not_allowed_for_material
            - err_production_process_not_found
            - err_multiple_production_processes
            - err_process_plant_mismatch
            - err_material_process_incompatible
            - err_composite_not_allowed_in_recipe
            - err_elementary_not_allowed_in_recipe
            - err_prechain_circular_reference
            - err_product_type_no_recipe
            - err_product_mass_not_set
            - err_required_cell_empty
            - err_plant_not_found
            - err_plant_no_access
            - err_multiple_plants
            - err_product_already_exists
            - err_multiple_products
            - err_linked_product_type_mismatch
            - err_tech_spec_not_recognized
            - err_recipe_incomplete
            - err_orphan_row
            - err_duplicate_tech_spec
            - err_empty_sheet
            - err_no_products
            - err_tech_spec_invalid_value
            - err_dtp_service
            - err_data_translation_wrong_step
            - err_data_translation_invalid_plant_mapping
            - err_data_translation_unresolved_reference
            - err_data_translation_incomplete
            - err_data_translation_material_not_available
            - err_recycled_content_proof_missing
            - err_ppwr_declaration_exists
            - err_ppwr_declaration_editable_exists
            - err_ppwr_declaration_frozen
            - err_ppwr_doc_number_unavailable
            - err_ppwr_variant_mass_invalid
            - err_ppwr_variant_dimension_invalid
            - err_ppwr_variant_material_repeated
            - err_ppwr_variant_material_type
            - err_ppwr_invalid_field_key
            - err_ppwr_supplier_request_not_packaging_manufacturer
            - err_ppwr_supplier_request_pending
            - err_ppwr_supplier_request_not_pending
            - err_ppwr_field_not_requested
            - err_ppwr_document_limit_reached
            - err_ppwr_generation_not_packaging_manufacturer
            - err_ppwr_declaration_incomplete
            - err_ppwr_invalid_signatory
            - err_ppwr_invitation_send_failed
            - err_ppwr_supplier_link_placeholder_missing
            - err_ppwr_supplier_subject_missing
            - err_ppwr_supplier_email_missing
            - err_ppwr_verification_code_limit
            - err_ppwr_verification_code_send_failed
            - err_ppwr_email_not_verified
            - err_ppwr_attestation_required
            - err_ppwr_document_not_deletable
            - err_proof_document_limit
            - err_proof_size_limit
            - err_transport_route_unavailable
            - err_transport_missing_coordinates
            - err_transport_no_route
            - err_transport_same_address
            - err_reauthentication_required
            - err_two_factor_required
  responses:
    Unauthorized:
      description: >
        No credentials; or the key is malformed, unknown, revoked, or expired.
        The

        response is uniform and does not reveal which check failed.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: >
        A valid key lacked the required permission, or targeted a resource
        outside

        its access.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    InternalServerError:
      description: An unexpected server error occurred. Surface it to an operator.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: |
        Per-manufacturer API key in the `X-API-Key` header, format
        `emidat-{key_id}-{secret}` where `key_id` is a random public handle.
        Issued by an owner in the Emidat
        dashboard; carries its own permission scopes, optional plant
        restrictions, and optional expiry. A revoked, expired, or unknown key
        returns 401. See the Authentication guide for details.

````