External IDs & Mapping

How to use your own identifiers with the Retraced Public API, in one place. There are two mechanisms, and every resource uses one or both of them:

Mechanism What it is Where
externalId A field you write on a record you own (a product, a BOM header, an order, …). From then on you can address that record by your own ID instead of the Retraced ID. Products, BOM, materials, style properties, orders, order lines
Mapping a code to Retraced reference data A lookup you maintain in Retraced that translates your code into a Retraced ID for data you do not own: a partner company, a style type, a raw material, a facility process. Companies (Internal Company Code), Constant Mappings

Both follow the same contract:

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

Calling an endpoint by externalId vs Retraced ID

Once a record carries an externalId, every place the API takes that record's ID accepts the externalId too: path parameters (GET /styles/{id}, PATCH /orders/{id}), query filters (?styleId=, ?orderId=), and references inside a body (parentId, lines[].styleId, bomHeaderId, lines[].id).

# The same product, three ways to reach it
curl "$BASE_URL/styles/style_01KZ3Y5M4X8QW2P7NBJ6R9T0AE" -H "companyapikey: $API_KEY"   # Retraced ID
curl "$BASE_URL/styles/DRS-2026-001" -H "companyapikey: $API_KEY"                        # your externalId
curl "$BASE_URL/styles?level=variant&styleId=DRS-2026-001" -H "companyapikey: $API_KEY"  # as a filter

Rules that apply everywhere:

Products

Every product level - style, variant and SKU - goes through the single /styles resource and carries one externalId, unique within your company across all three levels. BOM headers, BOM lines, BOM placements, material headers, material lines and style properties each have their own externalId, each unique within its own type.

Endpoint Accepts your externalId in
GET / PUT /styles/{id} {id}
POST /styles parentId
POST /styles/archive, POST /styles/unarchive ids[]
GET /styles styleId, variantId, propertyExternalIds
POST / PUT /styles brands, departments, seasons, collections, customProperties (a style property's externalId)
GET /supply-chains/{id} {id}
GET / PUT /bom-headers/{id}, POST /bom-headers/delete {id}, bomHeaderIds[]; styleId and link.styleId in bodies and ?styleId=
GET / POST / PUT /bom-lines, POST /bom-lines/delete {id}, bomHeaderId, placementId, bomLineIds[], entity.styleId, component.styleId
GET /bom-placements/{id}, GET /bom-placements?ids= {id}, ids
GET / PUT /material-headers/{id}, POST /material-headers/delete {id}, materialHeaderIds[], link.styleId, ?styleId=
GET / POST / PUT /material-lines, POST /material-lines/delete {id}, materialHeaderId, parentId, materialLineIds[], styleId
GET /style-properties?externalId= exact filter on the property's externalId

Create a style with your own ID, then a variant under it by that ID:

curl -X POST "$BASE_URL/styles" -H "companyapikey: $API_KEY" -H "Content-Type: application/json" -d '{
  "name": "Summer Dress 2026",
  "externalId": "DRS-2026-001",
  "styleTypeId": "DRESSES",
  "isComponent": false,
  "codes": [{ "codeId": "ERP_ID", "codeValue": "DRS-2026-001" }]
}'

curl -X POST "$BASE_URL/styles" -H "companyapikey: $API_KEY" -H "Content-Type: application/json" -d '{
  "name": "Summer Dress 2026 - Blue",
  "parentId": "DRS-2026-001",
  "externalId": "DRS-2026-001-BLU",
  "color": "Blue",
  "codes": [{ "codeId": "ERP_ID", "codeValue": "DRS-2026-001-BLU" }]
}'

Response (201) - id and styleId (the parent) are Retraced IDs, externalId is yours:

{
  "metadata": { "success": true },
  "data": {
    "id": "variant_01KZ3Y7D2QH4V9S8E1MWX5B6CN",
    "styleId": "style_01KZ3Y5M4X8QW2P7NBJ6R9T0AE",
    "externalId": "DRS-2026-001-BLU",
    "name": "Summer Dress 2026 - Blue",
    "color": "Blue",
    "styleTypeId": "DRESSES",
    "codes": [{ "codeId": "ERP_ID", "codeValue": "DRS-2026-001-BLU" }],
    "isArchived": false,
    "createdAt": "2026-10-22T06:33:41.000Z",
    "updatedAt": "2026-10-22T06:33:41.000Z"
  }
}

