LCA Provider Integration Guide

Step-by-step walkthrough for Life Cycle Assessment (LCA) providers who need to pull product data, supply chain structure, company energy sources, and material composition from the Retraced Public API.

Setup

BASE_URL="https://publicapi.retraced.com/api/v2"

# API key provided by the brand you are integrating with
API_KEY="your-api-key"

All examples use curl. The API key is passed via the companyapikey header.

Important: If your API key contains $ characters, use single quotes around its value to prevent the shell from interpreting it as a variable:

-H 'companyapikey: $2b$10$...'   # correct — single quotes
-H "companyapikey: $2b$10$..."   # wrong — shell expands $2b to empty string → 401

All responses use the standard envelope: { "metadata": { "success": true }, "data": ... }. Errors return metadata.success: false plus a machine-readable code field.


Overview

The typical LCA data collection workflow involves four steps:

  1. Fetch the product hierarchy — styles, variants, and SKUs
  2. Fetch the supply chain — which facilities produce the product and at which tier
  3. Fetch energy source data — ESG section 2.1.6.1 for each supplier company
  4. Fetch material composition — material headers and material lines for the product

The example below uses a single product: Summer Dress 2024 (style style_01KMZMMMW8JA3S1RVX1PS8E19S), one variant in Blue (variant_01KN6YZNVKNBN1TJ4MR9T7FFD0), and one SKU in size S (sku_01KN6Z1JB710ANAN120JH5BBB0).


Step 1 — Fetch the product hierarchy

1a. List all styles

Retrieve the top-level product catalogue. Use nameOrCode to search for a specific product.

curl "$BASE_URL/styles?level=style&page=1&limit=50" \
  -H "companyapikey: $API_KEY"

Response (200):

{
  "metadata": {
    "success": true,
    "pagination": { "page": 1, "limit": 50, "total": 7, "totalPages": 1 }
  },
  "data": [
    {
      "id": "style_01KMZMMMW8JA3S1RVX1PS8E19S",
      "forCompanyId": "G3JITT4AM9L",
      "name": "Summer Dress 2024",
      "codes": [{ "codeId": "ERP_ID", "codeValue": "DRS-2024-001" }],
      "description": "Lightweight summer dress in organic cotton",
      "gender": "female",
      "styleLevelId": "APPAREL_END_CONSUMER_FINISHED_GOODS",
      "styleTypeId": "Y0RL9LGNUWOL9RXB07",
      "brands": [],
      "departments": [],
      "seasons": [],
      "suppliers": [],
      "mainSupplier": null,
      "isComponent": false,
      "isArchived": false,
      "weight": null,
      "createdAt": "2026-03-30T15:08:48.904Z",
      "updatedAt": "2026-03-30T15:08:49.545Z"
    }
  ]
}
STYLE_ID="style_01KMZMMMW8JA3S1RVX1PS8E19S"

Weight for functional units: weight is { "value": number, "unit": "GRAM" | "KILOGRAM" | ... } or null when the brand hasn't entered it. Variant and SKU responses additionally expose isWeightAvailable, and inherit the parent's weight when their own is not set — if you need a mass for your functional unit and the SKU returns null, check the parent style.

1b. List variants for the style

curl "$BASE_URL/styles?level=variant&styleId=$STYLE_ID&page=1&limit=50" \
  -H "companyapikey: $API_KEY"

Response (200):

{
  "metadata": {
    "success": true,
    "pagination": { "page": 1, "limit": 50, "total": 1, "totalPages": 1 }
  },
  "data": [
    {
      "id": "variant_01KN6YZNVKNBN1TJ4MR9T7FFD0",
      "forCompanyId": "G3JITT4AM9L",
      "name": "Summer Dress 2024 — Blue",
      "codes": [{ "codeId": "ERP_ID", "codeValue": "DRS-2024-001-BLU" }],
      "description": "Lightweight summer dress in organic cotton",
      "gender": "female",
      "styleLevelId": "APPAREL_END_CONSUMER_FINISHED_GOODS",
      "styleTypeId": "Y0RL9LGNUWOL9RXB07",
      "suppliers": [],
      "mainSupplier": null,
      "isComponent": false,
      "isArchived": false,
      "weight": null,
      "isWeightAvailable": false,
      "createdAt": "2026-04-02T11:24:17.139Z",
      "updatedAt": "2026-04-02T11:24:39.088Z"
    }
  ]
}
VARIANT_ID="variant_01KN6YZNVKNBN1TJ4MR9T7FFD0"

