Salsify PIM mapping.
This guide shows how a Salsify organization emits ULC records.
Salsify data model in one page
- Products are the core entity, identified by an internal ID and one or more external IDs (commonly SKU, UPC, MPN).
- Properties are typed fields on products: strings, numbers, enums, digital assets, or references.
- Product relationships model accessories, replacements, and bundle components through typed reference properties.
- Digital assets (images, PDFs, IES files) are first-class objects with their own IDs, versioning, and renditions.
- Channels and recipes define per-channel export transformations. The ULC emitter is typically a Salsify channel recipe plus an external transform service.
- APIs: GraphQL for typed reads, REST for bulk operations and webhooks, CSV for imports.
Core mapping
Identity
| Salsify | ULC path |
|---|---|
| Parent product identifier / catalog model property | product_family.catalog_model |
| Variant external ID (SKU) | configuration.catalog_number |
| Internal brand property | product_family.manufacturer.slug |
| Product line / series property | product_family.catalog_line |
| Derived full slug | record_id |
Category and mounting
| Salsify category | primary_category | mounting_types |
|---|---|---|
.../recessed/downlight | downlight | ["recessed_ceiling"] |
.../pendant/linear | linear | ["pendant"] |
.../exterior/wall-pack | bulkhead_wall_pack | ["surface_wall"] |
.../industrial/high-bay | high_bay | ["pendant", "surface_ceiling"] |
.../outdoor/bollard | bollard | ["surface_floor"] |
Dimensional values
Salsify dimensional values use the Number property type paired with a unit-of-measure property (for example, overall_diameter_mm with an expressed_in: mm companion convention). The emitter converts to ULC dual-unit (mm and in) at emit time.
Digital assets
| Salsify digital-asset role | source_files[].file_type |
|---|---|
cutsheet_pdf | datasheet_pdf |
photometric_ies | ies |
photometric_ldt | ldt |
installation_instructions | installation_instructions_pdf |
The emitter streams each asset by its Salsify asset ID, computes SHA-256, and populates source_files[].reference. The cutsheet asset additionally populates product_family.cutsheet (required by the schema) with the same hash; an emitter that writes only source_files[] produces invalid records.
Gotchas
- Multi-CCT product families. Modeled as parent product with variant children. One child per CCT becomes one ULC scenario record; the shared
product_familyblock derives from the parent. - Localization. Salsify properties support per-locale values. ULC is locale-neutral at the schema level; the emitter chooses a canonical locale (typically en-US).
- Property type mismatches. Salsify's "number" type is a double. ULC distinguishes integer in some fields. Coerce at emit time.
- Asset digest caching. SHA-256 on every asset on every run is expensive. Cache by Salsify asset ID + version; invalidate on
updated_atchange. - Missing values. If a property a record should carry is blank for a SKU, skip or fail the export rather than shipping a thin record. There is no level to set: the validator computes the conformance level from whatever the record carries, so gate the export on completeness in Salsify instead.
Full guide with emit-flow pseudocode, accessory-relationship walking, and Python transform skeleton: github.com/ulcspec/ULC/blob/main/mappings/pim/salsify.md