{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://zeropact.co/contracts/schemas/product-input.schema.json","title":"Zeropact ProductInput Shared Contract","description":"Shared JSON Schema definitions for customer product input. Two entry profiles exist: product-input-flexible.schema.json (minimum viable declaration) and product-input-strict.schema.json (full lifecycle inventory for calculation). The FLEXIBLE profile is the product's default customer lane -- five core fields, no lifecycle knowledge required, gaps filled by enrichment/inference; STRICT is an opt-in contract for complete, calculation-ready payloads (API-to-API integrations, mature customers), and its line-item requirements (including transport mode-or-medium) only bind payloads that claim that completeness. Product identity fields such as product_id and product_version are assigned at ingest and must not appear in customer input. Methodology tracking is intentionally deferred from this input contract.","$ref":"#/$defs/InputProductFlexible","$defs":{"UseStageArchetype":{"description":"Target vocabulary. Membership does not imply an active reference model; see use_stage/data-model.md §4.10.","type":"string","enum":["washable_textile","non_washable_wearable","plug_in_continuous_appliance","plug_in_cycle_appliance","battery_powered_portable","fuel_consuming_device","water_consuming_fixture","cosmetic_consumable","durable_static","single_use_consumable"]},"UseStageCompositionArchetype":{"description":"Named for materials only. Never repeats the archetype or the product type.","type":"string","enum":["cotton","cotton_polyester_blend","synthetic_textile","leather","synthetic_leather","generic"]},"DurationUnit":{"type":"string","enum":["day","week","month","year"]},"LifetimeBasis":{"type":"string","enum":["lifetime_uses","lifetime_cycles","service_life_years","lifetime_distance"]},"WasteTreatmentType":{"description":"Terminal fates only. The non-terminal diagnostic values (OPEN_BURNING, MBT, RDF_PRODUCTION, UNACCOUNTED) are deliberately absent: they never enter a partition. See eol/waste-treatment-types.md §1.2.","type":"string","enum":["RECYCLING_MATERIAL","RECYCLING_COMPOST","PREP_REUSE","ENERGY_RECOVERY_INCIN","INCINERATION_NO_RECOVERY","LANDFILL_SANITARY","LANDFILL_UNSANITARY","OPEN_DUMP","LANDFILL_UNSPECIFIED","UNCOLLECTED","OTHER_DISPOSAL"]},"MaterialRecyclability":{"description":"Mutually exclusive. `compostable` is not `organic`: an organic-certified textile is `recyclable`.","type":"string","enum":["compostable","recyclable","not_recyclable"]},"Share":{"description":"A fraction of one. The field is named `share` everywhere; `fraction` is not an alias.","type":"number","minimum":0,"maximum":1},"NonNegativeNumber":{"type":"number","minimum":0},"Quantity":{"description":"Every reference-pack value is {value, unit}, never a bare number. A unit mismatch against the declared parameter unit is a load error, not a runtime coercion.","type":"object","additionalProperties":false,"required":["value","unit"],"properties":{"value":{"type":"number"},"unit":{"type":"string","minLength":1}}},"EvidenceProof":{"description":"A reference to an approved evidence object, never a free-floating URL.","type":"object","additionalProperties":false,"required":["document_name","source_reference"],"properties":{"document_name":{"type":"string","minLength":1},"source_reference":{"type":"string","minLength":1}}},"InputMultiplier":{"description":"`multiplier: null` means not provided. It does not mean zero and does not override a reference default. Omitting the whole object resolves the same way but records no object-level provenance.","type":"object","additionalProperties":false,"required":["multiplier"],"properties":{"multiplier":{"type":["number","null"],"exclusiveMinimum":0},"proof":{"anyOf":[{"$ref":"#/$defs/EvidenceProof"},{"type":"null"}]}},"allOf":[{"description":"A proof may not be supplied without a non-null multiplier.","if":{"required":["proof"],"properties":{"proof":{"type":"object"}}},"then":{"properties":{"multiplier":{"type":"number"}}}}]},"InputLifetime":{"type":"object","additionalProperties":false,"required":["duration","unit"],"properties":{"duration":{"type":"number","exclusiveMinimum":0,"default":1},"unit":{"$ref":"#/$defs/DurationUnit"}}},"UsesPerDurationQuantity":{"description":"Typed usage frequency. The unit is part of the value, so 3/year cannot be confused with 3/month. An active usage model must resolve a strictly positive value.","type":"object","additionalProperties":false,"required":["value","unit"],"properties":{"value":{"$ref":"#/$defs/NonNegativeNumber"},"unit":{"$ref":"#/$defs/DurationUnit"}}},"InputCircularity":{"description":"Product-level circularity characteristics. Applies in both use-stage input modes: the Specific DoS is the per-use denominator regardless of whether the customer supplied mode-A parameters or mode-B inventories. `total_uses` is optional when duration x uses_per_duration_unit resolves; when supplied it is authoritative and the derived value is kept only as a diagnostic.","type":"object","additionalProperties":false,"properties":{"uses_per_duration_unit":{"anyOf":[{"$ref":"#/$defs/UsesPerDurationQuantity"},{"type":"null"}]},"lifetime":{"anyOf":[{"$ref":"#/$defs/InputLifetime"},{"type":"null"}]},"total_uses":{"type":["number","null"],"exclusiveMinimum":0},"durability":{"anyOf":[{"$ref":"#/$defs/InputMultiplier"},{"type":"null"}]},"repairability":{"anyOf":[{"$ref":"#/$defs/InputMultiplier"},{"type":"null"}]}}},"WashableTextileOverrides":{"description":"Mode-A overrides for `washable_textile`. All optional. Every key here is consumed by the archetype formula (use_stage/data-model.md §12.3); `air_dry_share` is the one parameter with no inventory term, because air drying carries no burden.","type":"object","additionalProperties":false,"required":["archetype"],"properties":{"archetype":{"const":"washable_textile"},"uses_before_washing":{"type":["number","null"],"exclusiveMinimum":0},"machine_wash_share":{"$ref":"#/$defs/NullableShare"},"hand_wash_share":{"$ref":"#/$defs/NullableShare"},"dry_clean_share":{"$ref":"#/$defs/NullableShare"},"tumble_dry_share":{"$ref":"#/$defs/NullableShare"},"air_dry_share":{"$ref":"#/$defs/NullableShare"},"ironing_share":{"$ref":"#/$defs/NullableShare"},"soap_per_wash":{"$ref":"#/$defs/NullableNonNegative"},"water_per_wash":{"$ref":"#/$defs/NullableNonNegative"},"elec_per_wash":{"$ref":"#/$defs/NullableNonNegative"},"soap_per_hand_wash":{"$ref":"#/$defs/NullableNonNegative"},"water_per_hand_wash":{"$ref":"#/$defs/NullableNonNegative"},"elec_per_hand_wash":{"$ref":"#/$defs/NullableNonNegative"},"service_per_dry_clean":{"$ref":"#/$defs/NullableNonNegative"},"elec_per_dry":{"$ref":"#/$defs/NullableNonNegative"},"elec_per_ironing":{"$ref":"#/$defs/NullableNonNegative"},"number_of_washes":{"$ref":"#/$defs/NullableNonNegative"},"number_of_machine_washes":{"$ref":"#/$defs/NullableNonNegative"},"number_of_hand_washes":{"$ref":"#/$defs/NullableNonNegative"},"number_of_dry_cleans":{"$ref":"#/$defs/NullableNonNegative"},"number_of_dries":{"$ref":"#/$defs/NullableNonNegative"},"number_of_ironings":{"$ref":"#/$defs/NullableNonNegative"}}},"PlugInCycleApplianceOverrides":{"type":"object","additionalProperties":false,"required":["archetype"],"properties":{"archetype":{"const":"plug_in_cycle_appliance"},"cycles_per_duration_unit":{"$ref":"#/$defs/NullableNonNegative"},"energy_per_cycle":{"$ref":"#/$defs/NullableNonNegative"},"number_of_cycles":{"$ref":"#/$defs/NullableNonNegative"}}},"NullableShare":{"description":"A share, or null. `null` means the parameter does not apply to this scope; `0` means it applies and is zero. The two are NOT interchangeable, and neither is ever renormalized.","anyOf":[{"$ref":"#/$defs/Share"},{"type":"null"}]},"NullableNonNegative":{"description":"A non-negative number, or null. `null` means not supplied and resolves from defaults; `0` means a declared zero and suppresses the activity.","anyOf":[{"$ref":"#/$defs/NonNegativeNumber"},{"type":"null"}]},"UseStageParamOverrides":{"description":"Discriminated on `archetype`. One member per UseStageArchetype; only the archetypes with an active reference model are listed here.","oneOf":[{"$ref":"#/$defs/WashableTextileOverrides"},{"$ref":"#/$defs/PlugInCycleApplianceOverrides"}]},"InputUseStageInventoryLine":{"type":"object","additionalProperties":false,"required":["input","amount","unit"],"properties":{"input":{"description":"Conceptual descriptor, never a Zeropact process ID. Customer scenarios go through the normal mapping path.","type":"string","minLength":1},"amount":{"$ref":"#/$defs/NonNegativeNumber"},"unit":{"type":"string","minLength":1},"process_hint":{"type":["string","null"]}}},"InputUseStageGeographyInventory":{"type":"object","additionalProperties":false,"required":["country","inventory_basis","inventories"],"properties":{"country":{"$ref":"#/$defs/CustomerCountry"},"inventory_basis":{"description":"LifecycleQuantityBasis on the wire: true use or product lifetime. specific_use is a calc reporting cut after the DoS factor and is rejected on input.","type":"string","enum":["use","product_lifetime"]},"inventories":{"type":"array","minItems":1,"items":{"$ref":"#/$defs/InputUseStageInventoryLine"}}}},"InputUseStage":{"description":"Modes A and B are mutually exclusive. Supplying both is a validation error, not a merge: mode B replaces the formula, so a parameter override against a formula that no longer runs would silently do nothing.","type":"object","additionalProperties":false,"properties":{"parameters":{"anyOf":[{"$ref":"#/$defs/UseStageParamOverrides"},{"type":"null"}]},"inventories":{"anyOf":[{"type":"array","minItems":1,"items":{"$ref":"#/$defs/InputUseStageGeographyInventory"}},{"type":"null"}]}},"not":{"description":"Rejects mode A and mode B supplied together.","required":["parameters","inventories"],"properties":{"parameters":{"type":"object"},"inventories":{"type":"array"}}}},"CustomerCountry":{"description":"Customer-supplied country descriptor (ISO code, English name, or curated alias). Ingest must resolve it to an ISO country via normalize_country_code / resolve_country_identity; unresolved values (including process scopes and regions) are validation errors. Regions may be accepted later once reference aggregations exist. Resolved geography identity is not part of the input contract.","type":"string","minLength":2},"InputMarket":{"description":"One customer-declared market country. Country-level only in v1 (no in-country location). No resolved identity and no source field — declaration provenance is assigned later by enrichment, same track as materials / manufacturing.","type":"object","additionalProperties":false,"required":["country","share"],"properties":{"country":{"$ref":"#/$defs/CustomerCountry"},"share":{"$ref":"#/$defs/Share"}}},"SystemBoundary":{"type":"string","enum":["cradle-to-gate","cradle-to-grave","cradle-to-cradle"]},"InputMarketReference":{"description":"Customer-supplied market identity reused by EoL treatment shares. It deliberately carries no market-mix share and no resolved geography ID. Country-level only in v1.","type":"object","additionalProperties":false,"required":["country"],"properties":{"country":{"$ref":"#/$defs/CustomerCountry"}}},"InputEoLTreatmentShare":{"type":"object","additionalProperties":false,"required":["market","waste_treatment_type","share"],"properties":{"market":{"$ref":"#/$defs/InputMarketReference"},"waste_treatment_type":{"$ref":"#/$defs/WasteTreatmentType"},"share":{"$ref":"#/$defs/Share"}}},"InputEndOfLife":{"description":"Customer-declared EoL treatment distribution. RECYCLING_MATERIAL is the product recycling share (R2); all other terminal treatment shares form the remaining distribution according to the EoL methodology.","type":"object","additionalProperties":false,"required":["treatment_shares"],"properties":{"treatment_shares":{"type":"array","minItems":1,"items":{"$ref":"#/$defs/InputEoLTreatmentShare"}}},"x-ingest-validation":{"phase":"after_market_mix_resolution","rules":["Shares grouped by market must sum to 1.0.","The (market, waste_treatment_type) pair must be unique.","When market_mix is supplied, EoL markets must exactly match its resolved markets.","Shares must be non-negative.","Only terminal WasteTreatmentType values are allowed.","RECYCLING_MATERIAL is the R2 share; no separate R2 field exists."]}},"PhysicalProperties":{"description":"Customer-declared physical properties. Units are free-form strings (same pattern as materials.unit), resolved to id+name refs on the output contract.","type":"object","additionalProperties":false,"properties":{"weight":{"type":["number","null"],"minimum":0},"weight_unit":{"type":["string","null"]},"volume":{"type":["number","null"],"minimum":0},"volume_unit":{"type":["string","null"]},"density":{"type":["number","null"],"minimum":0},"density_unit":{"type":["string","null"]}}},"InputMaterialLoose":{"description":"Customer material or packaging row (flexible profile). No required fields at line-item level.","type":"object","additionalProperties":false,"properties":{"name":{"type":["string","null"]},"description":{"type":["string","null"]},"unit":{"type":["string","null"]},"geography":{"type":["string","null"]},"geography_code":{"type":["string","null"]},"geography_name":{"type":["string","null"]},"amount":{"type":["number","null"],"minimum":0},"percentage":{"$ref":"#/$defs/NullableShare"},"supplier":{"type":["string","null"]},"manufacturer":{"type":["string","null"]},"organic":{"type":["boolean","null"]},"recycled":{"type":["boolean","null"]},"certification":{"type":["string","null"],"enum":[null,"sustainable forest management","conventional forest management","BCI"]}}},"InputMaterialStrict":{"description":"Material row (strict profile): name plus either (amount and unit) or (percentage with product physical_properties.weight).","allOf":[{"$ref":"#/$defs/InputMaterialLoose"},{"required":["name"],"properties":{"name":{"type":"string","minLength":1}},"oneOf":[{"required":["amount","unit"],"properties":{"amount":{"$ref":"#/$defs/NonNegativeNumber"},"unit":{"type":"string","minLength":1}}},{"required":["percentage"],"properties":{"percentage":{"$ref":"#/$defs/Share"}}}]}]},"InputPackagingStrict":{"description":"Packaging row (strict profile): name, amount, and unit are all required.","allOf":[{"$ref":"#/$defs/InputMaterialLoose"},{"required":["name","amount","unit"],"properties":{"name":{"type":"string","minLength":1},"amount":{"$ref":"#/$defs/NonNegativeNumber"},"unit":{"type":"string","minLength":1}}}]},"InputTransportLoose":{"description":"Customer transport row (flexible profile). No required fields at line-item level. Transport geography IS origin/destination, so geography_code/geography_name are deliberately absent; `geography` remains only as an optional mapping hint. Evidence is unit-ful: `weight`+`weight_unit` and `distance`+`distance_unit`, with kg/km defaults when the unit is omitted (a unit without its value is rejected). Canonical conversion of non-default units (lbs, mi, ...) lands with the transport-evidence unit-normalization rule on the field-rule execution lane (#566); until then a non-canonical unit is rejected by the strict profile and treated as unusable evidence by the flexible profile -- never silently misconverted.","type":"object","additionalProperties":false,"properties":{"name":{"type":["string","null"]},"description":{"type":["string","null"]},"unit":{"type":["string","null"]},"geography":{"type":["string","null"]},"amount":{"type":["number","null"],"minimum":0},"origin":{"type":["string","null"]},"destination":{"type":["string","null"]},"transport_mode":{"type":["string","null"],"enum":[null,"very_heavy_truck","heavy_truck","medium_truck","light_truck","biodiesel_truck","van_light","passenger_car_small","passenger_car_medium","passenger_car_large","passenger_car_electric","passenger_car_average","bus","trolleybus","tractor","aircraft_long_haul","aircraft_medium_haul","aircraft_short_haul","aircraft_very_short_haul","aircraft_unspecified","barge","barge_tanker","container_ship","ferry","tanker","train","pipeline"]},"transport_medium":{"type":["string","null"],"enum":[null,"road","air","inland_waterway","sea","rail","pipeline"]},"distance":{"type":["number","null"],"minimum":0},"distance_unit":{"type":["string","null"],"default":"km"},"weight":{"type":["number","null"],"minimum":0},"weight_unit":{"type":["string","null"],"default":"kg"}},"allOf":[{"if":{"properties":{"distance_unit":{"type":"string","minLength":1}},"required":["distance_unit"]},"then":{"required":["distance"],"properties":{"distance":{"$ref":"#/$defs/NonNegativeNumber"}}}},{"if":{"properties":{"weight_unit":{"type":"string","minLength":1}},"required":["weight_unit"]},"then":{"required":["weight"],"properties":{"weight":{"$ref":"#/$defs/NonNegativeNumber"}}}}]},"InputTransportStrict":{"description":"Transport row (strict profile): name (the matching key for candidate retrieval), origin, destination, distance, weight, and transport_mode or transport_medium. Units default to kg/km when omitted; a non-canonical unit (anything other than kg/km) is rejected until the transport-evidence unit-normalization rule lands (#566). When both mode and medium are supplied they must agree (mode -> medium derivation, enforced by the Pydantic profile; JSON Schema cannot express the mapping).","allOf":[{"$ref":"#/$defs/InputTransportLoose"},{"required":["name","origin","destination","distance","weight"],"properties":{"name":{"type":"string","minLength":1},"origin":{"type":"string","minLength":1},"destination":{"type":"string","minLength":1},"distance":{"$ref":"#/$defs/NonNegativeNumber"},"weight":{"$ref":"#/$defs/NonNegativeNumber"}}},{"anyOf":[{"required":["transport_mode"],"not":{"properties":{"transport_mode":{"const":null}}}},{"required":["transport_medium"],"not":{"properties":{"transport_medium":{"const":null}}}}]},{"if":{"properties":{"distance_unit":{"type":"string","minLength":1}},"required":["distance_unit"]},"then":{"properties":{"distance_unit":{"type":"string","pattern":"^[kK][mM]$"}}}},{"if":{"properties":{"weight_unit":{"type":"string","minLength":1}},"required":["weight_unit"]},"then":{"properties":{"weight_unit":{"type":"string","pattern":"^[kK][gG]$"}}}},{"if":{"properties":{"unit":{"type":"string","minLength":1}},"required":["unit"]},"then":{"required":["amount"],"properties":{"amount":{"$ref":"#/$defs/NonNegativeNumber"}}}}]},"InputManufacturingLoose":{"description":"Customer manufacturing row (flexible profile). No required fields at line-item level.","type":"object","additionalProperties":false,"properties":{"name":{"type":["string","null"]},"description":{"type":["string","null"]},"unit":{"type":["string","null"]},"geography":{"type":["string","null"]},"geography_code":{"type":["string","null"]},"geography_name":{"type":["string","null"]},"amount":{"type":["number","null"],"minimum":0}}},"InputManufacturingStrict":{"description":"Manufacturing row (strict profile): name, amount, and unit are all required.","allOf":[{"$ref":"#/$defs/InputManufacturingLoose"},{"required":["name","amount","unit"],"properties":{"name":{"type":"string","minLength":1},"amount":{"$ref":"#/$defs/NonNegativeNumber"},"unit":{"type":"string","minLength":1}}}]},"InputProductCoreProperties":{"type":"object","additionalProperties":false,"properties":{"sku":{"type":"string","minLength":1},"name":{"type":"string","minLength":1},"description":{"type":["string","null"]},"brand":{"type":["string","null"]},"customer_category":{"type":["string","null"]},"url":{"type":["string","null"]},"manufacturing_location":{"description":"Raw customer descriptor (code, name, or alias), resolved to OutputProduct.geography_id at ingest.","type":"string","minLength":1},"physical_properties":{"anyOf":[{"$ref":"#/$defs/PhysicalProperties"},{"type":"null"}]},"system_boundary":{"$ref":"#/$defs/SystemBoundary"},"functional_unit":{"type":"string","enum":["One unit of product","One kilogram of product"]},"circularity":{"anyOf":[{"$ref":"#/$defs/InputCircularity"},{"type":"null"}]},"use_stage":{"anyOf":[{"$ref":"#/$defs/InputUseStage"},{"type":"null"}]},"market_mix":{"description":"Customer-declared product market mix. Empty array is invalid when declared; omit/null means undeclared.","anyOf":[{"type":"array","minItems":1,"items":{"$ref":"#/$defs/InputMarket"}},{"type":"null"}]},"end_of_life":{"anyOf":[{"$ref":"#/$defs/InputEndOfLife"},{"type":"null"}]},"materials":{"anyOf":[{"type":"array","items":{"$ref":"#/$defs/InputMaterialLoose"}},{"type":"null"}]},"packaging":{"anyOf":[{"type":"array","items":{"$ref":"#/$defs/InputMaterialLoose"}},{"type":"null"}]},"transports":{"anyOf":[{"type":"array","items":{"$ref":"#/$defs/InputTransportLoose"}},{"type":"null"}]},"manufacturing":{"anyOf":[{"type":"array","items":{"$ref":"#/$defs/InputManufacturingLoose"}},{"type":"null"}]}}},"InputProductFlexible":{"description":"Flexible customer product input: requires sku, name, manufacturing_location, functional_unit, and system_boundary only. Lifecycle lists and all other fields may be omitted, null, or empty.","allOf":[{"$ref":"#/$defs/InputProductCoreProperties"},{"required":["sku","name","manufacturing_location","functional_unit","system_boundary"]}]},"InputProductStrict":{"description":"Strict customer product input: flexible requirements plus at least one row in each lifecycle list with line-item rules documented on the strict row defs.","allOf":[{"$ref":"#/$defs/InputProductFlexible"},{"required":["materials","packaging","transports","manufacturing"],"properties":{"materials":{"type":"array","minItems":1,"items":{"$ref":"#/$defs/InputMaterialStrict"}},"packaging":{"type":"array","minItems":1,"items":{"$ref":"#/$defs/InputPackagingStrict"}},"transports":{"type":"array","minItems":1,"items":{"$ref":"#/$defs/InputTransportStrict"}},"manufacturing":{"type":"array","minItems":1,"items":{"$ref":"#/$defs/InputManufacturingStrict"}}}},{"if":{"properties":{"materials":{"contains":{"required":["percentage"],"not":{"required":["amount"]}}}},"required":["materials"]},"then":{"required":["physical_properties"],"properties":{"physical_properties":{"type":"object","required":["weight"],"properties":{"weight":{"type":"number","exclusiveMinimum":0}}}}}}]},"InputMaterial":{"description":"Deprecated alias for InputMaterialLoose.","$ref":"#/$defs/InputMaterialLoose"},"InputTransport":{"description":"Deprecated alias for InputTransportLoose.","$ref":"#/$defs/InputTransportLoose"},"InputManufacturing":{"description":"Deprecated alias for InputManufacturingLoose.","$ref":"#/$defs/InputManufacturingLoose"},"InputProduct":{"description":"Deprecated alias for InputProductFlexible.","$ref":"#/$defs/InputProductFlexible"}}}