1c. List SKUs for the variant

curl "$BASE_URL/styles?level=sku&variantId=$VARIANT_ID&page=1&limit=50" \
  -H "companyapikey: $API_KEY"

Response (200):

{
  "metadata": {
    "success": true,
    "pagination": { "page": 1, "limit": 50, "total": 7, "totalPages": 1 }
  },
  "data": [
    {
      "id": "sku_01KN6Z1JB710ANAN120JH5BBB0",
      "forCompanyId": "G3JITT4AM9L",
      "name": "Summer Dress 2024 — Blue — S",
      "codes": [{ "codeId": "EAN_GTIN_13", "codeValue": "4006381333931" }],
      "description": "Lightweight summer dress in organic cotton",
      "gender": "female",
      "styleLevelId": "APPAREL_END_CONSUMER_FINISHED_GOODS",
      "styleTypeId": "Y0RL9LGNUWOL9RXB07",
      "suppliers": [],
      "mainSupplier": null,
      "isComponent": false,
      "isArchived": false,
      "weight": null,
      "isWeightAvailable": false,
      "createdAt": "2026-04-02T11:25:19.079Z",
      "updatedAt": "2026-04-02T11:25:52.639Z"
    }
  ]
}

Note: Supply chain and material data can be attached at the style, variant, or SKU level — and child products inherit from their parent when they have nothing of their own (see the notes in Steps 2 and 4). In this example we continue at the style level using STYLE_ID; substitute VARIANT_ID or SKU_ID to work at a finer granularity.


Step 2 — Fetch the supply chain

Retrieve the supplier network for the product. Each node in the response represents a facility, grouped by production process and tier.

The endpoint accepts a style, variant, or SKU ID. If the product you query has no supply chain of its own, the platform automatically falls back to the closest ancestor's active supply chain — so querying a SKU returns the style's chain when the brand mapped at style level.

curl "$BASE_URL/supply-chains/$STYLE_ID" \
  -H "companyapikey: $API_KEY"

Response (200):

{
  "metadata": { "success": true },
  "data": {
    "productId": "style_01KMZMMMW8JA3S1RVX1PS8E19S",
    "productName": "Summer Dress 2024",
    "nodesByProductionProcesses": [
      {
        "productionProcessId": "MANUFACTURING",
        "productionProcessName": "Manufacturing",
        "tier": "T2",
        "nodes": [
          {
            "tier": "T2",
            "companyId": "G3JITT4AM9L",
            "companyName": "Developers' CMT & Fabric",
            "country": "DK",
            "coordinates": { "latitude": 55.681986, "longitude": 12.600743 },
            "address": {
              "line1": "Ekvipagemestervej 10",
              "line2": null,
              "city": "København",
              "zipCode": "1438",
              "country": "DK"
            },
            "facilityProcesses": ["Cut make trim (CMT)"],
            "productionProcess": "Manufacturing"
          }
        ]
      },
      {
        "productionProcessId": "SOURCING_AND_IMPORT",
        "productionProcessName": "Sourcing and import",
        "tier": "T1",
        "nodes": [
          {
            "tier": "T1",
            "companyId": "PZ86G3I3SXB",
            "companyName": "Creative Sourcing Co. Ltd.",
            "country": "CN",
            "coordinates": { "latitude": 31.322372, "longitude": 120.722366 },
            "address": {
              "line1": "Jiangsu",
              "line2": null,
              "city": null,
              "zipCode": null,
              "country": "CN"
            },
            "facilityProcesses": ["Sourcing agency"],
            "productionProcess": "Sourcing and import"
          }
        ]
      }
    ]
  }
}

