Docs navigation
Home / Docs / Custom fields

Custom fields

Define your own structured attributes on products, variants, categories, brands, and channel SKUs — with types, validation, and per-field storefront visibility.

Foundry’s standard fields cover what every catalog needs — name, SKU, cost, weight. Custom fields cover what makes your catalog yours: voltage, thread pitch, core charge, fitment year. They’re structured and typed, not a notes box, so you can validate them, map them to channels, and render them in a storefront spec table.

The model

A custom field has two halves, and keeping them straight explains most of the behavior:

  • Definition — an org-level entry in your field library: key, label, type, validation rules. Defined once.
  • Value — what a specific product, variant, category, brand, or channel SKU holds for that definition.

Definitions live in your organization’s library and are reused everywhere. You don’t redefine “Voltage” per product; you define it once and attach it where it applies.

Keys vs labels

Every definition carries both:

ExampleChanges over time?
keyvoltageNo — it’s the stable machine name
labelVoltageYes — safe to rename anytime

The key is what API consumers, imports, and channel mappings bind to. The label is what humans see. Rename labels freely; treat keys as permanent once anything references them.

Pick keys deliberately at creation: lowercase, no spaces, descriptive. thread_pitch ages better than field_3.

Field types

TypeUse forConfig
TEXTShort strings
LONGTEXTMulti-line plain text
RICHTEXTFormatted HTMLSanitized on write
NUMBERNumeric valuesOptional unit, precision
BOOLEANYes/no
SELECTOne of a fixed listoptions: [{value, label}]
MULTISELECTSeveral of a fixed listoptions: [{value, label}]
DATEDates
URLLinks
JSONArbitrary structured data
ARRAYLists of values
OBJECTNested structures

Every definition you create is published to your public storefront by default. visibleOnStorefront defaults to ON, so decide it as you create the field — set it false for anything internal (cost inputs, supplier references, raw payloads you keep for your own use). Details under storefront visibility.

Prefer SELECT over TEXT whenever the set of valid answers is knowable. Free text turns into Blue, blue, BLUE, and blue inside a month, and nothing downstream will ever group them.

NUMBER takes a unit and precision in its config. Storing 12 with unit V beats storing the text 12V — a number can be sorted, converted, and compared wherever you consume it.

Custom field values are not a filterable dimension. There is no API for “find every variant where voltage is 12” — values are returned with the records you read, not queried across. Consistent typing still matters, because it’s what makes the values usable once you have them: in a spec table, a channel mapping, an export, or your own index.

If you need to filter or facet on these values — a year/make/model picker, say — build that index in your own service. Choosing SELECT or MULTISELECT constrains and labels the values; it does not make them queryable here.

Where a field can be attached

Each definition declares which entities it applies to:

  • PRODUCT — attributes of the product as a concept
  • VARIANT — attributes that differ per sellable SKU
  • CHANNEL_SKU — values specific to one channel’s listing
  • CATEGORY — attributes of a category
  • BRAND — attributes of a brand

A field can apply to several. The distinction that matters most is product vs variant: if the value differs between sizes or colors, it belongs on the variant. If it’s true of the whole product family, put it on the product. Getting this wrong is the most common modeling mistake — a “Color” field on the product forces one color for every variant.

Validation

Definitions carry optional validation:

  • required — must have a value
  • min / max — numeric bounds
  • minLength / maxLength — string length
  • regex — pattern match

Validation is enforced on write, including through the API and imports. A field marked required at the template level is enforced when that template’s class is applied — see PIM classes & templates.

Worth separating two things that sound alike. Validation applies to the value of a field you named correctly — a bad value is rejected. An unrecognised field key is a different case: unknown properties are stripped from a payload rather than erroring, so writing to a key that doesn’t exist returns success and stores nothing. That’s the failure worth defending against in an integration — ensure the definition exists before writing to it, and read the value back if the write mattered.

Storefront visibility

Every definition has a visibleOnStorefront flag controlling whether its resolved value is exposed on the public Storefront API, where a headless PDP can render it in a spec table.

This defaults to ON. That’s convenient for spec-style fields and wrong for internal ones. Before a storefront goes live, audit your definitions and turn it off for anything you wouldn’t print on a product page:

  • Supplier or vendor names
  • Internal cost or margin notes
  • Data-confidence or QA scores
  • Competitor identifiers

Two more fields shape the spec table:

  • storefrontGroup — a section label like “Electrical” or “Dimensions”. Null means ungrouped.
  • storefrontOrder — sort position within the group. Ties break by label.

Grouping is what turns thirty rows of attributes into a spec table someone will actually read.

Global fields

A definition marked global is suggested on every product and variant in the org, regardless of class or template attachment. Use this sparingly — for genuinely universal attributes like country of origin. Most fields should arrive through a template so they appear only where they’re relevant.

Archiving

Definitions archive rather than delete. Archived fields stop being offered on new records but keep their existing values, so historical data stays intact and any integration reading that key keeps working. There’s no way to “un-say” a field that’s already been pushed to a channel — archive is the honest operation.

Writes to an archived definition still succeed, and the value really is stored — but the read paths that resolve values for display, for the storefront, and for the admin all skip archived definitions. So an integration writing to a field a merchant archived last week gets a 200 and produces nothing anyone will see. If that matters to you, check the archived state before writing and tell the merchant, rather than writing into a field nothing reads.

Working with values over the API

Custom field values are read and written through the Partner API alongside the record they belong to. Values are keyed by the definition’s key, so a client written against thread_pitch keeps working when someone relabels it to “Thread Pitch (mm)”.

A caveat worth knowing: the API strips unknown fields from a payload rather than erroring. If you POST a value for a key that doesn’t exist — or one not attached to that entity type — it’s silently dropped rather than rejected. When a write appears to succeed but the value doesn’t appear, check the key spelling and the field’s appliesTo scope first.