A second product with "externalId": "DRS-2026-001" is refused:

{
  "metadata": { "success": false },
  "statusCode": 409,
  "code": "external_id_already_exists",
  "message": "externalId 'DRS-2026-001' is already used by product 'style_01KZ3Y5M4X8QW2P7NBJ6R9T0AE'"
}

Products have two more things to know:

Product fields that take mapped codes rather than externalIds - styleTypeId, rawMaterialId, and the company fields forCompanyId, suppliers[], mainSupplier, vendors[] - are covered under Companies and Constant Mappings below.

Orders and order lines

An order and each of its lines carry their own optional externalId. Order externalIds are unique across your company's orders; line externalIds are unique across all of your company's order lines, not just within one order. The two are separate namespaces, so an order and one of its lines may share a value.

Endpoint Accepts your externalId in
GET / PATCH / DELETE /orders/{id} {id} (the order)
PATCH /orders/{id} lines[].id (a line of that order)
POST / PATCH /orders lines[].styleId (a product's externalId)
GET /order-lines?orderId= orderId
GET /order-lines/{lineId} {lineId}

GET /orders has no externalId filter. To find an order by your ID, call GET /orders/{externalId} directly; to find it by order number, use GET /orders?orderNumber= (exact match).

externalId and orderNumber are independent

orderNumber is the business document number that appears in the dashboard and in tracing; externalId is your addressing key. The API never fills one from the other:

The one value the API derives is the line's orderLineNumber: when you omit it, it becomes {orderNumber}-{orderType}-L{lineNo}. That derivation uses orderNumber, never externalId, and existing lines keep their number when orderNumber changes later.

Create an order by your own keys end to end - your order ID, your supplier code, your facility-process code, and a product by its externalId:

curl -X POST "$BASE_URL/orders" -H "companyapikey: $API_KEY" -H "Content-Type: application/json" -d '{
  "orderNumber": "PO-2026-0042",
  "externalId": "ERP-PO-88213",
  "buyerCompanyId": "<your-company-id>",
  "supplierCompanyId": "SUP-0917",
  "supplierFacilityProcess": "ERP-FP-CMT",
  "lines": [
    { "styleId": "DRS-2026-001-BLU", "quantity": 300, "unit": "PIECES", "externalId": "ERP-PO-88213-L1" }
  ]
}'

Response (201, trimmed) - every reference comes back resolved to a Retraced ID:

{
  "metadata": { "success": true },
  "data": {
    "id": "order_01KZ3YB0F2N7XQ4VH9R8J5WT6D",
    "externalId": "ERP-PO-88213",
    "orderNumber": "PO-2026-0042",
    "orderType": "PURCHASE_ORDER",
    "buyerCompanyId": "<your-company-id>",
    "supplierCompanyId": "FU5651SSQ5Y",
    "supplierFacilityProcess": "CUT_MAKE_TRIM",
    "lineCount": 1,
    "lines": [
      {
        "id": "ordline_01KZ3YB0F7E2KM8D3SA6PY1Q9C",
        "externalId": "ERP-PO-88213-L1",
        "orderLineNumber": "PO-2026-0042-PURCHASE_ORDER-L1",
        "lineNo": 1,
        "styleId": "variant_01KZ3Y7D2QH4V9S8E1MWX5B6CN",
        "quantity": 300,
        "unit": "PIECES"
      }
    ]
  }
}

Then work with it by those keys:

# Read the order and one of its lines
curl "$BASE_URL/orders/ERP-PO-88213" -H "companyapikey: $API_KEY"
curl "$BASE_URL/order-lines/ERP-PO-88213-L1" -H "companyapikey: $API_KEY"
curl "$BASE_URL/order-lines?orderId=ERP-PO-88213" -H "companyapikey: $API_KEY"

# Update a line, addressed by its externalId (omit "externalId" on the order to keep it)
curl -X PATCH "$BASE_URL/orders/ERP-PO-88213" -H "companyapikey: $API_KEY" -H "Content-Type: application/json" -d '{
  "lines": [ { "id": "ERP-PO-88213-L1", "quantity": 320, "unit": "PIECES" } ]
}'

PATCH /orders/{id} with lines is a full sync of the order's lines; a line you drop is deleted and its externalId is free again within the same request. DELETE /orders/{id} is a hard delete of the order and its lines - see Archiving vs. deleting. Details of both are in the Orders & Order Lines Guide.