Reading the response:

Collect the companyId values for the next step:

COMPANY_IDS=("G3JITT4AM9L" "PZ86G3I3SXB")

Step 3 — Fetch energy source data (ESG section 2.1.6.1)

For each supplier company from Step 2, retrieve their declared energy sources using the templateId=2.1.6.1 filter. This returns only data points belonging to the Energy sources impact area.

for COMPANY_ID in "${COMPANY_IDS[@]}"; do
  echo "=== ESG Energy Sources: $COMPANY_ID ==="
  curl "$BASE_URL/companies/$COMPANY_ID/esg?templateId=2.1.6.1" \
    -H "companyapikey: $API_KEY"
  echo
done

Example response (200):

{
  "metadata": { "success": true },
  "data": {
    "companyId": "G3JITT4AM9L",
    "environmental": {
      "sectionId": "2.1",
      "name": "Environmental",
      "totalDataPoints": 4,
      "answeredDataPoints": 3,
      "dataPoints": [
        {
          "retracedId": "energy_source_grid",
          "name": "Share of grid electricity",
          "description": "Percentage of total energy consumption sourced from the national grid",
          "impactArea": "Energy sources",
          "impactAreaId": "2.1.6.1",
          "templateId": "2.1.6.1",
          "value": 62,
          "unit": "%",
          "year": 2023
        },
        {
          "retracedId": "energy_source_solar",
          "name": "Share of on-site solar energy",
          "description": "Percentage of total energy consumption generated from on-site solar panels",
          "impactArea": "Energy sources",
          "impactAreaId": "2.1.6.1",
          "templateId": "2.1.6.1",
          "value": 28,
          "unit": "%",
          "year": 2023
        },
        {
          "retracedId": "energy_source_renewable_cert",
          "name": "Share of renewable energy certificates (RECs)",
          "description": "Percentage of electricity consumption covered by renewable energy certificates",
          "impactArea": "Energy sources",
          "impactAreaId": "2.1.6.1",
          "templateId": "2.1.6.1",
          "value": 10,
          "unit": "%",
          "year": 2023
        },
        {
          "retracedId": "energy_source_fossil",
          "name": "Share of fossil fuel energy",
          "description": "Percentage of total energy consumption sourced from fossil fuels",
          "impactArea": "Energy sources",
          "impactAreaId": "2.1.6.1",
          "templateId": "2.1.6.1",
          "value": null,
          "unit": "%",
          "year": null
        }
      ]
    },
    "social": {
      "sectionId": "2.2",
      "name": "Social",
      "totalDataPoints": 0,
      "answeredDataPoints": 0,
      "dataPoints": []
    },
    "governance": {
      "sectionId": "2.3",
      "name": "Governance",
      "totalDataPoints": 0,
      "answeredDataPoints": 0,
      "dataPoints": []
    }
  }
}

How to read it:

ESG filter semantics

All filters combine with AND:

Query param Matching Example
templateId Exact match on the template/section ID templateId=2.1.6.1 (Energy sources)
retracedId Exact match on a specific data point retracedId=energy_source_grid
name Case-insensitive partial match on the data point name name=solar
impactArea Case-insensitive partial match on the impact area label impactArea=energy

When a company returns 404

GET /companies/:id/esg responds 404 company_not_found when the company has no ESG profile visible to you — either the ID is wrong, or the supplier hasn't shared a company profile with your network. There is no separate 403; missing access looks identical to a missing company. Handle this per supplier and continue with the rest (the collection script below does).


Step 4 — Fetch material composition

Material composition is structured in two layers: material headers (a named composition, e.g. "Shell Fabric") and material lines (the individual fibres/materials, organized as a tree). The full write model — including inheritance and copy-on-write — is covered in the Materials Guide; for LCA collection you only need to read.

4a. List material headers for the product

styleId accepts a style, variant, or SKU ID. Children automatically hold links to compositions created on their parent, so querying a SKU also surfaces the style's composition.

curl "$BASE_URL/material-headers?styleId=$STYLE_ID&page=1&limit=50" \
  -H "companyapikey: $API_KEY"

