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

Your own product IDs. If the brand set an externalId when creating a product, every product ID field in this API - path IDs, styleId, variantId, link.styleId, entity.styleId, component.styleId - also accepts that value. IDs in responses are always Retraced IDs, and each product returns its externalId next to id.

An externalId is resolved inside the catalogue of the account whose API key you are using. If you are a partner working on a brand's products with your own key, use the Retraced IDs the brand gave you - the brand's externalId values are not visible in your own namespace and will not resolve.

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.