Companies: Internal Company Code

Companies are different from the records above: a partner company is not yours, so it has no externalId you can write. Instead, the Internal Company Code you give a company in your network plays that role. It is a value your company stores about a partner, so each of your partners can carry your ERP's supplier or customer number, and another Retraced account that works with the same partner sees only its own codes.

Where to set it: in the Retraced dashboard (v2), open Network and edit the Internal company code column of the company. There is no public API endpoint that writes it. The code is yours to define: it is not derived from anything, and nothing in Retraced is derived from it. Only companies currently in your network resolve; a code you gave to a company you have since unlinked matches nothing.

Wherever the API takes a company ID, it also accepts that code:

Endpoint Accepts your Internal Company Code in
GET /companies/{id}, GET /companies/{id}/esg {id}
GET /companies companyIds, excludeCompanyIds
POST / PATCH /orders buyerCompanyId, supplierCompanyId, receiverCompanyId, vendorCompanyId, factory
GET /orders supplier, buyer
POST / PUT /styles forCompanyId, suppliers[], mainSupplier, vendors[]
GET /styles companyId, suppliers, mainSupplier, vendors
POST / PUT /material-headers, POST /material-lines forCompanyId
GET /certificates companyId, and companyId inside filter

Rules specific to company codes:

# Read a partner by the code you gave it
curl "$BASE_URL/companies/SUP-0917" -H "companyapikey: $API_KEY"

Response (200, trimmed) - the profile of the company that carries code SUP-0917:

{
  "metadata": { "success": true },
  "data": {
    "forCompanyId": "FU5651SSQ5Y",
    "byCompanyId": "<your-company-id>",
    "profileType": "ABOUT",
    "data": { "official_name": { "retracedId": "official_name", "value": "Wynyard Textiles Ltd" } }
  }
}
# The same partner's certificates, and the code echoed back in the list
curl "$BASE_URL/certificates?companyId=SUP-0917" -H "companyapikey: $API_KEY"
curl "$BASE_URL/companies?companyIds=SUP-0917" -H "companyapikey: $API_KEY"
{
  "metadata": { "success": true, "pagination": { "page": 1, "limit": 20, "total": 1, "totalPages": 1 } },
  "data": [
    { "id": "FU5651SSQ5Y", "name": "Wynyard Textiles", "internalCompanyCode": "SUP-0917", "country": "PT" }
  ]
}

Constant Mappings: materials, product types, facility processes

Three kinds of reference data are maintained by Retraced and shared by every account, so you cannot put an externalId on them. Instead you map your code to the Retraced ID once, and then send the code anywhere the ID is expected:

resourceType Retraced ID it maps to Where you send the code
STYLE_TYPE A style type from GET /style-types (TSHIRTS, DRESSES, …) styleTypeId on POST / PUT /styles and GET /styles?styleTypeId=
RAW_MATERIAL A raw material (COTTON_BALE, …) rawMaterialId on POST / PUT /material-lines and GET /material-lines?rawMaterialId=
FACILITY_PROCESS A facility process from GET /facility-processes (CUT_MAKE_TRIM, …) buyerFacilityProcess, supplierFacilityProcess, receiverFacilityProcess on POST / PATCH /orders

Where to manage them:

Rules:

Create three mappings in one call:

curl -X POST "$BASE_URL/constant-mappings" -H "companyapikey: $API_KEY" -H "Content-Type: application/json" -d '[
  { "resourceType": "STYLE_TYPE",       "retracedId": "TSHIRTS",       "externalId": "ERP-PT-TEE",  "externalName": "Tee" },
  { "resourceType": "RAW_MATERIAL",     "retracedId": "COTTON_BALE",   "externalId": "ERP-MAT-COT" },
  { "resourceType": "FACILITY_PROCESS", "retracedId": "CUT_MAKE_TRIM", "externalId": "ERP-FP-CMT" }
]'

Response (201, trimmed) - four rows, because the raw-material mapping is mirrored onto its style type:

