{"openapi":"3.1.0","info":{"title":"Ecodex API","version":"2026-10-01","description":"Ecodex REST API v1: search, read and discover carbon emission factors.\n\nDeveloper guide (quick start, envelope, errors, pricing): https://getecodex.com/developers\n\nAuthentication: an API key created in Settings → API, sent as `Authorization: Bearer ecodex_sk_…` (or `X-API-Key`). \nServer-to-server only: the API sends no CORS headers, never put a key in a browser or in a query string.\n\nEvery success response is `{ data, meta, warnings[] }`; every error is `{ error: { code, message, request_id, details } }`. \nQuote `meta.request_id` / `error.request_id` (also in the `X-Request-Id` header) when contacting support.\n\nSpend cap: a workspace invoiced in euros has a monthly spend cap (calendar month, UTC). A billable call whose \nworst-case cost would take the month above it is refused with HTTP 402 `spend_cap_reached` before anything runs; \nnothing is billed. `GET /v1/usage` shows what the month has cost so far.\n\nResults depend on the search engine version: for an identical input, results can change when the engine evolves \n(prompts, guards, model). `meta.engine.version` and `meta.engine.model_version` trace it.\n\nSources: only the sources assigned to your workspace are searchable and readable. Attribution of the source is \nrequired in any display of a value (see `GET /v1/filters/source` for the licence of each source).","contact":{"name":"Ecodex","url":"https://getecodex.com"}},"servers":[{"url":"https://getecodex.com/api/v1"}],"tags":[{"name":"Search","description":"AI-ranked search of emission factors. Billed per call."},{"name":"Match","description":"Match up to 200 activity rows to emission factors. Billed per evaluated item."},{"name":"PCF","description":"Beta. Product carbon footprint: compute, seal, read, publish."},{"name":"Factors","description":"Read one emission factor by identifier. Free, rate-limited."},{"name":"Filters","description":"Values available for the search filters, scoped to your sources. Free."},{"name":"Usage","description":"Metered usage of your workspace. Free."},{"name":"Service","description":"API information and the OpenAPI document. Public."}],"security":[{"bearerAuth":[]},{"apiKeyHeader":[]}],"paths":{"/":{"get":{"operationId":"getInfo","tags":["Service"],"summary":"API information","description":"API information. No authentication.","security":[],"responses":{"200":{"description":"API information.","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/openapi.json":{"get":{"operationId":"getOpenApi","tags":["Service"],"summary":"OpenAPI document","description":"This OpenAPI document. No authentication.","security":[],"responses":{"200":{"description":"This document.","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/search":{"post":{"operationId":"searchFactors","tags":["Search"],"summary":"Search emission factors","description":"AI-ranked search of emission factors (hybrid keyword + semantic retrieval, then AI reranking). One query per call, billed per call. A call the engine could not evaluate answers 503 and is not billed.\n\nEvery parameter travels in the JSON body (there are no query-string parameters): `query` is the only required field; `location`, `scope`, `source`, `unit_type`, `data_type`, `year_min` / `year_max` narrow the results. Filter values are exact strings: list them with GET /filters/{facet} (GET /filters/scope for the boundaries, GET /filters/location for the locations) rather than guessing. The example picker of the request body shows a minimal, a filtered and a country-comparison request.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"query":{"type":"string","minLength":2,"description":"Product or activity to look up (e.g. \"hot-rolled stainless steel\"). Name only: put the country in `location`, the boundary in `scope`. French or English, matched against both catalog languages."},"context":{"type":"string","description":"Optional business intent used to rank results, e.g. \"Bill of materials of an industrial machine, cradle-to-gate per kg\". Ignored when unrelated to the query."},"language":{"default":"en","type":"string","enum":["fr","en"],"description":"Language of the returned labels (`fr` or `en`, default `en`). Does not restrict the matching."},"source":{"type":"array","items":{"type":"string"},"description":"Restrict to these sources (exact names, see `GET /v1/filters/source`). Strict: each must be accessible to your workspace (`has_access`), otherwise the call is refused with `sources_not_accessible`."},"location":{"type":"array","items":{"type":"string"},"description":"Preferred locations (exact values, see `GET /v1/filters/location`). Soft preference: a result from another location can be returned, flagged by the `location_mismatch` warning."},"scope":{"type":"array","items":{"type":"string"},"description":"Life-cycle boundaries (exact values, see `GET /v1/filters/scope`). Strict."},"data_type":{"type":"array","items":{"type":"string"},"description":"Data types, e.g. \"Generic\" (see `GET /v1/filters/data_type`). Strict."},"unit":{"type":"array","items":{"type":"string"},"description":"Preferred activity units (see `GET /v1/filters/unit`). Boosts matching units, does not exclude others."},"unit_type":{"type":"array","items":{"type":"string"},"description":"Unit families: mass, energy, volume, surface, distance, freight, passenger_transportation, power, time, monetary, number, composite. Strict: other families are excluded. A family, never a unit: any other value is refused with `unknown_unit_type` (for \"km\" or \"kg\", use `unit`)."},"year_min":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Oldest reference year accepted (inclusive)."},"year_max":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Most recent reference year accepted (inclusive)."},"currency":{"type":"string","pattern":"^[A-Z]{3}$","description":"ISO 4217 code (see `GET /v1/filters/currency`). Converts monetary factors for display; `source_fe` / `source_unit` then carry the catalog value."},"convert_to_unit":{"type":"string","description":"Target unit within a family (see `GET /v1/filters/convert_to_unit_targets`), e.g. \"ton\" converts per-kg factors to per-ton. Display only."},"compare_countries":{"default":false,"type":"boolean","description":"When true, results are distinct factors each carrying `country_variants[]` (one value per location). For comparing the same factor across countries."}},"required":["query"],"additionalProperties":false},"examples":{"minimal":{"summary":"Minimal: one query, English labels","value":{"query":"hot-rolled stainless steel","language":"en","context":"Bill of materials of an industrial machine, cradle-to-gate factor per kg","unit_type":["mass"]}},"filtered_fr":{"summary":"Filtered: French labels, location and boundary","value":{"query":"plastique","language":"fr","context":"Matière première d’un produit industriel, facteur cradle-to-gate par kg","location":["France"],"scope":["cradle_to_gate"],"unit_type":["mass"]}},"compare_countries":{"summary":"Compare the same factor across countries","value":{"query":"electricity","language":"en","compare_countries":true,"source":["Ember"],"unit_type":["energy"]}}}}}},"responses":{"200":{"description":"Ranked results (best first, at most 10).","content":{"application/json":{"schema":{"type":"object","required":["data","meta","warnings"],"properties":{"data":{"$ref":"#/components/schemas/SearchData"},"meta":{"$ref":"#/components/schemas/Meta"},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/Warning"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}}},"/filters/{facet}":{"get":{"operationId":"listFilterValues","tags":["Filters"],"summary":"List the values of a search filter","description":"Values available for a search filter, scoped to your sources. Free.","parameters":[{"name":"facet","in":"path","required":true,"description":"Filter to list: source, scope, location, unit, unit_type, data_type, sector, sub_sector, currency, convert_to_unit_targets.","example":"scope","schema":{"type":"string","enum":["source","scope","location","unit","unit_type","data_type","sector","sub_sector","currency","convert_to_unit_targets"]}},{"name":"language","in":"query","required":false,"description":"Language of the values for bilingual facets (`fr` or `en`, default `en`).","schema":{"default":"en","type":"string","enum":["fr","en"]}},{"name":"source","in":"query","required":false,"description":"Restrict to these sources (repeat the parameter: `?source=DEFRA&source=IDEES`). Each must be accessible to your workspace (`has_access`), otherwise the call is refused with `sources_not_accessible`. For `facet=source`, restricts the catalog listing (any source, accessible or not) and returns the licence details. Ignored by `currency` and `convert_to_unit_targets`.","example":["DEFRA","Base Carbone"],"schema":{"maxItems":100,"type":"array","items":{"type":"string","minLength":1}},"style":"form","explode":true},{"name":"prefix","in":"query","required":false,"description":"Case-insensitive starts-with filter on the values (useful for `location`).","example":"cradle","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"Filter values.","content":{"application/json":{"schema":{"type":"object","required":["data","meta","warnings"],"properties":{"data":{"$ref":"#/components/schemas/FilterValues"},"meta":{"$ref":"#/components/schemas/Meta"},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/Warning"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/factors/{id}":{"get":{"operationId":"getFactor","tags":["Factors"],"summary":"Read one emission factor","description":"Read one emission factor by identifier. Free, dedicated hourly limit (default 300/hour). Unknown and inaccessible factors both answer 404.","parameters":[{"name":"id","in":"path","required":true,"description":"Factor identifier: the `object_id` of a search result.","example":"8e8d23e6-4871-4093-8089-6cfc17920e45","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The factor.","content":{"application/json":{"schema":{"type":"object","required":["data","meta","warnings"],"properties":{"data":{"$ref":"#/components/schemas/Factor"},"meta":{"$ref":"#/components/schemas/Meta"},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/Warning"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/match":{"post":{"operationId":"matchFactors","tags":["Match"],"summary":"Match activity rows to emission factors","description":"Match up to 200 activity rows (bill of materials, purchases, inventory) to emission factors in one synchronous call. Billed per evaluated item: items in `status: \"error\"` (timeout, rate limit, AI outage) were not evaluated, are not billed, and can be re-sent alone.\n\nEach item comes back with `status` (`matched`, `unmatched`, `error`), the best factor (`match`), its `confidence` (high ≥ 0.75, medium ≥ 0.50) and `alternatives`. `config.context` (50 characters minimum) is required: it drives the AI scoring. The call answers within 100 s; past that it fails with 504 `engine_timeout` and nothing is billed. A call replayed after a network error is billed again: re-send only the items you did not receive.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"items":{"minItems":1,"maxItems":200,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Your row identifier (row number, SKU…), returned as is. Unique within the call."},"activity":{"type":"string","minLength":1,"description":"Label of the row, lightly normalized: expand abbreviations, fix typos, drop internal reference codes; keep brands and models. Prefer the full material name (\"Polycarbonate\", not \"PC\"). French or English (other languages: translate first)."},"activity_fallback":{"type":"string","description":"Optional second query, used when `activity` finds nothing acceptable: the acronym of a full chemical name (\"PVC\"), a more specific catalog-like name for a vague label, or a synonym. Must differ from `activity`."},"unit":{"type":"string","enum":["kg","ton","kWh","m²","m³","unit","t.km","km","k€","m","L","MWh","passenger.km","lb","Ha","£ (GBP)","TEU.km","miles","ft","ft²","hour","year","ha","gallon","W","kW","boe"],"description":"Unit of your quantity (strict list, see `GET /v1/filters/unit`). Drives the unit-compatibility score: e.g. \"kg\" for materials, \"kWh\" for energy, \"t.km\" for freight, \"k€\" for spend."},"unit_type":{"type":"string","enum":["mass","energy","volume","surface","distance","freight","passenger_transportation","monetary","number","power","time","composite"],"description":"Optional strict unit family (mass, energy, volume, surface, distance, freight, passenger_transportation, monetary, number, power, time, composite): candidates of another family are excluded. Leave empty when unsure."},"location":{"type":"string","description":"Optional country or region of the activity (e.g. \"France\", \"China\"), full name. Leave empty when unknown rather than guessing."},"item_type":{"type":"string","enum":["raw_material","finished_product","energy","transport_freight","transport_passenger","service","waste","land_use","water"],"description":"Optional GHG Protocol category of the row (raw_material, finished_product, energy, transport_freight, transport_passenger, service, waste, land_use, water). Guides scoring; not a filter."}},"required":["id","activity","unit"]},"description":"Activity rows to match, 1 to 200 per call. `id` must be unique within the call: it is your join key back to your file."},"language":{"default":"en","type":"string","enum":["fr","en"],"description":"Language of the returned labels and messages (`fr` or `en`, default `en`). Matching uses both catalog languages regardless."},"config":{"type":"object","properties":{"context":{"type":"string","minLength":50,"description":"Required, 50 characters minimum: describe the batch (sector, product types, geography, intended use, life-cycle boundary, expected units). It drives the AI scoring of every item."},"sources":{"type":"array","items":{"type":"string"},"description":"Restrict to these sources (exact names, see `GET /v1/filters/source`). Each must be accessible to your workspace, otherwise the call is refused with `sources_not_accessible`."},"scope":{"type":"string","description":"Restrict to one life-cycle boundary (exact value, see `GET /v1/filters/scope`). Strict at retrieval."},"region_fallback":{"default":true,"type":"boolean","description":"Default true: `location` is a soft preference. False: other locations are excluded at retrieval (rarely useful)."},"currency":{"type":"string","pattern":"^[A-Z]{3}$","description":"ISO 4217 code (see `GET /v1/filters/currency`): converts monetary factors for display; `source_fe` / `source_unit` then carry the catalog value. Does not change the scores."},"convert_to_unit":{"type":"string","description":"Target unit within a family (see `GET /v1/filters/convert_to_unit_targets`), e.g. \"ton\". Display only."}},"required":["context"],"description":"Settings applied to every item of the call."}},"required":["items","config"],"additionalProperties":false},"examples":{"purchases_fr":{"summary":"Two purchase lines, French labels","value":{"config":{"context":"Inventaire achats d’un fabricant de mobilier, lignes en français, quantités en kg"},"language":"fr","items":[{"id":"row-12","activity":"Panneau MDF 18 mm","unit":"kg","location":"France","item_type":"raw_material"},{"id":"row-13","activity":"Vis acier zinguée","unit":"unit","item_type":"finished_product"}]}},"bom_en":{"summary":"Bill of materials, English labels, per-ton display","value":{"config":{"context":"Bill of materials of a small kitchen appliance made in France, cradle-to-gate factors per kg of raw material","convert_to_unit":"ton"},"language":"en","items":[{"id":"1","activity":"Polypropylene","unit":"kg","location":"Germany","item_type":"raw_material"},{"id":"2","activity":"Stainless steel","unit":"kg","item_type":"raw_material"},{"id":"3","activity":"Corrugated cardboard","unit":"kg","item_type":"raw_material"}]}}}}}},"responses":{"200":{"description":"One result per item, in the order sent.","content":{"application/json":{"schema":{"type":"object","required":["data","meta","warnings"],"properties":{"data":{"$ref":"#/components/schemas/MatchData"},"meta":{"$ref":"#/components/schemas/Meta"},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/Warning"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"},"504":{"$ref":"#/components/responses/EngineTimeout"}}}},"/pcf/compute":{"post":{"operationId":"computePcf","tags":["PCF"],"summary":"Compute a product carbon footprint (beta)","description":"Beta. Compute the carbon footprint of a product from its bill of materials (A1), with optional transport (A2), manufacturing (A3), distribution (A4), use (B) and end of life (C). Iterative: send a partial model, read `missing_fields[]` and `assumptions[]`, enrich, compute again.\n\nBilling: one PCF per session (up to 50 distinct resolved lines; 51-100 lines = 2 PCF), metered at the first compute that resolves a line; recomputes of the same session are free, finalize is free. **Always send back the `data.session_id` of the previous response**: unchanged materials are then neither re-matched nor billed again, and the total stays reproducible. Without it, each call is a new session and is billed again. The returned id can differ from the one sent (always keep the latest). A session expires after 30 days without use and accepts 50 computes (409 `session_compute_cap` beyond). `incomplete: true` means some lines are excluded from the total (see the `incomplete` warning).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"product_model":{"type":"object","properties":{"functional_unit":{"type":"object","properties":{"amount":{"type":"number","description":"Amount of the functional unit."},"unit":{"type":"string","description":"Unit of the functional unit (kg, unit, m2, L…)."}},"required":["amount","unit"],"description":"Declared functional unit, e.g. `{ \"amount\": 1, \"unit\": \"unit\" }` or `{ \"amount\": 1, \"unit\": \"kg\" }`. Results are per functional unit."},"reference_flow":{"type":"object","properties":{"amount":{"type":"number","description":"Amount of the reference flow."},"unit":{"type":"string","description":"Unit of the reference flow."}},"required":["amount","unit"],"description":"Reference flow when it differs from the functional unit (denominator of the intensity)."},"production":{"type":"object","properties":{"country":{"type":"array","items":{"type":"string"},"description":"Production countries (full names). Required for downstream distribution (A4)."},"valid_from":{"type":"string","description":"Start of validity of the footprint (ISO 8601), copied to the PACT reference period."}},"description":"Where the product is made."},"buyer_id":{"type":"string","description":"Optional buyer identifier (loads buyer-specific defaults when configured)."},"methodology":{"type":"object","properties":{"standard":{"type":"array","items":{"type":"string"},"description":"Standards followed (e.g. \"ISO 14067\", \"GHG Protocol Product\", \"PACT\")."},"allocation_default":{"type":"string","enum":["mass","economic"],"description":"Default allocation key for co-products: mass | economic."}},"description":"Declared methodology."},"stages":{"type":"object","properties":{"A1_materials":{"maxItems":200,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"description":"Material label (the matching query). Use the full material name (\"Polyvinyl chloride\", not \"PVC\")."},"quantity":{"type":"number","description":"Quantity of this material in the reference flow."},"unit":{"type":"string","description":"Unit of the quantity (kg, g, ton…)."},"location":{"type":"string","description":"Country where the material is PRODUCED (not where it is bought). Drives the geographic matching and the default upstream transport."},"role":{"type":"string","enum":["component","packaging"],"description":"component (default) or packaging (the sales packaging of the product: in the bill of materials, excluded from the assembly mass)."},"description":{"type":"string","description":"Longer description, used as a fallback query."},"fe_objectID":{"type":"string","description":"Pin an emission factor (`object_id` from `/v1/search` or `/v1/match`): resolved directly, no AI matching. Refused when the factor’s source is not assigned to your workspace."},"fe_state":{"type":"string","description":"Transformation state of the matched factor (resin, pellets, semi_finished, component…). Inferred when absent."},"target_state":{"type":"string","description":"State of the component in the finished product: the engine adds the manufacturing processes between the two states."}},"required":["name","quantity","unit"]},"description":"Bill of materials, at most 200 lines. Each line is matched to an emission factor unless pinned with `fe_objectID`."},"A2_transport":{"type":"object","properties":{"temperature_regime":{"type":"string","description":"Default temperature regime of the logistics platforms: ambient | mixed | controlled."},"legs":{"type":"array","items":{"type":"object","properties":{"mode":{"type":"string","description":"road | sea | rail | air | inland_waterway."},"distance_km":{"type":"number","description":"Distance of the leg in km. Filled from distance tables when absent."},"origin":{"type":"string","description":"Origin country or region of the leg."},"dest":{"type":"string","description":"Destination country or region of the leg."},"load_factor":{"type":"number","description":"Optional load factor."},"material_ref":{"type":["string","number"],"description":"A1 material carried by this leg: index in `A1_materials` (preferred) or material name. Absent: the leg carries the whole bill of materials."},"material_refs":{"type":"array","items":{"type":["string","number"]},"description":"Several A1 materials carried by this leg (indexes or names)."}}},"description":"Transport legs (emissions = tonnes × km × freight factor)."},"nodes":{"type":"array","items":{"type":"object","properties":{"node_type":{"type":"string","description":"Logistics platform type (warehouse, transhipment, storage…)."},"temperature_regime":{"type":"string","description":"ambient | mixed | controlled (cold chain)."}}},"description":"Logistics platforms (emissions = tonnes × platform factor)."}},"description":"Upstream transport (suppliers → plant). Absent: default routing inferred from the material origins and the production country."},"A3_manufacturing":{"type":"object","properties":{"processes":{"type":"array","items":{"type":"object","properties":{"family":{"type":"string","description":"Process family: plastics | metals | textile | packaging | chemicals | agro | generic."},"process":{"type":"string","description":"Process, e.g. plastics_extrusion, metals_rolling."},"output_qty":{"type":"number","description":"Output quantity of the process."},"energy_kwh":{"type":"number","description":"Measured energy use of the process (kWh)."},"energy_country":{"type":"string","description":"Country of the electricity grid (location-based)."},"renewable_share":{"type":"number","description":"Renewable share of the electricity, 0..1 (market-based disclosure)."},"contract_ef":{"type":"number","description":"Contractual electricity factor, kgCO2e/kWh (market-based)."},"loss_rate":{"type":"number","description":"Material loss rate of the process, 0..1 (inflates the upstream material)."},"allocation":{"type":"object","properties":{"method":{"type":"string","enum":["mass","economic"]},"share":{"type":"number"},"product":{"type":"object","properties":{"mass":{"type":"number"},"price":{"type":"number"},"quantity":{"type":"number"}}},"co_products":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"mass":{"type":"number"},"price":{"type":"number"},"quantity":{"type":"number"}}}},"justification":{"type":"string"}},"description":"Co-product allocation of the process: `method` (mass | economic), an explicit `share` 0..1, or the mass / price / quantity of the product and of each co-product, plus a `justification`."}}},"description":"Explicit manufacturing processes (rarely needed)."},"direct_emissions":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Label of the emission."},"value":{"type":"number","description":"Emission in kgCO2e."},"unit":{"type":"string","description":"Unit of the value."}}},"description":"Direct emissions declared by the plant."},"assembly":{"anyOf":[{"type":"object","properties":{"category":{"type":"string","description":"electronics | mechanical | textile_confection | furniture | generic (default energy per kg)."},"energy_kwh":{"type":"number","description":"Measured total assembly energy (kWh): preferred over the default."},"energy_country":{"type":"string","description":"Country of the assembly electricity. Default: production country."},"contract_ef":{"type":"number","description":"Contractual electricity factor, kgCO2e/kWh (market-based)."},"renewable_share":{"type":"number","description":"Renewable share of the assembly electricity, 0..1."},"loss_rate":{"type":"number","description":"Loss rate of the assembly, 0..1."}}},{"type":"boolean","const":false}],"description":"Final assembly of the components: one line computed on the mass of the non-packaging components. Modelled by default (category `generic`); pass `false` to leave it out (single-material product, assembly outside your boundary)."}},"description":"Manufacturing. Present (even `{}`): the engine derives the processes from the gap between the matched factors and the target states. Absent: created with the default assembly (`{ \"assembly\": {} }`), so the footprint always includes the step that combines the components."},"A4_distribution":{"type":"object","properties":{"temperature_regime":{"type":"string","description":"Temperature regime of the downstream platforms: ambient | mixed | controlled."},"shipped_mass_kg":{"type":"number","description":"Known shipped mass (product + packaging, kg). Default: reference-flow mass plus shipping packaging."},"destinations":{"maxItems":10,"type":"array","items":{"type":"object","properties":{"location":{"type":"string","minLength":1,"description":"Market country."},"sales_share":{"type":"number","description":"Share of sales on this market (%), normalized server-side."},"legs":{"type":"array","items":{"type":"object","properties":{"mode":{"type":"string","description":"road | sea | rail | air | inland_waterway."},"distance_km":{"type":"number","description":"Distance of the leg in km. Filled from distance tables when absent."},"origin":{"type":"string","description":"Origin country or region of the leg."},"dest":{"type":"string","description":"Destination country or region of the leg."},"load_factor":{"type":"number","description":"Optional load factor."},"material_ref":{"type":["string","number"],"description":"A1 material carried by this leg: index in `A1_materials` (preferred) or material name. Absent: the leg carries the whole bill of materials."},"material_refs":{"type":"array","items":{"type":["string","number"]},"description":"Several A1 materials carried by this leg (indexes or names)."}}},"description":"Explicit plant → market legs (same fields as A2). Absent: default routing."},"nodes":{"type":"array","items":{"type":"object","properties":{"node_type":{"type":"string","description":"Logistics platform type (warehouse, transhipment, storage…)."},"temperature_regime":{"type":"string","description":"ambient | mixed | controlled (cold chain)."}}},"description":"Logistics platforms. Absent: one default distribution warehouse; `[]`: none."}},"required":["location","sales_share"]},"description":"Sales markets (1 to 10), weighted by their share of sales."},"packaging":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"description":"Packaging label (the matching query)."},"quantity":{"type":"number","description":"Quantity of packaging."},"unit":{"type":"string","description":"Unit (kg, g…)."},"fe_objectID":{"type":"string","description":"Pin an emission factor (no AI matching)."},"description":{"type":"string","description":"Longer description, used as a fallback query."}},"required":["name","quantity","unit"]},"description":"Shipping packaging (secondary / tertiary: film, cartons, pallets). The sales packaging goes in A1 with `role: \"packaging\"`."}},"description":"Optional downstream distribution (plant → markets, cradle-to-shelf). Requires `production.country`. Reported in the `a4_distribution` extension; the sealed PACT footprint stays cradle-to-gate."},"B_use":{"type":"object","properties":{"declared_negligible":{"type":"object","properties":{"justification":{"type":"string","minLength":20,"description":"Required justification (20 characters minimum), copied to the deliverable."}},"required":["justification"],"description":"Passive product: no consumption during use. Exclusive with `profile`."},"profile":{"type":"object","properties":{"lifetime":{"type":"object","properties":{"value":{"type":"number"},"unit":{"type":"string"}},"required":["value","unit"],"description":"Declared service life `{ value, unit }` (informative, not used in the computation)."},"cycles":{"type":"object","properties":{"count":{"type":"number"},"unit":{"type":"string"}},"required":["count"],"description":"Total cycles over the whole life `{ count, unit }` (not per year): multiplies the per-cycle consumption."}},"description":"Use profile: lifetime and total number of cycles."},"use_locations":{"maxItems":10,"type":"array","items":{"type":"object","properties":{"location":{"type":"string","minLength":1,"description":"Country of use (drives the electricity mix)."},"share":{"type":"number","description":"Share of use in this country (%), normalized server-side."}},"required":["location","share"]},"description":"Countries of use (1 to 10) with shares. Absent: inherited from the A4 markets; without either, electricity is not computed."},"energy":{"type":"array","items":{"type":"object","properties":{"carrier":{"type":"string","minLength":1,"description":"Energy carrier (\"electricity\", \"natural gas\"…)."},"quantity_per_cycle":{"type":"number","description":"Quantity consumed per cycle."},"unit":{"type":"string","description":"Unit (kWh, MJ…)."},"fe_objectID":{"type":"string","description":"Pin an emission factor for a non-electricity carrier."},"description":{"type":"string","description":"Longer description, used as a fallback query."},"contract_ef":{"type":"number","description":"Contractual electricity factor, kgCO2e/kWh (overrides the grid mix)."},"renewable_share":{"type":"number","description":"Renewable share of the electricity, 0..1."}},"required":["carrier","quantity_per_cycle","unit"]},"description":"Energy consumed per cycle. \"electricity\" uses the grid mix of each country of use; other carriers are matched like materials."},"consumables":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"description":"Consumable label (the matching query)."},"quantity_per_cycle":{"type":"number","description":"Quantity consumed per cycle."},"unit":{"type":"string","description":"Unit (L, kg…)."},"fe_objectID":{"type":"string","description":"Pin an emission factor (no AI matching)."},"description":{"type":"string","description":"Longer description, used as a fallback query."}},"required":["name","quantity_per_cycle","unit"]},"description":"Consumables per cycle (water, detergent, filters…), matched like materials."},"grid_decarbonization":{"type":"number","description":"What-if lever, 0..1: projected decarbonization of the grid applied to the electricity factor. Absent (baseline): current mix held constant."}},"description":"Optional use phase: either `declared_negligible` (passive product) or `profile` + `energy` / `consumables`. Reported in the `use_phase` extension."},"C_end_of_life":{"type":"object","properties":{"markets":{"maxItems":10,"type":"array","items":{"type":"object","properties":{"location":{"type":"string","minLength":1,"description":"End-of-life country."},"share":{"type":"number","description":"Share treated in this country (%), normalized server-side."}},"required":["location","share"]},"description":"End-of-life markets (1 to 10) with shares. Absent: the A4 markets, else world average."},"treatment_mix":{"type":"object","properties":{"recycling":{"type":"number"},"incineration":{"type":"number"},"landfill":{"type":"number"},"compost":{"type":"number"}},"description":"Product-level treatment mix override: `recycling` / `incineration` / `landfill` / `compost` in %, normalized server-side."},"materials":{"type":"array","items":{"type":"object","properties":{"material_ref":{"type":["string","number"],"description":"A1 material: index, `A1[i]` or exact material name."},"family":{"type":"string","description":"Force the end-of-life family: plastics | metals_ferrous | metals_non_ferrous | paper_cardboard | glass | wood | textile | electronics | mineral | organics | other."},"treatment_mix":{"type":"object","properties":{"recycling":{"type":"number"},"incineration":{"type":"number"},"landfill":{"type":"number"},"compost":{"type":"number"}},"description":"Treatment mix of this material (replaces the country mix)."}},"required":["material_ref"]},"description":"Per-material overrides."}},"description":"Optional end of life, largely automatic: present (even `{}`) = the product mass is split by material family × market and treated with each country’s statistical mix (recycling in cut-off). Reported in the `end_of_life` extension."}},"description":"Life-cycle stages. A1–A3 make the cradle-to-gate footprint; A4 adds distribution (cradle-to-shelf); B and C are reported separately, and both together make it cradle-to-grave."},"overrides":{"type":"array","items":{"type":"object","properties":{"stage":{"type":"string"},"line_ref":{"type":"string"},"field":{"type":"string"},"value":{},"justification":{"type":"string"},"by":{"type":"string"}},"required":["stage","line_ref","field","value","justification","by"]},"description":"Audited overrides of computed values: `stage`, `line_ref`, `field`, `value`, `justification`, `by`."}},"required":["functional_unit","stages"],"description":"The product to assess. Minimum: `functional_unit` and `stages.A1_materials` (name, quantity, unit, location). Every other block is optional, with documented defaults; gaps are reported in `missing_fields[]`."},"language":{"default":"en","type":"string","enum":["fr","en"],"description":"Language of labels and messages (`fr` or `en`, default `en`)."},"context":{"type":"string","description":"Optional business context guiding the material matching (sector, use, geography). Synthesized from the model when absent."},"session_id":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Omit (or null) on the first call. Then ALWAYS send back the `data.session_id` of the previous response: unchanged materials are not re-matched (faster, not billed again) and the total stays reproducible. The returned id can differ from the one sent: always keep the one from the response."},"infer_fe_states":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true","false"]}],"description":"Optional, default false: let the AI infer the transformation state of untagged factors (better A3; capped data quality, flagged `ai_estimate`). Frozen per session: pass the same value at finalize."},"infer_numeric":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true","false"]}],"description":"Optional, default false: let the AI estimate a missing leg distance or process energy within hard bounds (capped data quality, flagged `ai_estimate`). Never an emission factor. Frozen per session: pass the same value at finalize."}},"required":["product_model"],"additionalProperties":false},"examples":{"minimal":{"summary":"Minimal: functional unit + bill of materials","value":{"language":"en","session_id":null,"product_model":{"functional_unit":{"amount":1,"unit":"unit"},"production":{"country":["France"]},"stages":{"A1_materials":[{"name":"Hot-rolled steel","quantity":2.4,"unit":"kg","location":"Germany"},{"name":"Polypropylene","quantity":0.6,"unit":"kg","location":"China"}]}}}},"usual":{"summary":"Usual: materials, upstream transport, assembly","value":{"language":"fr","session_id":null,"product_model":{"functional_unit":{"amount":1,"unit":"unit"},"production":{"country":["France"]},"stages":{"A1_materials":[{"name":"Acier laminé","quantity":2.4,"unit":"kg","location":"Allemagne"},{"name":"Polypropylène","quantity":0.6,"unit":"kg","location":"Chine"},{"name":"Carton ondulé","quantity":0.3,"unit":"kg","role":"packaging"}],"A2_transport":{"legs":[{"mode":"road","distance_km":800,"origin":"Allemagne","dest":"France"}]},"A3_manufacturing":{"assembly":{"category":"mechanical","energy_country":"France"}}}}}}}}}},"responses":{"200":{"description":"The footprint, its lines, assumptions and gaps.","content":{"application/json":{"schema":{"type":"object","required":["data","meta","warnings"],"properties":{"data":{"$ref":"#/components/schemas/PcfResult"},"meta":{"$ref":"#/components/schemas/Meta"},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/Warning"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"},"504":{"$ref":"#/components/responses/EngineTimeout"}}}},"/pcf/sessions/{session_id}/finalize":{"post":{"operationId":"finalizePcf","tags":["PCF"],"summary":"Seal the deliverable of a session (beta)","description":"Beta. Seal the deliverable of a session: a PACT-aligned footprint with a sha-256 `content_hash` (signature `unsigned`). Send the same `product_model` as your last compute. The deliverable is stored as a PCF job of your workspace (`job_id`, also visible in the Ecodex /pcf page); finalizing the same session again updates that job. Free. Refused with 422 `pcf_incomplete` while lines are excluded from the total.","parameters":[{"name":"session_id","in":"path","required":true,"description":"The `session_id` returned by your last `POST /v1/pcf/compute`.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"product_model":{"type":"object","properties":{"functional_unit":{"type":"object","properties":{"amount":{"type":"number","description":"Amount of the functional unit."},"unit":{"type":"string","description":"Unit of the functional unit (kg, unit, m2, L…)."}},"required":["amount","unit"],"description":"Declared functional unit, e.g. `{ \"amount\": 1, \"unit\": \"unit\" }` or `{ \"amount\": 1, \"unit\": \"kg\" }`. Results are per functional unit."},"reference_flow":{"type":"object","properties":{"amount":{"type":"number","description":"Amount of the reference flow."},"unit":{"type":"string","description":"Unit of the reference flow."}},"required":["amount","unit"],"description":"Reference flow when it differs from the functional unit (denominator of the intensity)."},"production":{"type":"object","properties":{"country":{"type":"array","items":{"type":"string"},"description":"Production countries (full names). Required for downstream distribution (A4)."},"valid_from":{"type":"string","description":"Start of validity of the footprint (ISO 8601), copied to the PACT reference period."}},"description":"Where the product is made."},"buyer_id":{"type":"string","description":"Optional buyer identifier (loads buyer-specific defaults when configured)."},"methodology":{"type":"object","properties":{"standard":{"type":"array","items":{"type":"string"},"description":"Standards followed (e.g. \"ISO 14067\", \"GHG Protocol Product\", \"PACT\")."},"allocation_default":{"type":"string","enum":["mass","economic"],"description":"Default allocation key for co-products: mass | economic."}},"description":"Declared methodology."},"stages":{"type":"object","properties":{"A1_materials":{"maxItems":200,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"description":"Material label (the matching query). Use the full material name (\"Polyvinyl chloride\", not \"PVC\")."},"quantity":{"type":"number","description":"Quantity of this material in the reference flow."},"unit":{"type":"string","description":"Unit of the quantity (kg, g, ton…)."},"location":{"type":"string","description":"Country where the material is PRODUCED (not where it is bought). Drives the geographic matching and the default upstream transport."},"role":{"type":"string","enum":["component","packaging"],"description":"component (default) or packaging (the sales packaging of the product: in the bill of materials, excluded from the assembly mass)."},"description":{"type":"string","description":"Longer description, used as a fallback query."},"fe_objectID":{"type":"string","description":"Pin an emission factor (`object_id` from `/v1/search` or `/v1/match`): resolved directly, no AI matching. Refused when the factor’s source is not assigned to your workspace."},"fe_state":{"type":"string","description":"Transformation state of the matched factor (resin, pellets, semi_finished, component…). Inferred when absent."},"target_state":{"type":"string","description":"State of the component in the finished product: the engine adds the manufacturing processes between the two states."}},"required":["name","quantity","unit"]},"description":"Bill of materials, at most 200 lines. Each line is matched to an emission factor unless pinned with `fe_objectID`."},"A2_transport":{"type":"object","properties":{"temperature_regime":{"type":"string","description":"Default temperature regime of the logistics platforms: ambient | mixed | controlled."},"legs":{"type":"array","items":{"type":"object","properties":{"mode":{"type":"string","description":"road | sea | rail | air | inland_waterway."},"distance_km":{"type":"number","description":"Distance of the leg in km. Filled from distance tables when absent."},"origin":{"type":"string","description":"Origin country or region of the leg."},"dest":{"type":"string","description":"Destination country or region of the leg."},"load_factor":{"type":"number","description":"Optional load factor."},"material_ref":{"type":["string","number"],"description":"A1 material carried by this leg: index in `A1_materials` (preferred) or material name. Absent: the leg carries the whole bill of materials."},"material_refs":{"type":"array","items":{"type":["string","number"]},"description":"Several A1 materials carried by this leg (indexes or names)."}}},"description":"Transport legs (emissions = tonnes × km × freight factor)."},"nodes":{"type":"array","items":{"type":"object","properties":{"node_type":{"type":"string","description":"Logistics platform type (warehouse, transhipment, storage…)."},"temperature_regime":{"type":"string","description":"ambient | mixed | controlled (cold chain)."}}},"description":"Logistics platforms (emissions = tonnes × platform factor)."}},"description":"Upstream transport (suppliers → plant). Absent: default routing inferred from the material origins and the production country."},"A3_manufacturing":{"type":"object","properties":{"processes":{"type":"array","items":{"type":"object","properties":{"family":{"type":"string","description":"Process family: plastics | metals | textile | packaging | chemicals | agro | generic."},"process":{"type":"string","description":"Process, e.g. plastics_extrusion, metals_rolling."},"output_qty":{"type":"number","description":"Output quantity of the process."},"energy_kwh":{"type":"number","description":"Measured energy use of the process (kWh)."},"energy_country":{"type":"string","description":"Country of the electricity grid (location-based)."},"renewable_share":{"type":"number","description":"Renewable share of the electricity, 0..1 (market-based disclosure)."},"contract_ef":{"type":"number","description":"Contractual electricity factor, kgCO2e/kWh (market-based)."},"loss_rate":{"type":"number","description":"Material loss rate of the process, 0..1 (inflates the upstream material)."},"allocation":{"type":"object","properties":{"method":{"type":"string","enum":["mass","economic"]},"share":{"type":"number"},"product":{"type":"object","properties":{"mass":{"type":"number"},"price":{"type":"number"},"quantity":{"type":"number"}}},"co_products":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"mass":{"type":"number"},"price":{"type":"number"},"quantity":{"type":"number"}}}},"justification":{"type":"string"}},"description":"Co-product allocation of the process: `method` (mass | economic), an explicit `share` 0..1, or the mass / price / quantity of the product and of each co-product, plus a `justification`."}}},"description":"Explicit manufacturing processes (rarely needed)."},"direct_emissions":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Label of the emission."},"value":{"type":"number","description":"Emission in kgCO2e."},"unit":{"type":"string","description":"Unit of the value."}}},"description":"Direct emissions declared by the plant."},"assembly":{"anyOf":[{"type":"object","properties":{"category":{"type":"string","description":"electronics | mechanical | textile_confection | furniture | generic (default energy per kg)."},"energy_kwh":{"type":"number","description":"Measured total assembly energy (kWh): preferred over the default."},"energy_country":{"type":"string","description":"Country of the assembly electricity. Default: production country."},"contract_ef":{"type":"number","description":"Contractual electricity factor, kgCO2e/kWh (market-based)."},"renewable_share":{"type":"number","description":"Renewable share of the assembly electricity, 0..1."},"loss_rate":{"type":"number","description":"Loss rate of the assembly, 0..1."}}},{"type":"boolean","const":false}],"description":"Final assembly of the components: one line computed on the mass of the non-packaging components. Modelled by default (category `generic`); pass `false` to leave it out (single-material product, assembly outside your boundary)."}},"description":"Manufacturing. Present (even `{}`): the engine derives the processes from the gap between the matched factors and the target states. Absent: created with the default assembly (`{ \"assembly\": {} }`), so the footprint always includes the step that combines the components."},"A4_distribution":{"type":"object","properties":{"temperature_regime":{"type":"string","description":"Temperature regime of the downstream platforms: ambient | mixed | controlled."},"shipped_mass_kg":{"type":"number","description":"Known shipped mass (product + packaging, kg). Default: reference-flow mass plus shipping packaging."},"destinations":{"maxItems":10,"type":"array","items":{"type":"object","properties":{"location":{"type":"string","minLength":1,"description":"Market country."},"sales_share":{"type":"number","description":"Share of sales on this market (%), normalized server-side."},"legs":{"type":"array","items":{"type":"object","properties":{"mode":{"type":"string","description":"road | sea | rail | air | inland_waterway."},"distance_km":{"type":"number","description":"Distance of the leg in km. Filled from distance tables when absent."},"origin":{"type":"string","description":"Origin country or region of the leg."},"dest":{"type":"string","description":"Destination country or region of the leg."},"load_factor":{"type":"number","description":"Optional load factor."},"material_ref":{"type":["string","number"],"description":"A1 material carried by this leg: index in `A1_materials` (preferred) or material name. Absent: the leg carries the whole bill of materials."},"material_refs":{"type":"array","items":{"type":["string","number"]},"description":"Several A1 materials carried by this leg (indexes or names)."}}},"description":"Explicit plant → market legs (same fields as A2). Absent: default routing."},"nodes":{"type":"array","items":{"type":"object","properties":{"node_type":{"type":"string","description":"Logistics platform type (warehouse, transhipment, storage…)."},"temperature_regime":{"type":"string","description":"ambient | mixed | controlled (cold chain)."}}},"description":"Logistics platforms. Absent: one default distribution warehouse; `[]`: none."}},"required":["location","sales_share"]},"description":"Sales markets (1 to 10), weighted by their share of sales."},"packaging":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"description":"Packaging label (the matching query)."},"quantity":{"type":"number","description":"Quantity of packaging."},"unit":{"type":"string","description":"Unit (kg, g…)."},"fe_objectID":{"type":"string","description":"Pin an emission factor (no AI matching)."},"description":{"type":"string","description":"Longer description, used as a fallback query."}},"required":["name","quantity","unit"]},"description":"Shipping packaging (secondary / tertiary: film, cartons, pallets). The sales packaging goes in A1 with `role: \"packaging\"`."}},"description":"Optional downstream distribution (plant → markets, cradle-to-shelf). Requires `production.country`. Reported in the `a4_distribution` extension; the sealed PACT footprint stays cradle-to-gate."},"B_use":{"type":"object","properties":{"declared_negligible":{"type":"object","properties":{"justification":{"type":"string","minLength":20,"description":"Required justification (20 characters minimum), copied to the deliverable."}},"required":["justification"],"description":"Passive product: no consumption during use. Exclusive with `profile`."},"profile":{"type":"object","properties":{"lifetime":{"type":"object","properties":{"value":{"type":"number"},"unit":{"type":"string"}},"required":["value","unit"],"description":"Declared service life `{ value, unit }` (informative, not used in the computation)."},"cycles":{"type":"object","properties":{"count":{"type":"number"},"unit":{"type":"string"}},"required":["count"],"description":"Total cycles over the whole life `{ count, unit }` (not per year): multiplies the per-cycle consumption."}},"description":"Use profile: lifetime and total number of cycles."},"use_locations":{"maxItems":10,"type":"array","items":{"type":"object","properties":{"location":{"type":"string","minLength":1,"description":"Country of use (drives the electricity mix)."},"share":{"type":"number","description":"Share of use in this country (%), normalized server-side."}},"required":["location","share"]},"description":"Countries of use (1 to 10) with shares. Absent: inherited from the A4 markets; without either, electricity is not computed."},"energy":{"type":"array","items":{"type":"object","properties":{"carrier":{"type":"string","minLength":1,"description":"Energy carrier (\"electricity\", \"natural gas\"…)."},"quantity_per_cycle":{"type":"number","description":"Quantity consumed per cycle."},"unit":{"type":"string","description":"Unit (kWh, MJ…)."},"fe_objectID":{"type":"string","description":"Pin an emission factor for a non-electricity carrier."},"description":{"type":"string","description":"Longer description, used as a fallback query."},"contract_ef":{"type":"number","description":"Contractual electricity factor, kgCO2e/kWh (overrides the grid mix)."},"renewable_share":{"type":"number","description":"Renewable share of the electricity, 0..1."}},"required":["carrier","quantity_per_cycle","unit"]},"description":"Energy consumed per cycle. \"electricity\" uses the grid mix of each country of use; other carriers are matched like materials."},"consumables":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"description":"Consumable label (the matching query)."},"quantity_per_cycle":{"type":"number","description":"Quantity consumed per cycle."},"unit":{"type":"string","description":"Unit (L, kg…)."},"fe_objectID":{"type":"string","description":"Pin an emission factor (no AI matching)."},"description":{"type":"string","description":"Longer description, used as a fallback query."}},"required":["name","quantity_per_cycle","unit"]},"description":"Consumables per cycle (water, detergent, filters…), matched like materials."},"grid_decarbonization":{"type":"number","description":"What-if lever, 0..1: projected decarbonization of the grid applied to the electricity factor. Absent (baseline): current mix held constant."}},"description":"Optional use phase: either `declared_negligible` (passive product) or `profile` + `energy` / `consumables`. Reported in the `use_phase` extension."},"C_end_of_life":{"type":"object","properties":{"markets":{"maxItems":10,"type":"array","items":{"type":"object","properties":{"location":{"type":"string","minLength":1,"description":"End-of-life country."},"share":{"type":"number","description":"Share treated in this country (%), normalized server-side."}},"required":["location","share"]},"description":"End-of-life markets (1 to 10) with shares. Absent: the A4 markets, else world average."},"treatment_mix":{"type":"object","properties":{"recycling":{"type":"number"},"incineration":{"type":"number"},"landfill":{"type":"number"},"compost":{"type":"number"}},"description":"Product-level treatment mix override: `recycling` / `incineration` / `landfill` / `compost` in %, normalized server-side."},"materials":{"type":"array","items":{"type":"object","properties":{"material_ref":{"type":["string","number"],"description":"A1 material: index, `A1[i]` or exact material name."},"family":{"type":"string","description":"Force the end-of-life family: plastics | metals_ferrous | metals_non_ferrous | paper_cardboard | glass | wood | textile | electronics | mineral | organics | other."},"treatment_mix":{"type":"object","properties":{"recycling":{"type":"number"},"incineration":{"type":"number"},"landfill":{"type":"number"},"compost":{"type":"number"}},"description":"Treatment mix of this material (replaces the country mix)."}},"required":["material_ref"]},"description":"Per-material overrides."}},"description":"Optional end of life, largely automatic: present (even `{}`) = the product mass is split by material family × market and treated with each country’s statistical mix (recycling in cut-off). Reported in the `end_of_life` extension."}},"description":"Life-cycle stages. A1–A3 make the cradle-to-gate footprint; A4 adds distribution (cradle-to-shelf); B and C are reported separately, and both together make it cradle-to-grave."},"overrides":{"type":"array","items":{"type":"object","properties":{"stage":{"type":"string"},"line_ref":{"type":"string"},"field":{"type":"string"},"value":{},"justification":{"type":"string"},"by":{"type":"string"}},"required":["stage","line_ref","field","value","justification","by"]},"description":"Audited overrides of computed values: `stage`, `line_ref`, `field`, `value`, `justification`, `by`."}},"required":["functional_unit","stages"],"description":"The product to assess. Minimum: `functional_unit` and `stages.A1_materials` (name, quantity, unit, location). Every other block is optional, with documented defaults; gaps are reported in `missing_fields[]`."},"language":{"default":"en","type":"string","enum":["fr","en"],"description":"Language of labels and messages (`fr` or `en`, default `en`)."},"context":{"type":"string","description":"Same matching context as at compute."},"buyer_material_code":{"type":"string","description":"Optional buyer material code, copied to the deliverable metadata."},"verification":{"type":"object","properties":{"attested_by":{"type":"string","description":"Name of the attesting person."},"role":{"type":"string","description":"executive | third_party."},"statement":{"type":"string","description":"Attestation statement."},"attested_at":{"type":"string","description":"Date of the attestation (ISO 8601)."},"third_party":{"type":"boolean","description":"True for an independent third party."}},"description":"Optional attestation, sealed inside the deliverable (not verified by the engine)."},"infer_fe_states":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true","false"]}],"description":"Optional, default false: let the AI infer the transformation state of untagged factors (better A3; capped data quality, flagged `ai_estimate`). Frozen per session: pass the same value at finalize."},"infer_numeric":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["true","false"]}],"description":"Optional, default false: let the AI estimate a missing leg distance or process energy within hard bounds (capped data quality, flagged `ai_estimate`). Never an emission factor. Frozen per session: pass the same value at finalize."},"product_name":{"type":"string","minLength":1,"maxLength":200,"description":"Name of the product in the Ecodex /pcf page (default \"PCF via API\"). Kept on later finalizes unless sent again."}},"required":["product_model"],"additionalProperties":false},"examples":{"minimal":{"summary":"Same model as the last compute, named for /pcf","value":{"language":"en","product_name":"Steel bracket SB-200","product_model":{"functional_unit":{"amount":1,"unit":"unit"},"production":{"country":["France"]},"stages":{"A1_materials":[{"name":"Hot-rolled steel","quantity":2.4,"unit":"kg","location":"Germany"},{"name":"Polypropylene","quantity":0.6,"unit":"kg","location":"China"}]}}}}}}}},"responses":{"200":{"description":"The sealed deliverable and the job it is stored in.","content":{"application/json":{"schema":{"type":"object","required":["data","meta","warnings"],"properties":{"data":{"$ref":"#/components/schemas/PcfFinalized"},"meta":{"$ref":"#/components/schemas/Meta"},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/Warning"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/PcfIncomplete"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"},"504":{"$ref":"#/components/responses/EngineTimeout"}}}},"/pcf/jobs/{job_id}":{"get":{"operationId":"getPcfJob","tags":["PCF"],"summary":"Read a stored PCF (beta)","description":"Beta. Read a stored PCF job: its sealed deliverable (PACT JSON, `content_hash`) and its public page, if any. Free.","parameters":[{"name":"job_id","in":"path","required":true,"description":"The `job_id` returned by the finalize (also listed in the Ecodex /pcf page).","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The job.","content":{"application/json":{"schema":{"type":"object","required":["data","meta","warnings"],"properties":{"data":{"$ref":"#/components/schemas/PcfJob"},"meta":{"$ref":"#/components/schemas/Meta"},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/Warning"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/pcf/jobs/{job_id}/publish":{"post":{"operationId":"publishPcf","tags":["PCF"],"summary":"Publish the public page (beta)","description":"Beta. Publish the job’s public page (`/p/<token>`, no Ecodex account needed to view it): footprint, composition level and optional sheet, never the emission factors, their sources or values. The deliverable must be finalized. Free.","parameters":[{"name":"job_id","in":"path","required":true,"description":"The `job_id` returned by the finalize (also listed in the Ecodex /pcf page).","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"confirmed":{"type":"boolean","const":true,"description":"Must be `true`: you confirm the footprint may be shown on a public page reachable by anyone with the link or the QR code."},"public_profile":{"type":"object","properties":{"description":{"type":"string","maxLength":400,"description":"Short product description (400 characters max)."},"recycled_content_pct":{"type":"number","minimum":0,"maximum":100,"description":"Recycled content of the product, 0 to 100."},"eol_instructions":{"type":"string","maxLength":400,"description":"End-of-life instructions for the end user (400 characters max)."},"composition_level":{"type":"string","enum":["none","families","detail"],"description":"Composition shown: none | families (material families, default) | detail (each material)."},"show_equivalences":{"type":"boolean","description":"Show everyday equivalences of the footprint (default true)."}},"additionalProperties":false,"description":"Optional public sheet. Replaces the stored one when sent."}},"required":["confirmed"],"additionalProperties":false},"examples":{"default":{"summary":"Default sheet","value":{"confirmed":true}},"with_sheet":{"summary":"With a public sheet","value":{"confirmed":true,"public_profile":{"description":"Wall bracket in hot-rolled steel, made in France.","recycled_content_pct":30,"composition_level":"families"}}}}}}},"responses":{"200":{"description":"The public page.","content":{"application/json":{"schema":{"type":"object","required":["data","meta","warnings"],"properties":{"data":{"$ref":"#/components/schemas/Publication"},"meta":{"$ref":"#/components/schemas/Meta"},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/Warning"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/pcf/jobs/{job_id}/republish":{"post":{"operationId":"republishPcf","tags":["PCF"],"summary":"Publish the latest deliverable as a new version (beta)","description":"Beta. Publish the latest deliverable of an already published job as a new version of the same page (same URL, same QR code). Free.","parameters":[{"name":"job_id","in":"path","required":true,"description":"The `job_id` returned by the finalize (also listed in the Ecodex /pcf page).","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"confirmed":{"type":"boolean","const":true,"description":"Must be `true`: you confirm the footprint may be shown on a public page reachable by anyone with the link or the QR code."},"public_profile":{"type":"object","properties":{"description":{"type":"string","maxLength":400,"description":"Short product description (400 characters max)."},"recycled_content_pct":{"type":"number","minimum":0,"maximum":100,"description":"Recycled content of the product, 0 to 100."},"eol_instructions":{"type":"string","maxLength":400,"description":"End-of-life instructions for the end user (400 characters max)."},"composition_level":{"type":"string","enum":["none","families","detail"],"description":"Composition shown: none | families (material families, default) | detail (each material)."},"show_equivalences":{"type":"boolean","description":"Show everyday equivalences of the footprint (default true)."}},"additionalProperties":false,"description":"Optional public sheet. Replaces the stored one when sent."}},"required":["confirmed"],"additionalProperties":false},"examples":{"default":{"summary":"Default sheet","value":{"confirmed":true}},"with_sheet":{"summary":"With a public sheet","value":{"confirmed":true,"public_profile":{"description":"Wall bracket in hot-rolled steel, made in France.","recycled_content_pct":30,"composition_level":"families"}}}}}}},"responses":{"200":{"description":"The public page.","content":{"application/json":{"schema":{"type":"object","required":["data","meta","warnings"],"properties":{"data":{"$ref":"#/components/schemas/Publication"},"meta":{"$ref":"#/components/schemas/Meta"},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/Warning"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/pcf/jobs/{job_id}/revoke":{"post":{"operationId":"revokePcf","tags":["PCF"],"summary":"Take the public page offline (beta)","description":"Beta. Take the public page offline (the URL answers \"withdrawn\"). Publishing again later keeps the same URL. Free.","parameters":[{"name":"job_id","in":"path","required":true,"description":"The `job_id` returned by the finalize (also listed in the Ecodex /pcf page).","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","maxLength":500,"description":"Optional reason, kept in the publication history (500 characters max)."}},"additionalProperties":false},"examples":{"reason":{"summary":"With a reason","value":{"reason":"Product discontinued"}}}}}},"responses":{"200":{"description":"The public page.","content":{"application/json":{"schema":{"type":"object","required":["data","meta","warnings"],"properties":{"data":{"$ref":"#/components/schemas/Publication"},"meta":{"$ref":"#/components/schemas/Meta"},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/Warning"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/pcf/jobs/{job_id}/qr.svg":{"get":{"operationId":"getPcfQrCode","tags":["PCF"],"summary":"QR code of the public page (beta)","description":"Beta. QR code (SVG) of the job’s active public page. Free.","parameters":[{"name":"job_id","in":"path","required":true,"description":"The `job_id` returned by the finalize (also listed in the Ecodex /pcf page).","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"SVG image.","content":{"image/svg+xml":{"schema":{"type":"string"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/usage":{"get":{"operationId":"getUsage","tags":["Usage"],"summary":"Metered usage of the workspace","description":"Metered usage of your workspace over a period, with the same formulas as the invoice. Euro amounts are null unless the workspace is billed in euros. Free.","parameters":[{"name":"from","in":"query","required":false,"description":"Start of the period (ISO 8601 date or date-time, inclusive). Default: start of the open billing window.","example":"2026-10-01","schema":{"type":"string"}},{"name":"to","in":"query","required":false,"description":"End of the period (ISO 8601, exclusive). Default: end of the open billing window.","example":"2026-11-01","schema":{"type":"string"}}],"responses":{"200":{"description":"Usage summary.","content":{"application/json":{"schema":{"type":"object","required":["data","meta","warnings"],"properties":{"data":{"$ref":"#/components/schemas/Usage"},"meta":{"$ref":"#/components/schemas/Meta"},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/Warning"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"API key `ecodex_sk_…` as a bearer token."},"apiKeyHeader":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key `ecodex_sk_…`."}},"schemas":{"Meta":{"type":"object","required":["request_id","api_version","billing"],"properties":{"request_id":{"type":"string","description":"Identifier of this request (also in the X-Request-Id header)."},"api_version":{"type":"string","description":"Dated version of the contract."},"engine":{"type":"object","description":"Engine that produced the result (AI endpoints only).","properties":{"name":{"type":"string"},"version":{"type":"string"},"model":{"type":"string"},"model_version":{"type":"string"},"degraded":{"type":"array","items":{"type":"string"},"description":"Pipeline stages that failed for this call (partial pool). Empty when nominal."}}},"billing":{"type":"object","properties":{"billable_units":{"type":"integer","description":"Units this call metered."},"unit":{"type":"string","enum":["search_call","match_item","pcf","none"],"description":"search_call | match_item (evaluated items) | pcf (PCF units: 1 per session up to 50 resolved lines, billed in euros or debited from credits) | none (nothing metered, e.g. finalize or a recompute of an already-billed session)."},"credits_debited":{"type":"integer","description":"Credits-mode workspaces only: what this call debited from the matching or PCF pool."}}}}},"Warning":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"search: mixed_units | mixed_scopes | mixed_sources | mixed_years | mixed_products | location_mismatch | no_high_confidence_match | degraded_retrieval. match: degraded_retrieval | llm_quota_exhausted | prep_degraded | error_items | scope_no_match. pcf: degraded | incomplete | deliverable_not_persisted."},"message":{"type":"string"}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id","details"],"properties":{"code":{"type":"string","enum":["invalid_request","invalid_api_key","insufficient_credits","spend_cap_reached","account_inactive","account_suspended","forbidden","factor_not_found","session_not_found","job_not_found","not_found","session_compute_cap","publication_conflict","pcf_incomplete","rate_limited","service_unavailable","llm_quota_exhausted","engine_timeout","internal_error"]},"message":{"type":"string"},"request_id":{"type":"string"},"details":{"type":"object","description":"invalid_request: `code` (validation_failed, missing_query, unknown_currency, context_required, duplicate_ids, sources_not_accessible…) and `fields[]` per invalid field. publication_conflict: `code` (not_finalized, already_published, not_published…). insufficient_credits: `pool`, `balance`, `required`. spend_cap_reached: `cap_cents`, `spent_cents`, `required_cents`, `currency`, `period_from`."}}}}},"SearchResult":{"type":"object","required":["object_id","name","fe","unit","source","scope","quality_score","confidence"],"properties":{"object_id":{"type":"string","description":"Factor identifier, readable with GET /v1/factors/{id}."},"name":{"type":"string"},"fe":{"type":["number","null"],"description":"Value (after display conversion when `currency` / `convert_to_unit` was set)."},"unit":{"type":"string","description":"Activity unit: `fe` is expressed in kgCO2e per this unit (e.g. \"kg\", \"kWh\", \"k€\"). After a display conversion, the converted unit."},"source_fe":{"type":"number","description":"Catalog value, present only when a conversion applied."},"source_unit":{"type":"string","description":"Catalog unit, present only when a conversion applied."},"source":{"type":"string"},"scope":{"type":"string"},"location":{"type":["string","null"]},"year":{"type":["integer","null"]},"data_type":{"type":"string"},"quality_score":{"type":"number","minimum":0,"maximum":1,"description":"Match quality, 0-1."},"confidence":{"type":"string","enum":["high","medium","low"],"description":"high ≥ 0.80, medium ≥ 0.50."},"reason":{"type":"string","description":"Short rationale, medium / low confidence only."},"country_variants":{"type":"array","description":"Present only with compare_countries=true.","items":{"type":"object","properties":{"location":{"type":"string"},"year":{"type":["integer","null"]},"fe":{"type":["number","null"]}}}},"cbam_benchmark":{"type":"object","description":"CBAM source only: EU free-allocation benchmark for the factor’s CN code."}}},"SearchData":{"type":"object","required":["query","results"],"properties":{"query":{"type":"string"},"context_applied":{"type":"string","enum":["applied","ignored_as_unrelated"]},"results":{"type":"array","items":{"$ref":"#/components/schemas/SearchResult"}}}},"FilterValues":{"type":"object","required":["filter","language","values","total_values"],"properties":{"filter":{"type":"string"},"language":{"type":"string","enum":["fr","en"]},"source_scope":{"type":["array","null"],"items":{"type":"string"}},"values":{"type":"array","items":{"type":"object","required":["value"],"properties":{"value":{"type":"string"},"count":{"type":"integer"},"has_access":{"type":"boolean","description":"facet=source: whether your workspace can read this source."},"licence_tier":{"type":"string","description":"facet=source: open | open_attribution | copyleft | restricted | licensed | unclear."},"licence":{"type":"string"},"attribution":{"type":"string"},"usage_rights_url":{"type":"string"}}}},"total_values":{"type":"integer"},"note":{"type":"string"}}},"Factor":{"type":"object","required":["object_id","origin","name","fe","activity_unit","source"],"properties":{"object_id":{"type":"string"},"origin":{"type":"string","enum":["catalog","workspace_private"],"description":"`workspace_private`: a factor of your workspace’s private database."},"name":{"type":"object","properties":{"fr":{"type":["string","null"]},"en":{"type":["string","null"]}}},"fe":{"type":["number","null"],"description":"kgCO2e per activity unit."},"activity_unit":{"type":"object","properties":{"fr":{"type":["string","null"]},"en":{"type":["string","null"]}}},"source":{"type":["string","null"]},"version":{"type":["string","null"]},"year":{"type":["number","null"]},"scope":{"type":"object","properties":{"fr":{"type":["string","null"]},"en":{"type":["string","null"]}}},"location":{"type":"object","properties":{"fr":{"type":["string","null"]},"en":{"type":["string","null"]}}},"sector":{"type":"object","properties":{"fr":{"type":["string","null"]},"en":{"type":["string","null"]}}},"sub_sector":{"type":"object","properties":{"fr":{"type":["string","null"]},"en":{"type":["string","null"]}}},"data_type":{"type":"object","properties":{"fr":{"type":["string","null"]},"en":{"type":["string","null"]}}},"uncertainty":{"type":["string","null"]},"comments":{"type":"object","properties":{"fr":{"type":["string","null"]},"en":{"type":["string","null"]}}},"ghg_breakdown":{"type":["object","null"],"description":"Per-gas breakdown when the source publishes it."},"lifecycle_breakdown":{"type":["array","null"],"description":"Per life-cycle stage values when the source publishes them."},"factor_lineage_key":{"type":["string","null"],"description":"Stable across versions of the same factor."},"versioned_key":{"type":["string","null"],"description":"Identifies this factor in this source version."},"source_link":{"type":["string","null"]}}},"MatchCandidate":{"type":"object","required":["object_id","name","fe","unit","source","quality_score"],"properties":{"object_id":{"type":"string","description":"Factor identifier: readable with GET /v1/factors/{id}, pinnable in a PCF (`fe_objectID`)."},"name":{"type":"string"},"fe":{"type":["number","null"],"description":"kgCO2e per `unit` (after display conversion when `config.currency` / `config.convert_to_unit` was set). Null when `restricted`."},"unit":{"type":"string","description":"Activity unit (e.g. \"kg\", \"kWh\", \"k€\")."},"source_fe":{"type":["number","null"],"description":"Catalog value, present only when a conversion applied."},"source_unit":{"type":"string","description":"Catalog unit, present only when a conversion applied."},"restricted":{"type":"boolean","description":"Present (true) when the value belongs to a source your workspace cannot read."},"source":{"type":"string"},"scope":{"type":"string","description":"Life-cycle boundary."},"location":{"type":["string","null"]},"sector":{"type":["string","null"]},"sub_sector":{"type":["string","null"]},"year":{"type":["integer","null"]},"quality_score":{"type":"number","minimum":0,"maximum":1,"description":"Match quality, 0-1 (high ≥ 0.75, medium ≥ 0.50)."},"reason":{"type":"string","description":"Short rationale of the AI scoring, when given."},"notes":{"type":"array","items":{"type":"string"},"description":"Server-side caps that lowered the confidence (old data, fallback model…)."}}},"MatchItem":{"type":"object","required":["id","status","confidence","match","alternatives"],"properties":{"id":{"type":"string","description":"Your item id."},"status":{"type":"string","enum":["matched","unmatched","error"],"description":"unmatched: evaluated, no acceptable factor (billed). error: NOT evaluated (not billed): re-send the item."},"error_code":{"type":"string","description":"status=error only: timeout | rate_limited | llm_error | llm_quota_exhausted | internal."},"prep_degraded":{"type":"boolean","description":"Matched on the raw label (AI label preparation unavailable): review first."},"query_used":{"type":"string","description":"Query actually sent to the retrieval."},"confidence":{"type":["string","null"],"enum":["high","medium","low",null]},"match":{"oneOf":[{"$ref":"#/components/schemas/MatchCandidate"},{"type":"null"}]},"alternatives":{"type":"array","items":{"$ref":"#/components/schemas/MatchCandidate"},"description":"Other candidates of the matched item, best first."},"reason":{"type":"string","description":"unmatched only: why the best candidate was rejected."}}},"MatchData":{"type":"object","required":["items","summary"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/MatchItem"}},"summary":{"type":"object","properties":{"total":{"type":"integer"},"matched":{"type":"integer"},"unmatched":{"type":"integer"},"errors":{"type":"integer","description":"Items not evaluated (not billed)."},"high_confidence":{"type":"integer"},"medium_confidence":{"type":"integer"},"avg_score":{"type":"number"}}},"conversion":{"type":["object","null"],"properties":{"currency":{"type":["string","null"]},"convert_to_unit":{"type":["string","null"]}}}}},"PcfResult":{"type":"object","required":["session_id","pcf","lines","missing_fields","incomplete"],"description":"Beta: additive fields can appear.","properties":{"session_id":{"type":"string","description":"Send it back on the next compute and at finalize (it can differ from the one sent)."},"pcf":{"type":"object","description":"Totals in kgCO2e per functional unit: `total`, `total_a1_a3` (cradle-to-gate), `by_stage`, uncertainty when computed."},"lines":{"type":"array","items":{"type":"object"},"description":"One line per computed activity (material, leg, process…), with its factor, emissions and data quality."},"missing_fields":{"type":"array","items":{"type":"object"},"description":"Gaps of the model: `path`, `reason`, `suggestion`; `blocking: true` = the line is excluded from the total."},"incomplete":{"type":"boolean","description":"True when at least one line is excluded from the total: the total is partial and finalize is refused."},"assumptions":{"type":"array","items":{"type":"object"},"description":"Defaults the engine applied, with their provenance."},"engine":{"type":"object","description":"Engine version, degraded stages, AI usage."},"material_library":{"type":"object","description":"Present when your workspace’s validated factor library served a line."}}},"PcfFinalized":{"allOf":[{"$ref":"#/components/schemas/PcfResult"},{"type":"object","properties":{"job_id":{"type":"string","description":"The stored PCF job (GET /v1/pcf/jobs/{job_id}). Absent only with the `deliverable_not_persisted` warning."},"deliverable":{"type":"object","description":"Sealed deliverable: `pact` (PACT-aligned footprint), `content_hash` (sha-256), `signature.status = \"unsigned\"`."}}}]},"Publication":{"type":"object","properties":{"job_id":{"type":"string"},"publication":{"type":["object","null"],"properties":{"url":{"type":"string","description":"Public page (https://getecodex.com/p/<token>)."},"version":{"type":["integer","null"]},"published_at":{"type":["string","null"]},"revoked_at":{"type":["string","null"]},"active":{"type":"boolean"}}},"version":{"type":["integer","null"],"description":"publish / republish: version just published."},"already_revoked":{"type":"boolean","description":"revoke: the page was already offline."}}},"PcfJob":{"type":"object","required":["job_id","status"],"properties":{"job_id":{"type":"string"},"name":{"type":["string","null"]},"status":{"type":"string","enum":["draft","processing","computed","finalized","error"]},"session_id":{"type":["string","null"]},"total_kg_co2e":{"type":["number","null"]},"created_at":{"type":"string"},"updated_at":{"type":"string"},"deliverable":{"type":["object","null"],"description":"The sealed deliverable (PACT JSON, content_hash), null until finalized."},"publication":{"type":["object","null"],"properties":{"url":{"type":"string"},"version":{"type":"integer"},"published_at":{"type":"string"},"revoked_at":{"type":["string","null"]},"active":{"type":"boolean"}}}}},"Usage":{"type":"object","description":"billing_mode, currency, period_from / period_to, total_calls, tools[] (calls, exempt_calls, match_items, pcf_units, pcf_lines, credits_debited, amount_cents; pcf_lines / pcf_finalizes = calls metered before the per-PCF pricing), invoice_lines, total_amount_cents, pricing, unbilled.","additionalProperties":true}},"responses":{"BadRequest":{"description":"Invalid request (`invalid_request`). Not billed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"Missing, invalid, revoked or expired API key (`invalid_api_key`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PaymentRequired":{"description":"Insufficient credits (`insufficient_credits`: details.pool, balance, required) or monthly spend cap reached (`spend_cap_reached`: details.cap_cents, spent_cents, required_cents, currency, period_from; this call could take the calendar month above the cap). Nothing executed, not billed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"Workspace access inactive or suspended (`account_inactive`, `account_suspended`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Unknown or inaccessible resource (`factor_not_found`, `session_not_found`, `job_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"PCF session cap reached (`session_compute_cap`) or publication state conflict (`publication_conflict`, see details.code). Not billed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PcfIncomplete":{"description":"Lines are excluded from the total (`pcf_incomplete`): fix them, recompute, then finalize. Not billed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"EngineTimeout":{"description":"The engine did not answer within 100 s (`engine_timeout`). Not billed.","headers":{"Retry-After":{"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"TooManyRequests":{"description":"Rate limit reached (`rate_limited`). Not billed.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait (upper bound)."},"RateLimit-Limit":{"schema":{"type":"integer"}},"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServiceUnavailable":{"description":"Temporary engine outage (`service_unavailable`, `llm_quota_exhausted`). Not billed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InternalError":{"description":"Internal error (`internal_error`). Quote the request_id to support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}