Response (200):

{
  "metadata": {
    "success": true,
    "pagination": { "page": 1, "limit": 50, "total": 1, "totalPages": 1 }
  },
  "data": [
    {
      "id": "mtrialhdr_01KTSC4N9GE3Q0V2J6M8R1WXYZ",
      "name": "Shell",
      "forCompanyId": "G3JITT4AM9L",
      "description": "Outer body material composition",
      "link": {
        "styleId": "style_01KMZMMMW8JA3S1RVX1PS8E19S",
        "isMainMaterial": true,
        "isOwner": true
      },
      "createdAt": "2026-03-30T15:10:00.000Z",
      "createdByUserId": "user-1",
      "createdByCompanyId": "G3JITT4AM9L",
      "updatedAt": "2026-03-30T15:10:00.000Z",
      "updatedByUserId": "user-1",
      "updatedByCompanyId": "G3JITT4AM9L"
    }
  ]
}

A product can have several headers (shell, lining, trims). link.isMainMaterial marks the product's primary composition — usually the one you want for the LCA. link.isOwner tells you whether this product owns the composition or inherits it from its parent.

MATERIAL_HEADER_ID="mtrialhdr_01KTSC4N9GE3Q0V2J6M8R1WXYZ"

4b. List material lines for the header

Add include=rawMaterials,countriesOfOrigin to resolve the raw material catalog entries and country details in the same call.

curl "$BASE_URL/material-lines?materialHeaderId=$MATERIAL_HEADER_ID&include=rawMaterials,countriesOfOrigin&page=1&limit=50" \
  -H "companyapikey: $API_KEY"

Response (200):

{
  "metadata": {
    "success": true,
    "pagination": { "page": 1, "limit": 50, "total": 2, "totalPages": 1 }
  },
  "data": [
    {
      "id": "mtrialln_01KTSC5A2B3C4D5E6F7G8H9J0K",
      "materialHeaderId": "mtrialhdr_01KTSC4N9GE3Q0V2J6M8R1WXYZ",
      "rawMaterialId": "COTTON_BALE",
      "percentage": 95,
      "weights": 120,
      "weightUnit": "GSM",
      "parentId": null,
      "hierarchy": 0,
      "position": 100,
      "countriesOfOrigin": ["IN"],
      "createdAt": "2026-03-30T15:10:00.000Z",
      "rawMaterial": {
        "id": "COTTON_BALE",
        "name": "Cotton bale",
        "category": "NATURAL_MATERIAL",
        "isRecycled": false,
        "isSynthetic": false
      },
      "countries": [
        {
          "countryCode": "IN",
          "countryName": "India",
          "region": "Asia"
        }
      ]
    },
    {
      "id": "mtrialln_01KTSC5A2B3C4D5E6F7G8H9J1L",
      "materialHeaderId": "mtrialhdr_01KTSC4N9GE3Q0V2J6M8R1WXYZ",
      "rawMaterialId": "ELASTANE_FIBER",
      "percentage": 5,
      "weights": null,
      "weightUnit": null,
      "parentId": null,
      "hierarchy": 0,
      "position": 200,
      "countriesOfOrigin": ["DE"],
      "createdAt": "2026-03-30T15:10:01.000Z",
      "rawMaterial": {
        "id": "ELASTANE_FIBER",
        "name": "Elastane fiber",
        "category": "SYNTHETIC_MATERIAL",
        "isRecycled": false,
        "isSynthetic": true
      },
      "countries": [
        {
          "countryCode": "DE",
          "countryName": "Germany",
          "region": "Europe"
        }
      ]
    }
  ]
}

How to read the lines:


Full LCA Collection Script

Complete bash script that collects all four data sets for a given product ID.

#!/usr/bin/env bash
set -euo pipefail

BASE_URL="https://publicapi.retraced.com/api/v2"
API_KEY="your-api-key"
STYLE_ID="style_01KMZMMMW8JA3S1RVX1PS8E19S"

api_get() { curl -s "$BASE_URL$1" -H "companyapikey: $API_KEY"; }