{
  "metadata": { "success": true },
  "data": [
    { "id": "0T6TPNXPEYBXSW4FE7", "resourceType": "STYLE_TYPE",       "retracedId": "TSHIRTS",       "name": "T-shirts",            "externalId": "ERP-PT-TEE",  "externalName": "Tee" },
    { "id": "YJCXF083DKCVV09YFR", "resourceType": "RAW_MATERIAL",     "retracedId": "COTTON_BALE",   "name": "Cotton bale",         "externalId": "ERP-MAT-COT", "externalName": null },
    { "id": "EAQSFK29UCKBXMSX8N", "resourceType": "STYLE_TYPE",       "retracedId": "COTTON_BALE",   "name": "Cotton bale",         "externalId": "ERP-MAT-COT", "externalName": null },
    { "id": "USZK8KJEGO4CX5FNRL", "resourceType": "FACILITY_PROCESS", "retracedId": "CUT_MAKE_TRIM", "name": "Cut make trim (CMT)", "externalId": "ERP-FP-CMT",  "externalName": null }
  ]
}

Use the codes, then manage them:

# A product by product-type code, a material line by material code, an order by facility-process code
curl -X POST "$BASE_URL/styles" -H "companyapikey: $API_KEY" -H "Content-Type: application/json" \
  -d '{ "name": "Basic Tee", "externalId": "TEE-2026-001", "styleTypeId": "ERP-PT-TEE", "isComponent": false, "codes": [{ "codeId": "ERP_ID", "codeValue": "TEE-2026-001" }] }'
# -> "styleTypeId": "TSHIRTS"

curl -X POST "$BASE_URL/material-lines" -H "companyapikey: $API_KEY" -H "Content-Type: application/json" \
  -d '{ "styleId": "TEE-2026-001", "materialHeaderId": "<material-header-id-or-externalId>", "parentId": null, "rawMaterialId": "ERP-MAT-COT", "percentage": 100 }'
# -> "rawMaterialId": "COTTON_BALE"

# List, filtered by type and code
curl "$BASE_URL/constant-mappings?resourceType=STYLE_TYPE&externalId=ERP-PT-TEE" -H "companyapikey: $API_KEY"

# Rename a code (the mapping id comes from the list or the create response)
curl -X PATCH "$BASE_URL/constant-mappings" -H "companyapikey: $API_KEY" -H "Content-Type: application/json" \
  -d '[ { "id": "0T6TPNXPEYBXSW4FE7", "externalId": "ERP-PT-TEE-V2", "externalName": "Tee (v2)" } ]'

# Delete by id, or by code together with its type (never by code alone - a mirrored code exists in two types)
curl -X DELETE "$BASE_URL/constant-mappings?ids=0T6TPNXPEYBXSW4FE7" -H "companyapikey: $API_KEY"
curl -X DELETE "$BASE_URL/constant-mappings?externalIds=ERP-FP-CMT&resourceType=FACILITY_PROCESS" -H "companyapikey: $API_KEY"
# -> 204

An unknown or unmapped code is passed through unchanged and fails as the ID would on that endpoint: 400 style_type_not_found on /styles, 404 material_not_found on /material-lines, 400 invalid_facility_process_ids on /orders. On the list endpoints (GET /styles?styleTypeId=, GET /material-lines?rawMaterialId=) an unknown code simply matches nothing and returns an empty page.

Error reference

Status code When
409 external_id_already_exists The externalId is already held by another record of the same type in your company (product, BOM header/line/placement, material header/line, style property, order, order line). The message names the owner.
404 not_found, order_not_found, bom_header_not_found, material_header_not_found, … A path ID or externalId matched nothing in your company (also returned for another company's externalId).
404 company_not_found / invalid_company_id A company code matched no partner, or matched more than one.
400 style_type_not_found styleTypeId is neither a style type ID nor one of your product-type codes.
404 material_not_found rawMaterialId on POST / PUT /material-lines is neither a raw material ID nor one of your material codes.
400 invalid_facility_process_ids A *FacilityProcess value is neither a facility process ID nor one of your codes.
404 style_type_not_found, material_not_found, facility_process_not_found retracedId on POST /constant-mappings is not a Retraced ID.
404 constant_mapping_not_found A mapping id or code on PATCH / DELETE /constant-mappings does not exist.
409 constant_mapping_already_exists The code is already mapped for that resourceType.
409 constant_mapping_code_is_retraced_id A FACILITY_PROCESS code equals a facility process ID.
400 duplicate_constant_mapping_codes The same code appears twice for one resourceType in a single call.
400 constant_mapping_mirror_in_batch A call names both a mapping and its raw-material mirror.