Custom Fields
Checkout and waitlist forms can collect extra answers from buyers. Each answer definition is a custom field: a named input with a type, label, and optional validation.
Identity is name — a slug such as size, never a UUID. Nested collections live
under a product (checkout or waitlist) or under a variant (checkout override).
Overview
- List custom fields
- Retrieve a custom field
- Create a custom field
- Update a custom field
- Delete a custom field
Each operation has live and draft twins, and three parent collections. Paths are listed on the endpoint pages.
Collections
A product has two independent arrays:
- Checkout —
/products/{productId}/custom-fields(and/draft/custom-fields). Shown on the checkout form. - Waitlist —
/products/{productId}/waitlist-custom-fields(and/draft/waitlist-custom-fields). Shown on the waitlist form only. These do not appear at checkout.
A variant may store its own checkout array at
/product-variants/{id}/custom-fields (live) or
/products/{productId}/draft/variants/{id}/custom-fields (overlay):
- An empty array means inherit the product checkout fields.
- A non-empty array wholesale-replaces the product checkout fields for that price. Creating the first variant field drops the product-level checkout fields for buyers of that variant.
- There is no way to force “no fields” on a variant when the product has some.
- Variants have no waitlist collection.
Live writes while a draft exists
When the parent product has has_draft: true, live nested writes return 409
and change nothing. Edit the overlay instead (/products/{productId}/draft/…).
See Live writes while a draft exists.
Draft writes require draft_version from Get Product Draft.
Create and update on the overlay also return a fresh preview_url. Overlay
delete returns 204 with no body.
Include on parent GETs
Get Product and Get Product Draft
always include custom_fields and waitlist_custom_fields keys. The value is
null unless you pass ?include=custom_fields and/or
?include=waitlist_custom_fields. When requested, the value is the stored array
(possibly empty) — not the { items, pagination } list envelope.
Get Product Variant and
Get Product Draft Variant
always include custom_fields. The value is null unless
?include=custom_fields. Nested variants[] on a product leave this key null.
GET /products list items omit the keys.
Product and draft GET also accept include=styles,settings,media — see
Checkout appearance.
Writes are the nested collection endpoints. Sending custom_fields on product
or variant PATCH is ignored.
Order
order is the field's 0-based position in its collection, and the order the
fields appear at checkout. A collection is always numbered 0..n-1 with no
gaps.
Create appends the new field to the end. Delete closes the gap. Patching
order moves that field to the given position and renumbers the rest, so one
write can change the order of fields you did not send — a value past the last
position lands the field last. There is no reorder endpoint, so ordering n
fields takes n calls.
Types
type is one of text, textarea, checkbox, integer, decimal,
select, date, datetime. select requires options with at least one
{ value, label }. checkbox is a field type, not a checkout consent string
(mandatory_consent lives on the product).
Custom Field Object
| Field | Type | Nullable | Description |
|---|---|---|---|
name | string | No | Identity slug. Pattern ^[a-z0-9_-]+$, max 50. Immutable after create. Path {name}. |
type | string | No | One of the types above. |
label | string | No | Buyer-facing copy, max 255. |
placeholder | string | Yes | Form-hint copy, max 255. |
enabled | boolean | No | false hides the field without deleting it. Disabled fields are skipped at checkout. |
required | boolean | No | Inert when enabled is false. |
order | integer | No | 0-based position in the collection. See Order. |
validation | object | Yes | { min, max } for integer / decimal answers. null means no min/max. precision is not public and is refused. |
validation.min | integer | Yes | Minimum. |
validation.max | integer | Yes | Maximum. |
options | array | No | Select choices. Empty when the type is not select. |
options[].value | string | No | Stored choice value, max 100. |
options[].label | string | No | Buyer-facing choice label, max 255. |
sync_easymail | boolean | No | Marks the definition for Easymail contact sync when the field is also enabled. Does not create Easymail catalog fields. |
Draft create/update responses add:
| Field | Type | Nullable | Description |
|---|---|---|---|
draft_version | integer | No | New overlay version after the write. Minimum 1. |
preview_url | string | No | Temporary signed checkout preview. Expires after about one hour. See Draft preview. |
The internal UUID minted on create is never returned.