echo "=== Step 1a: List styles ==="
api_get "/styles?level=style&page=1&limit=50" | python3 -m json.tool

echo "=== Step 1b: List variants ==="
api_get "/styles?level=variant&styleId=$STYLE_ID&page=1&limit=50" | python3 -m json.tool

echo "=== Step 1c: List SKUs (first variant — adjust as needed) ==="
VARIANT_ID=$(api_get "/styles?level=variant&styleId=$STYLE_ID&page=1&limit=1" \
  | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['data'][0]['id'])")
api_get "/styles?level=sku&variantId=$VARIANT_ID&page=1&limit=50" | python3 -m json.tool

echo "=== Step 2: Supply chain ==="
SUPPLY_CHAIN=$(api_get "/supply-chains/$STYLE_ID")
echo "$SUPPLY_CHAIN" | python3 -m json.tool
echo "$SUPPLY_CHAIN" > supply_chain.json

echo "=== Step 3: ESG energy sources per supplier ==="
COMPANY_IDS=$(echo "$SUPPLY_CHAIN" \
  | python3 -c "
import sys, json
sc = json.load(sys.stdin)['data']
ids = {n['companyId'] for g in sc['nodesByProductionProcesses'] for n in g['nodes'] if n['companyId']}
print('\n'.join(ids))
")

for COMPANY_ID in $COMPANY_IDS; do
  echo "--- $COMPANY_ID ---"
  # Some suppliers have no shared ESG profile → 404 company_not_found; continue
  api_get "/companies/$COMPANY_ID/esg?templateId=2.1.6.1" | python3 -m json.tool || true
done

echo "=== Step 4a: Material headers ==="
MATERIAL_HEADERS=$(api_get "/material-headers?styleId=$STYLE_ID&page=1&limit=50")
echo "$MATERIAL_HEADERS" | python3 -m json.tool

echo "=== Step 4b: Material lines ==="
HEADER_IDS=$(echo "$MATERIAL_HEADERS" \
  | python3 -c "import sys, json; d=json.load(sys.stdin); print('\n'.join(h['id'] for h in d['data']))")

for HEADER_ID in $HEADER_IDS; do
  echo "--- Header: $HEADER_ID ---"
  api_get "/material-lines?materialHeaderId=$HEADER_ID&include=rawMaterials,countriesOfOrigin&page=1&limit=50" \
    | python3 -m json.tool
done

echo "=== Done ==="

Quick Reference

Data Endpoint Key parameter
List styles GET /styles?level=style nameOrCode to search
List variants GET /styles?level=variant styleId (parent style)
List SKUs GET /styles?level=sku variantId (parent variant)
Supply chain GET /supply-chains/:id accepts style, variant, or SKU ID
ESG energy sources GET /companies/:companyId/esg?templateId=2.1.6.1 one call per supplier
Material headers GET /material-headers?styleId= accepts style, variant, or SKU ID
Material lines GET /material-lines?materialHeaderId= include=rawMaterials,countriesOfOrigin

Gotchas Checklist

  1. Empty supply chain ≠ error. nodesByProductionProcesses: [] with a 200 means nothing is mapped (or the story is inactive); 404 not_found means the product ID doesn't exist.
  2. Query any level. Supply chains and materials inherit upward — a SKU query falls back to the variant's/style's data automatically.
  3. ESG 404s are normal. company_not_found usually means the supplier hasn't shared a profile with your network. Skip and continue.
  4. ESG counts are filtered counts. totalDataPoints/answeredDataPoints describe the filtered result set, not the full profile.
  5. Material percentages are per tree level. Siblings under the same parent sum to 100; don't sum across hierarchy levels.
  6. Weight inherits. A null SKU weight may still be resolvable from the parent style; check isWeightAvailable.
  7. Boolean query filters on /styles accept only true or false (isArchived, isComponent, …) — any other value is rejected with a 400; omit the parameter to skip the filter.
  8. List limit is capped at 100 — page through large catalogues with page, or use updatedAfterUnixMs for incremental pulls.