Reference data setup
Reference data — brands, categories, styles, seasons, colours, sizes, merchandise departments, and suppliers — is the vocabulary every SKU is described with. Set it up first: SKUs reference these dimensions by key, and the rest of the platform (items, places, orders) is built on SKUs.
This guide covers creating the dimensions, the order to create them in, and the natural-key conventions that let a downstream integration (an IMS/ERP feed) create SKUs without ever looking up a GUID.
Overview
All eight dimensions are CRUD reference-data controllers that behave identically — once you've learned one, you've learned them all. Each exposes:
| Verb | Route pattern | Access mode | Returns |
|---|---|---|---|
POST | /api/<Dim>s/Create<Dim> | Create | the Detail DTO |
GET | /api/<Dim>s/GetFiltered<Dim>s | Read | PageResult<<Dim>SummaryDto> |
GET | /api/<Dim>s/Get<Dim>/{id} | Read | the Detail DTO |
POST | /api/<Dim>s/Update<Dim> | Update | the Detail DTO |
DELETE | /api/<Dim>s/Delete<Dim>/{id} | Delete | true |
POST | /api/<Dim>s/BulkUpsert | Create + Update | per-row result list |
All are gated on the ReferenceData module permission (see the
permission bitmask). All follow the
platform-wide error and response conventions.
Prerequisites
- A valid token with
ReferenceDatacreate/read permission — see Getting started. - Familiarity with Identifiers & natural keys —
this guide uses
ExternalRef/Code/Namethroughout.
Which order to create dimensions in
Two dimensions are hierarchical (self-referential parent link): Brand
(ParentBrandId) and Category (ParentCategoryId). The rest are flat.
- Flat dimensions first, in any order — Style, Season, Colour, Size, Merchandise Department, Supplier.
- Hierarchical dimensions roots-first — create a Brand/Category with no
parent, then its children referencing the parent's
Id. A create/update that pointsParentBrandIdat a brand that doesn't exist returns400("ParentBrandId"), and one that would form a cycle returns400("ParentBrandId would create a cycle"; the cycle check walks up to 32 levels).
You do not have to create dimensions before SKUs if you enable auto-create — see Letting SKU import create dimensions.
Step-by-step
1. Create a flat dimension
curl -sk -X POST "http://<host>/api/Styles/CreateStyle" \
-H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-d '{"Name":"Crew Neck","ExternalRef":"style_crew"}'
Returns 200 with the Detail DTO — a new Id, the fields echoed, audit fields,
and EntityVersionNo: 1.
2. Create a hierarchical dimension, roots-first
Create the root brand:
curl -sk -X POST "http://<host>/api/Brands/CreateBrand" \
-H "Authorization: Token $TOKEN" -H "Content-Type: application/json" \
-d '{"Name":"Nike","ExternalRef":"nike_01","ParentBrandId":null}'
Then a child referencing the root's Id:
curl -sk -X POST "http://<host>/api/Brands/CreateBrand" \
-H "Authorization: Token $TOKEN" -H "Content-Type: application/json" \
-d '{"Name":"Air Jordan","ExternalRef":"aj_01","ParentBrandId":"<nikeId>"}'
The child's response carries both ParentBrandId and a flattened
ParentBrandName ("Nike").
3. List and filter
# All brands (paged)
curl -sk "http://<host>/api/Brands/GetFilteredBrands" \
-H "Authorization: Token $TOKEN"
# OData filter — $ must be percent-escaped as %24 in curl
curl -sk "http://<host>/api/Brands/GetFilteredBrands?%24filter=Name%20eq%20%27Nike%27" \
-H "Authorization: Token $TOKEN"
The list response is a PageResult — the rows are under Items. See
Errors & response conventions
for the OData functions that are safe to use.
4. Update and delete
Update is a full-replace POST carrying the Id (there is no PUT); send every
field, not just the changed ones. EntityVersionNo increments on success.
Delete is a soft delete (DELETE …/Delete<Dim>/{id} → true); the row's
Name/ExternalRef become free to reuse (see
natural-key reuse).
Natural keys: the merchandise-department & season exception
SKU imports reference dimensions by key rather than GUID, resolved in this
precedence: Id → ExternalRef → Name (first non-null wins; the rest are
ignored). One wrinkle:
Code, not ExternalRefThese two dimensions have no ExternalRef column — their natural key is
Code. When a SKU import supplies MerchandiseDepartmentExternalRef or
SeasonExternalRef, the resolver matches it against the entity's Code column.
Every other dimension (Brand, Category, Style, Supplier) matches ExternalRef
directly. Colour and Size resolve by Name.
Letting SKU import create dimensions
The per-tenant flag AutoCreateReferenceData (default false) lets a SKU
create/import mint missing dimensions on the fly instead of failing. Toggle it
via configuration:
# Read current configuration (full DTO)
curl -sk "http://<host>/api/configurations/getconfigurations" \
-H "Authorization: Token $TOKEN"
# setconfigurations is a full-DTO replace — GET first, change the one flag, POST it back
curl -sk -X POST "http://<host>/api/configurations/setconfigurations" \
-H "Authorization: Token $TOKEN" -H "Content-Type: application/json" \
-d '{ ...full config..., "AutoCreateReferenceData": true }'
With the flag on, a createsku referencing an unknown
BrandExternalRef and supplying BrandName creates the brand and links it.
With the flag off (or with no Name to create from), the unknown reference
fails 400 ("BrandExternalRef '…' not found"). This flag affects single
createsku and SKU bulk-upsert; dimension-level bulk-upsert always creates
missing rows regardless of the flag.
Bulk sync from an external system (IMS/ERP)
Each dimension's BulkUpsert endpoint is the idempotent sync entry point. Rows
are matched by (Organisation, ExternalRef) (or Code); it is designed to be
called on a schedule without prior lookups.
Two behaviours to code against — both covered in response conventions:
- The HTTP status is always
200. Success/failure is per row (Created/Updated/Unchanged/Failed, withErrorMessageon failures). Never read the HTTP code to decide whether the batch worked — read the per-rowStatus. - A batch with any failed row is rolled back (all-or-nothing). Re-chunk around the failed rows and resubmit.
- Re-submitting an unchanged payload returns
Unchangedfor every row — safe to re-run.
For hierarchical dimensions, a parent referenced by
BulkUpsertDimItemDto.ParentExternalRef must already exist in the database —
an in-batch parent that only appears earlier in the same request is not resolved.
Load parents in an earlier batch, children in a later one.
Error handling
| Situation | Response |
|---|---|
Duplicate Name or ExternalRef (same tenant, non-deleted) | 400, Message names the field + value |
Empty required Name | 400, Message: "Name" |
| Unknown parent id | 400, Message: "ParentBrandId" |
| Parent link forms a cycle | 400, "ParentBrandId would create a cycle" |
| Get/Update/Delete unknown id | 400 — Get returns []; Update/Delete return NotFound |
Related
- Identifiers & natural keys
- Errors & response conventions
- SKU onboarding & lifecycle (next in the setup path) — uses these dimensions by natural key.
- Reference pages: Create Brand, List Brands, Upsert Brand.