Skip to content

Products, variants, and options#

RecordIDWhat it holds
Productprod_Name, description, product_type, status, categories, images, and a modifier set. No price, SKU, or stock.
Variantvar_unit_price_money, sku, barcode, tax settings, an inventory link, a delivery profile, unit weight and dimensions, and its own status
Option and valueopt_, optv_Labels that tell variants apart, such as Size with Small and Large. Each variant selects one value for each active option in selected_options.

A simple product has one variant, its default_variant_id. An optioned product has options and every variant you list for it: Flint never generates variants from option combinations. Across a product's variants:

  • All active variants use the same currency.
  • No two variants select the same combination of values.
  • No two variants or bundles share a sku or a barcode. A taken one returns 409 ALREADY_EXISTS, with the record that has it in details[0].current_resource.

An order line that names a variant_id takes its name, price, and tax settings from the catalog, and rejects your own values for them with 400 INVALID_REQUEST and detail code NOT_ALLOWED. To sell something that isn't in the catalog, send a custom line on the order instead.

Create a product#

variant_mode selects the shape of POST /v1/products:

variant_modeSendCreates
simpledefault_variantOne variant, which becomes default_variant_id
optionedoptions and variantsThe options and values, and up to 100 variants. Add more with POST /v1/products/{product_id}/variants.

Sending fields from the other shape, more than 100 variants, or a price, SKU, or tax field on the product itself returns 400 INVALID_REQUEST with param naming the field. Price, SKU, and tax fields belong on a variant.

Each variant selects its values by option_name and value, so it needs no option or value IDs, even for options created in the same request. Both match case-insensitively and ignore surrounding whitespace.

cURL
curl -X POST https://api.withflintpay.com/v1/products \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Idempotency-Key: product-tee-crew" \
  -H "Content-Type: application/json" \
  -d '{
    "variant_mode": "optioned",
    "name": "Crew tee",
    "product_type": "physical",
    "options": [{
      "name": "Size",
      "values": [{ "value": "Small" }, { "value": "Large" }]
    }],
    "variants": [
      {
        "name": "Small",
        "sku": "TEE-CREW-S",
        "unit_price_money": { "amount": 2800, "currency": "USD" },
        "selected_options": [{ "option_name": "Size", "value": "Small" }]
      },
      {
        "name": "Large",
        "sku": "TEE-CREW-L",
        "unit_price_money": { "amount": 3000, "currency": "USD" },
        "selected_options": [{ "option_name": "Size", "value": "Large" }]
      }
    ]
  }'
Response
{
  "data": {
    "product_id": "prod_01K0P7W6A4N9F3J2T8Q5R1C6XN",
    "name": "Crew tee",
    "product_type": "physical",
    "status": "active",
    "version": 1,
    "variant_count": 2,
    "options": [
      {
        "option_id": "opt_01K0P7W6A4N9F3J2T8Q5R1C6XA",
        "name": "Size",
        "position": 0,
        "status": "active",
        "values": [
          { "option_value_id": "optv_01K0P7W6A4N9F3J2T8Q5R1C6XB", "value": "Small", "position": 0, "status": "active" },
          { "option_value_id": "optv_01K0P7W6A4N9F3J2T8Q5R1C6XC", "value": "Large", "position": 1, "status": "active" }
        ]
      }
    ],
    "variants": [
      {
        "variant_id": "var_01K0P7W6A4N9F3J2T8Q5R1C6XP",
        "name": "Small",
        "sku": "TEE-CREW-S",
        "unit_price_money": { "amount": 2800, "currency": "USD" },
        "selected_options": [
          { "option_id": "opt_01K0P7W6A4N9F3J2T8Q5R1C6XA", "option_name": "Size", "option_value_id": "optv_01K0P7W6A4N9F3J2T8Q5R1C6XB", "value": "Small" }
        ]
      },
      {
        "variant_id": "var_01K0P7W6A4N9F3J2T8Q5R1C6XQ",
        "name": "Large",
        "sku": "TEE-CREW-L",
        "unit_price_money": { "amount": 3000, "currency": "USD" },
        "selected_options": [
          { "option_id": "opt_01K0P7W6A4N9F3J2T8Q5R1C6XA", "option_name": "Size", "option_value_id": "optv_01K0P7W6A4N9F3J2T8Q5R1C6XC", "value": "Large" }
        ]
      }
    ]
  }
}

The response is abridged. Only the create response includes variants, ordered by position and then variant_id, so you get every variant_id without another request. Other product responses leave it out: list a product's variants with GET /v1/products/{product_id}/variants.

Product types#

product_typeDeliveryAlso
physicalEach variant takes a delivery_profile_id. A variant created without one gets your default delivery profile.unit_weight and unit_dimensions feed delivery quotes
service, fee, digitalNone. delivery_profile_id is rejected, and delivery_configuration_status is not_applicable.
gift_cardNoneSells gift cards; see Gift card products

You can change product_type with PATCH /v1/products/{product_id}, except to or from gift_card: create a separate product instead.

Status and archiving#

Products and variants share the same three statuses. A product or variant created without status is active.

status on product
Final values do not change again
  • active
    Sellable. available_for_sale is true when the product also has at least one active variant, and a variant sells only while it and its product are both active.
  • inactive
    Not sellable, and reversible: set status back to active to sell it again. Use it to pause a product or variant you plan to sell later.
  • archivedFinal
    Set by DELETE. Archiving a product archives its variants. An archived record stays readable by ID, so past orders keep their references, and lists include it only when status=archived names it.

Warning: Archiving can't be undone

Any write to an archived product or variant returns 409 ACTION_NOT_ALLOWED with reason: archived and current_status: archived, and details[0] names the archived record in resource_type and resource_id. Creating a variant under an archived product names the product. Set status to inactive for anything you might sell again.

DELETE returns the archived record, the same body a following GET returns. Repeating it returns 200 with that body and changes nothing. A simple product's default variant can't be archived on its own: archive the product instead.

What blocks a change#

Orders keep a snapshot of the catalog, so an open order doesn't stop you from setting a product or variant to inactive. Other records that still sell it do:

ChangeRefused while the product or variant is in
Set to inactiveAn open checkout session, an open invoice payment attempt, an active payment link, a subscription plan that isn't archived, an active subscription, or, for a variant, an active bundle
Archive with DELETEAny of those, or an open order
Change a variant's currency, or a product's product_type or default_variant_idAn open order

A payment link that lets buyers choose among several variants in variant_ids blocks a variant only when it is the last active one the link lists. A refusal returns 409 RESOURCE_IN_USE. details[0].blocking_resources lists the records in the way, such as order, checkout_session, payment_link, subscription, subscription_plan, or bundle references, and blocking_resource_count gives the total. Close or change those records, then retry.

Options and variant selections#

Every active variant selects one active value for each active option. To change a product's options, send the complete options array in PATCH /v1/products/{product_id} with the version you read as expected_version. Include the option_id and option_value_id of every member you keep, add new members without an ID, and leave out a member to retire it. Change the catalog later shows the request.

Add an option to a product that already has variants#

A new or reactivated option needs a value on every active variant, so send existing_variant_value on that option's entry in options[]. Flint gives that value to each active variant that has no active value for the option after the request. It must match one of the option's active values, ignoring case and surrounding whitespace, and is at most 255 characters. Flint doesn't store or return it.

The option change and the variant selections succeed or fail together. Each variant that gets a value advances its version and sends product_variant.updated, and the product sends product.updated. Inactive and archived variants, and variants that already have an active value for the option, keep what they have. A variant in an active bundle can take the new value, because it only adds a selection. Add an option to a product that has variants has a full example.

Move a variant to other values#

Send the complete selected_options in PATCH /v1/products/{product_id}/variants/{variant_id}, naming one value for each active option. The variant keeps its variant_id, so inventory items, payment links, and bundles still point at it, and orders already placed keep the values they were placed with.

A variant keeps its selection for an option that is no longer active, such as one you removed. Each selected_options entry a variant returns is a valid input once you drop option_id and option_value_id, so you can resend the selection unchanged, or leave out an inactive option and keep its value. You can't change that value or add a selection for an option that is no longer active. If a removed option shares its name with an active one, selecting the active option replaces the old selection for that name. On a product with no active options, "selected_options": [] leaves the selection unchanged.

You can retire a whole option while variants select its values: they keep those selections. A single value of an active option can't be removed or set to inactive while an active variant selects it.

ResponseWhen
400 INVALID_REQUEST, detail REQUIREDActive variants need a value and existing_variant_value is missing. param is the field, such as options[1].existing_variant_value, and the message gives how many variants need one. Nothing changes.
400 INVALID_REQUEST, detail INVALID_VALUEexisting_variant_value matches none of the option's active values, even when no variant needs it
409 RESOURCE_IN_USEYou removed or inactivated a value an active variant selects. param is options[i].values, details[0].blocking_resources lists up to 10 product_variant references, and blocking_resource_count gives the total. Move those variants to another value or set them to inactive, then retry with the product's latest version.
409 ALREADY_EXISTSAnother variant of the product already selects the combination, named in details[0].current_resource. Two variants with the same combination in one create request return 400 INVALID_REQUEST instead.
409 VERSION_CONFLICTThe product changed since you read it. Read it again and retry with details[0].current_version.

Gift card products#

A gift_card product sells gift cards. Gift card purchase lines covers selling one on an order and when each card is issued. Its variants:

  • Use USD and are non-taxable. Send taxable: false or leave it out, and leave out line_item_tax_category.
  • Don't track inventory, take a modifier set, or need delivery.
  • Can't be bundle components or subscription lines.
  • Are left out of ordinary cart discounts. Gift card value already on the order can pay for other goods, but not for a new gift card.

gift_card_configuration keeps the value a card carries, face_value_money, separate from the variant's price, unit_price_money. It is accepted only on gift card variants.

price_modeRule
face_valueunit_price_money equals face_value_money. Leaving out the configuration creates a fixed denomination at the variant price in this mode.
discountedunit_price_money is less than face_value_money, such as a $100.00 card sold for $90.00

Add custom_amount_bounds to let the buyer choose the value, from minimum_money to maximum_money. The bounds must include face_value_money, and every value and bound is in USD and at most 200000 cents. The buyer's choice goes in the order line's gift_card_purchase.face_value_money. This variant sells a $100.00 card for $90.00 and lets the buyer pick from $25.00 to $500.00:

JSON
{
  "unit_price_money": { "amount": 9000, "currency": "USD" },
  "gift_card_configuration": {
    "face_value_money": { "amount": 10000, "currency": "USD" },
    "price_mode": "discounted",
    "custom_amount_bounds": {
      "minimum_money": { "amount": 2500, "currency": "USD" },
      "maximum_money": { "amount": 50000, "currency": "USD" }
    }
  }
}

A custom value is priced at the same ratio of unit_price_money to face_value_money, rounded to the nearest cent with ties rounded up:

Text
price = chosen value × unit_price_money ÷ face_value_money
2505 × 9000 ÷ 10000 = 2254.5, charged as 2255 ($22.55 for a $25.05 card)

On a variant update, gift_card_configuration replaces the whole configuration when you send it and stays as it is when you leave it out. null isn't accepted. The configuration you keep must still fit the new price, so to change the price of a face_value variant, send gift_card_configuration with the new face_value_money too. Send expected_version to reject a concurrent change. Gift cards covers the cards themselves.

Inventory, weight, and delivery#

A variant doesn't hold a stock count. Send inventory_item_id, or an inline inventory_item to create one, and inventory_tracking becomes tracked: each sale draws from the inventory item, whose stock lives in inventory levels per Location. Several variants can share one item. Flint never infers tracking from product_type, so an untracked variant sells without a stock check. Send inventory_item_id: null on a variant update to stop tracking. Quantities change through the inventory endpoints, never through product updates; see Track inventory.

unit_weight and unit_dimensions describe one unit of what the variant sells, not a parcel: a variant for a pack of three mugs holds the measurements of the pack. Both are optional, and reads leave out the ones that aren't set. Flint returns the value and unit you sent without converting them. A value out of range, with more than 3 decimal places, an unknown unit, or a missing field returns 400 INVALID_REQUEST with detail code INVALID_VALUE and param naming the field, such as unit_weight.value. Package weight and dimensions on a shipment, and a custom order line's unit_weight and unit_dimensions, use the same shapes and limits.

When an order line is created, Flint records the variant's unit_weight and unit_dimensions on the line, and delivery quotes use those recorded values. A later variant edit changes new orders and subscription renewals, not existing lines. A variant without unit_weight has an unknown weight, so a delivery option that prices by weight isn't offered for a group that contains it. See Record a variant's weight and dimensions.

Categories#

Product writes take category_handles, a set of up to 100 handles of categories that already exist. Handles match exactly: Flint doesn't match names, change letter case, or create a category from a product write. A handle that matches no category returns 404 RESOURCE_NOT_FOUND with param naming the entry, such as category_handles[0], and details[0].resource_type set to category. Reads return each category as {category_id, handle, name}. A handle never changes when its category is renamed, so it is safe to store and to filter the list with category_handle.

Fields that replace the whole value#

Most updates are sparse: send only what changes, and null clears an optional field. These fields replace their whole value instead:

FieldWhereexpected_version
options and their valuesPATCH /v1/products/{product_id}Required
imagesProduct and variant PATCHRequired
category_handlesProduct PATCH. [] clears every category.Not needed
selected_optionsVariant PATCHOptional
gift_card_configurationVariant PATCH. null isn't accepted.Optional
unit_weight, unit_dimensionsVariant PATCH. Send every field of the object, or null to clear it. Each is independent of the other.Optional

A stale expected_version returns 409 VERSION_CONFLICT with details[0].current_version, including on a repeated DELETE. Keep the version your edit started from; Edit with the version you read explains why. Images covers galleries.

Find products through their variants#

GET /v1/products takes three exact filters that select a product through one of its variants:

FilterMatches the variant'sComparison
skuskuWhole value, ignoring case and accents, not trimmed. sku=cafe-12 selects Café-12; sku=Cafe-1 and sku= cafe-12 don't. 1 to 100 characters.
variant_external_reference_idexternal_reference_idByte for byte, keeping whitespace, case, and Unicode. 1 to 255 characters.
inventory_item_idinventory_item_idA valid inventory item ID

The list's external_reference_id filter matches the product's own external_reference_id, byte for byte, never a variant's. A variant's SKU and an inventory item's SKU are separate fields, and sku searches only the variant's, so filter by inventory_item_id to find what sells from an item.

When you send more than one variant filter, a single variant must satisfy all of them. sku=MUG-12&inventory_item_id=invi_01K0P7W6A4N9F3J2T8Q5R1C6XM returns the product only when one variant has that SKU and draws from that item. Inactive variants match, and archived variants never do, so an archived product never matches. A product appears once however many of its variants match.

Send each filter once, with a value. A repeated key, an empty or too-long value, or a malformed inventory_item_id returns 400 INVALID_REQUEST with detail code INVALID_VALUE and param naming the filter. A bracketed key such as sku[] returns detail code UNKNOWN_FIELD. A comma is part of the value, not a separator. The filters apply before pagination, and a valid value that matches nothing returns an empty page.

The matching variant#

When an exact variant filter is supplied, each returned product carries matched_variant: the variant_id, name, and sku of one matching variant, the first by position and then by variant_id. It names one variant even when several match.

cURL
curl "https://api.withflintpay.com/v1/products?inventory_item_id=invi_01K0P7W6A4N9F3J2T8Q5R1C6XM" \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "data": [
    {
      "product_id": "prod_01K0P7W6A4N9F3J2T8Q5R1C6XN",
      "name": "Ceramic mug",
      "matched_variant": {
        "variant_id": "var_01K0P7W6A4N9F3J2T8Q5R1C6XP",
        "name": "White",
        "sku": "MUG-12-WHITE"
      }
    }
  ]
}

The product is abridged. Exact filters choose matched_variant even when the request also sends query. With query and no exact filter, matched_variant names the first variant, in the same order, whose SKU contains the query text, and is absent when the query matched the product another way. Product reads by ID never include it.

To read every matching variant, page each product's GET /v1/products/{product_id}/variants with the same filters. The variant list takes sku and inventory_item_id under the same names and rules. Its external_reference_id is the variant's own reference, so send your variant_external_reference_id value there as external_reference_id.

Expand the modifier set#

Product and variant reads and lists accept expand=modifier_set, which adds the modifier set for modifier_set_id, with its groups and choices, or null when there is none. It needs commerce.catalog.read as well as commerce.products.read, and a key without both gets 403 INSUFFICIENT_SCOPE with param: expand. Expand related records covers how lists expand each page.

Webhooks#

EventSent when
product.createdA product was created. Each of its variants sends product_variant.created.
product.updatedA product's own fields, options, images, categories, or modifier set changed, including a status change between active and inactive
product.archivedA product was archived with all its variants. The variants send no events of their own.
product_variant.createdA variant was created, with its product or on its own
product_variant.updatedA variant's own fields, images, or modifier set changed, Flint assigned it a delivery profile, or it received a value from existing_variant_value
product_variant.archivedA variant was archived on its own

A variant write also changes its product's version, updated_at, and rollups such as price_range and variant_count, without a product event. Keep another system in sync covers mirroring a catalog from these events and backfilling it from the lists.

Catalog setupCreate products with sizes, categories, modifiers, and bundles, then sell them on an order.Track inventoryLink variants to inventory items and hold stock when an order is paid.Catalog eventsEvery catalog event and the fields that change without one.

The Product object#

An item offered in the merchant's catalog.

Returned by GET /v1/products/{product_id} and carried by the endpoints below.

Attributes

active_variant_countinteger

Number of active variants belonging to the product.

available_for_saleboolean

Whether the product is active and has at least one active variant.

categoriesarray of objectRequired

Always present. Empty collections are returned as [].

created_atstring

When this resource was created, as an RFC3339 timestamp.

default_variantobject

The product's default variant, when one is set.

default_variant_idstring

Identifier of the variant chosen as this product's default, when one is set.

delivery_configuration_statusenumRequired

Delivery readiness aggregated across the product's non-archived variants.

  • configured
  • action_required
  • not_applicable
descriptionstring

Description of the product shown to buyers.

external_reference_idstring

Caller-owned identifier for this resource in an external system.

imagesarray of objectRequired

Images attached to this resource, in display order. The first image is primary.

matched_variantobject

On product list rows, the first non-archived variant by position, then variant_id, that matches all exact variant filters. Without exact variant filters, identifies the first variant whose SKU matches query. Omitted when no variant matches; a product name or description match alone does not set it. Page the product's variants to find all matches.

merchant_idstring

Identifier of the merchant that owns this resource.

metadatamap of string

Caller-owned metadata stored on this resource.

modifier_setobject or null

Attached modifier set, included when expand=modifier_set is requested. Requires commerce.catalog.read in addition to the resource's read scope. Present only when `expand` includes `modifier_set`; `null` when expanded and there is no related modifier set.

modifier_set_idstring or nullRequired

Attached modifier set. Send null on update to remove it.

namestringRequired

Product name shown to buyers.

option_countinteger

Number of active options belonging to the product.

optionsarray of objectRequired

Product options and their non-archived values. Empty when the product has no options.

price_rangeobject

Lowest and highest unit prices among active variants. Omitted when there are no active variants or their currencies differ.

product_idstringRequired

Stable identifier of the product.

product_typeenumRequired

Kind of goods or service this product represents. New variants of digital products store digital_goods when line_item_tax_category is omitted; explicit categories are preserved.

  • physical
  • service
  • fee
  • digital
  • gift_card
statusenumRequired

Product lifecycle status. Archived products remain readable and cannot be edited.

  • active
  • inactive
  • archived
updated_atstring

When this resource last changed, as an RFC3339 timestamp.

variant_countinteger

Number of non-archived variants belonging to the product.

variantsarray of object

The variants this request created, ordered by `position` and then `variant_id`. Present only on the `POST /v1/products` response. Read variants later with `GET /v1/products/{product_id}/variants`.

versionintegerRequired

Current resource version. Send it as expected_version to reject a write if the resource has changed.

JSON
{
  "active_variant_count": 1,
  "available_for_sale": true,
  "categories": [
    {
      "category_id": "ctg_01KEZF7C00V7HCPYBVVD8PX976",
      "handle": "tickets",
      "name": "Tickets"
    },
    {
      "category_id": "ctg_01KEZF7C00VWQMSGG3M5G79GQR",
      "handle": "events",
      "name": "Events"
    }
  ],
  "created_at": "2026-03-17T14:30:00Z",
  "default_variant_id": "var_01KEZF7C00SDPKNHP9DAVYBQZ9",
  "delivery_configuration_status": "configured",
  "images": [],
  "merchant_id": "mer_01KEZF7C00MPP0T9S0KH5BB4CC",
  "metadata": {
    "event_id": "evt_123"
  },
  "modifier_set_id": null,
  "name": "General Admission",
  "option_count": 0,
  "options": [],
  "price_range": {
    "max_unit_price_money": {
      "amount": 2500,
      "currency": "USD"
    },
    "min_unit_price_money": {
      "amount": 2500,
      "currency": "USD"
    }
  },
  "product_id": "prod_01KEZF7C00027JTCXQ7TQF86X8",
  "product_type": "physical",
  "status": "active",
  "updated_at": "2026-03-17T14:30:00Z",
  "variant_count": 1,
  "version": 1
}

List products#

GET/v1/products

Requires scope commerce.products.read or commerce.products.write

Rate limit: read

Returns a paginated list of products for the authenticated merchant. Default order: created_at descending, then product_id descending.

Query parameters

page_sizeinteger

Maximum number of results to return, from 1 to 100. Defaults to 20.

page_tokenstring

Opaque next_page_token from the previous response. Omit on the first request. You may change `page_size` between pages. Reuse it with the same endpoint, filters, sort, and request context. Do not parse or construct tokens. An invalid, expired, or mismatched token returns a validation error.

product_typearray of enum

Filter by product type. Values are trimmed and deduplicated in first-seen order; empty or unknown entries are rejected. Values within this filter are ORed; separate filters are ANDed.

  • physical
  • service
  • fee
  • digital
  • gift_card
statusarray of enum

Filter by status. Omitted, the list leaves out archived rows; name archived to include them. Values are trimmed and deduplicated in first-seen order; empty or unknown entries are rejected. Values within this filter are ORed; separate filters are ANDed.

  • active
  • inactive
  • archived
category_handlestring

Filter by category handle.

external_reference_idstring

Exact match on the product's own `external_reference_id`. To find a product by a variant's reference, use `variant_external_reference_id`.

skustring

Full-value match on ProductVariant.sku, case- and accent-insensitive, with no trimming. All exact variant filters must match the same non-archived variant, including inactive variants. matched_variant is the first matching variant by position, then variant_id; exact predicates take precedence over query for this witness.

variant_external_reference_idstring

Byte-sensitive whole-value match on ProductVariant.external_reference_id, preserving whitespace, casing, and Unicode. All exact variant filters must match the same non-archived variant. matched_variant is one deterministic witness; page the nested variant list to enumerate all matches.

inventory_item_idstring

Exact match on ProductVariant.inventory_item_id. Multiple variants may share one inventory item; this does not match or synchronize the inventory item's SKU. On products, all exact variant filters must match the same non-archived variant.

querystring

Search across product ID, external reference ID, name, description, and SKU. Text fields match any part of the value, and %, _ and \ are ordinary characters, not wildcards. IDs match from the start and need the type prefix, such as prod_01.

delivery_profile_idstring

Filter products with at least one non-deleted variant assigned to this delivery profile.

delivery_configuration_statusarray of enum

Filter by aggregated delivery readiness across non-deleted variants. Values are trimmed and deduplicated in first-seen order; empty or unknown entries are rejected. Values within this filter are ORed; separate filters are ANDed.

  • configured
  • action_required
  • not_applicable
sort_byenum

Field to sort by. Defaults to created_at; ties are broken by product_id.

  • name
  • created_at
  • updated_at
sort_directionenum

Sort direction. Defaults to desc.

  • asc
  • desc
created_afterstring

Inclusive lower bound on created_at.

created_beforestring

Exclusive upper bound on created_at.

updated_afterstring

Inclusive lower bound on updated_at.

updated_beforestring

Exclusive upper bound on updated_at.

expandarray of enum

Include related objects in the response. Supported expansions: modifier_set. Include the requested related resources using scoped batch reads for this page. Each relationship requires its existing target read scopes. Expansion changes the representation without changing pagination tokens. Limits: at most 10 unique expand paths per request.

  • modifier_set

Response · 200

A page of products.

dataarray of objectRequired

The requested product data.

metaobject

Additional information about this response.

next_page_tokenstring

Token for the next page. Omitted when there are no more results.

request_idstringRequired

Identifier of this API request, useful when contacting support.

curl https://api.withflintpay.com/v1/products \
  -H "Flint-Version: 2026-09-07" \
  -H "Authorization: Bearer YOUR_API_KEY"

A page of products.

JSON
{
  "data": [
    {
      "active_variant_count": 1,
      "available_for_sale": true,
      "categories": [
        {
          "category_id": "ctg_01KEZF7C00V7HCPYBVVD8PX976",
          "handle": "tickets",
          "name": "Tickets"
        },
        {
          "category_id": "ctg_01KEZF7C00VWQMSGG3M5G79GQR",
          "handle": "events",
          "name": "Events"
        }
      ],
      "created_at": "2026-03-17T14:30:00Z",
      "default_variant_id": "var_01KEZF7C00SDPKNHP9DAVYBQZ9",
      "delivery_configuration_status": "configured",
      "images": [],
      "merchant_id": "mer_01KEZF7C00MPP0T9S0KH5BB4CC",
      "metadata": {
        "event_id": "evt_123"
      },
      "modifier_set_id": null,
      "name": "General Admission",
      "option_count": 0,
      "options": [],
      "price_range": {
        "max_unit_price_money": {
          "amount": 2500,
          "currency": "USD"
        },
        "min_unit_price_money": {
          "amount": 2500,
          "currency": "USD"
        }
      },
      "product_id": "prod_01KEZF7C00027JTCXQ7TQF86X8",
      "product_type": "physical",
      "status": "active",
      "updated_at": "2026-03-17T14:30:00Z",
      "variant_count": 1,
      "version": 1
    }
  ],
  "meta": {
    "api_version": "2026-09-07"
  },
  "request_id": "req_01KWM61KKS71NZR1XSQXVAK4HP"
}

Create product#

POST/v1/productsIdempotent

Requires scope commerce.products.write

Rate limit: write

Creates a product, the catalog item buyers purchase through orders, checkout, payment links, and subscriptions. Send `name`, `product_type`, and `variant_mode`. A `simple` product takes one `default_variant`; an `optioned` product takes `options` and `variants`, each variant selecting one value for every option. Price, SKU, and inventory settings live on variants. A SKU or barcode that another variant or bundle already uses fails with `ALREADY_EXISTS`, and a category handle that doesn't exist fails with `RESOURCE_NOT_FOUND`.

Request body

Send exactly one of these

The `simple` variant of `CreateProductRequest`.

category_handlesarray of string

Relationship set of category handles that must already exist. Each handle has 1 to 100 characters (Unicode code points). Unknown handles return RESOURCE_NOT_FOUND. Replaces the whole set when present; send [] to clear it. On PATCH, omission leaves it unchanged. Does not require expected_version.

default_variantobjectRequired

The single variant to create as the product's default. Send either default_variant or both options and variants.

descriptionstring

Description of the product shown to buyers.

external_reference_idstring

Caller-owned identifier for this resource in an external system.

imagesarray of one of

The complete desired gallery in display order. The first image is primary. Send [] to clear the gallery.

metadatamap of string

Caller-owned metadata. Values can't be empty strings. Keys starting with flint_ are reserved for Flint, are not returned, and cannot be written through the public API.

modifier_set_idstring or null

Attached modifier set. Send null on update to remove it.

namestringRequired

Product name shown to buyers.

product_typeenumRequired

Kind of goods or service this product represents. New variants of digital products store digital_goods when line_item_tax_category is omitted; explicit categories are preserved.

  • physical
  • service
  • fee
  • digital
  • gift_card
statusenum

Whether the product is active or inactive. An inactive product's variants cannot be sold.

  • active
  • inactive
variant_modeenumRequired

Selects the variant mode branch.

  • simple

Response · 201

The created product.

dataobjectRequired

The requested product data.

metaobject

Additional information about this response.

request_idstringRequired

Identifier of this API request, useful when contacting support.

curl -X POST https://api.withflintpay.com/v1/products \
  -H "Flint-Version: 2026-09-07" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: a-unique-key" \
  -d '{
    "category_handles": [
      "tickets",
      "events"
    ],
    "default_variant": {
      "sku": "ga-001",
      "unit_price_money": {
        "amount": 2500,
        "currency": "USD"
      }
    },
    "metadata": {
      "event_id": "evt_123"
    },
    "name": "General Admission",
    "product_type": "physical",
    "variant_mode": "simple"
  }'

Get product#

GET/v1/products/{product_id}

Requires scope commerce.products.read or commerce.products.write

Rate limit: read

Returns a single product by ID.

Path parameters

product_idstringRequired

Flint product ID.

Query parameters

expandarray of enum

Include related objects in the response. Supported expansions: modifier_set. Expand the attached modifier set. Limits: at most 10 unique expand paths per request.

  • modifier_set

Response · 200

The requested product.

Same response as Create product.

curl https://api.withflintpay.com/v1/products/prod_01KEZF7C00BVSHCNWVH528HB66 \
  -H "Flint-Version: 2026-09-07" \
  -H "Authorization: Bearer YOUR_API_KEY"

The requested product.

JSON
{
  "data": {
    "active_variant_count": 1,
    "available_for_sale": true,
    "categories": [
      {
        "category_id": "ctg_01KEZF7C00V7HCPYBVVD8PX976",
        "handle": "tickets",
        "name": "Tickets"
      },
      {
        "category_id": "ctg_01KEZF7C00VWQMSGG3M5G79GQR",
        "handle": "events",
        "name": "Events"
      }
    ],
    "created_at": "2026-03-17T14:30:00Z",
    "default_variant_id": "var_01KEZF7C00SDPKNHP9DAVYBQZ9",
    "delivery_configuration_status": "configured",
    "images": [],
    "merchant_id": "mer_01KEZF7C00MPP0T9S0KH5BB4CC",
    "metadata": {
      "event_id": "evt_123"
    },
    "modifier_set_id": null,
    "name": "General Admission",
    "option_count": 0,
    "options": [],
    "price_range": {
      "max_unit_price_money": {
        "amount": 2500,
        "currency": "USD"
      },
      "min_unit_price_money": {
        "amount": 2500,
        "currency": "USD"
      }
    },
    "product_id": "prod_01KEZF7C00027JTCXQ7TQF86X8",
    "product_type": "physical",
    "status": "active",
    "updated_at": "2026-03-17T14:30:00Z",
    "variant_count": 1,
    "version": 1
  },
  "meta": {
    "api_version": "2026-09-07"
  },
  "request_id": "req_01KWM61KKS71NZR1XSQXVAK4HP"
}

Update product#

PATCH/v1/products/{product_id}Idempotent

Requires scope commerce.products.write

Rate limit: write

Applies a sparse update to product-parent fields. When category_handles is present, it replaces the full category list; send [] to clear it. Sellable price, SKU, and inventory live on variants. Fails with `ACTION_NOT_ALLOWED` and reason `archived` when the resource, or the parent it belongs to, is archived. The error names the archived resource and its `current_status`.

Path parameters

product_idstringRequired

Flint product ID.

Request body

category_handlesarray of string

Relationship set of category handles that must already exist. Each handle has 1 to 100 characters (Unicode code points). Unknown handles return RESOURCE_NOT_FOUND. Replaces the whole set when present; send [] to clear it. On PATCH, omission leaves it unchanged. Does not require expected_version.

default_variant_idstring or null

Send null to clear it.

descriptionstring or null

Send null to clear it.

expected_versioninteger

Resource version last read by the caller. Required when options or images is present.

external_reference_idstring or null

Caller-owned identifier for this resource in an external system. Send null to clear it.

imagesarray of one of

The complete desired gallery in display order. The first image is primary. Send [] to clear the gallery.

metadatamap of string or null

Caller-owned metadata. Omit this field to leave metadata unchanged. Send an object to merge by key, set a key to null to remove it, or set metadata to null to clear all caller-owned metadata. An empty object makes no change. Values can't be empty strings. Keys starting with flint_ are reserved for Flint, are not returned, and cannot be written through the public API. This operation also limits the metadata object you send, before merging: at most 50 keys, counting null deletions, each key 1 to 256 Unicode code points, and at most 32768 UTF-8 bytes of keys plus values.

modifier_set_idstring or null

Attached modifier set. Send null on update to remove it.

namestring

Product name shown to buyers.

optionsarray of object

Replaces all product options and values atomically. Include existing option_id and option_value_id values to retain members. Omitted members are archived. Null is not accepted.

product_typeenum

Kind of goods or service this product represents. Changing to digital fills variants' unset line_item_tax_category with digital_goods and advances their versions. Explicit categories are preserved. Returns 409 ACTION_NOT_ALLOWED with reason mixed_tax_treatment and blocking_resources if a containing bundle would become mixed.

  • physical
  • service
  • fee
  • digital
  • gift_card
statusenum

Whether the product is active or inactive. An inactive product's variants cannot be sold.

  • active
  • inactive

Response · 200

The updated product.

Same response as Create product.

curl -X PATCH https://api.withflintpay.com/v1/products/prod_01KEZF7C00BVSHCNWVH528HB66 \
  -H "Flint-Version: 2026-09-07" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: a-unique-key" \
  -d '{
    "category_handles": [
      "vip",
      "events"
    ],
    "expected_version": 1,
    "name": "VIP Admission",
    "status": "inactive"
  }'

Retire product#

DELETE/v1/products/{product_id}Idempotent

Requires scope commerce.products.write

Rate limit: write

Archives a product and returns its final state. Returns the archived resource as a read does. Deleting an already archived resource returns it again without changing its version or `updated_at`, sending a webhook, or checking references. A stale `expected_version` fails with `VERSION_CONFLICT` and the `current_version`, even when the resource is already archived.

Path parameters

product_idstringRequired

Flint product ID.

Query parameters

expected_versioninteger

Optional product version last read by the caller.

Response · 200

The archived product.

Same response as Create product.

curl -X DELETE https://api.withflintpay.com/v1/products/prod_01KEZF7C00BVSHCNWVH528HB66 \
  -H "Flint-Version: 2026-09-07" \
  -H "Authorization: Bearer YOUR_API_KEY"

List product options#

GET/v1/products/{product_id}/options

Requires scope commerce.products.read or commerce.products.write

Rate limit: read

List product options. Default order: position ascending, then option_id ascending.

Path parameters

product_idstringRequired

Flint product ID.

Query parameters

page_sizeinteger

Maximum number of results to return, from 1 to 100. Defaults to 20.

page_tokenstring

Opaque next_page_token from the previous response. Omit on the first request. You may change `page_size` between pages. Reuse it with the same endpoint, filters, sort, and request context. Do not parse or construct tokens. An invalid, expired, or mismatched token returns a validation error.

statusarray of enum

Filter by status. Omitted, the list leaves out archived rows; name archived to include them. Values are trimmed and deduplicated in first-seen order; empty or unknown entries are rejected. Values within this filter are ORed; separate filters are ANDed.

  • active
  • inactive
  • archived

Response · 200

Successful response.

dataarray of objectRequired

The requested product option data.

metaobject

Additional information about this response.

next_page_tokenstring

Token for the next page. Omitted when there are no more results.

request_idstringRequired

Identifier of this API request, useful when contacting support.

curl https://api.withflintpay.com/v1/products/prod_01KEZF7C00BVSHCNWVH528HB66/options \
  -H "Flint-Version: 2026-09-07" \
  -H "Authorization: Bearer YOUR_API_KEY"

Successful response.

JSON
{
  "data": [],
  "meta": {
    "api_version": "2026-09-07"
  },
  "next_page_token": "example",
  "request_id": "req_01KWM6BH96R4V5XE6XBWK603S4"
}

Get product option#

GET/v1/products/{product_id}/options/{option_id}

Requires scope commerce.products.read or commerce.products.write

Rate limit: read

Returns one option of a product, such as size or color, with its values and position.

Path parameters

product_idstringRequired

Flint product ID.

option_idstringRequired

Flint product option ID.

Response · 200

The product option.

dataobjectRequired

The requested product option data.

metaobject

Additional information about this response.

request_idstringRequired

Identifier of this API request, useful when contacting support.

curl https://api.withflintpay.com/v1/products/prod_01KEZF7C00BVSHCNWVH528HB66/options/opt_01KEZF7C008R5Q9BG9B4B4KQH8 \
  -H "Flint-Version: 2026-09-07" \
  -H "Authorization: Bearer YOUR_API_KEY"

The product option.

JSON
{
  "data": {
    "created_at": "2026-03-17T14:30:00Z",
    "name": "Seat",
    "option_id": "opt_01KEZF7C00GWX8X74KCS8FMPTX",
    "position": 0,
    "product_id": "prod_01KEZF7C00027JTCXQ7TQF86X8",
    "status": "active",
    "updated_at": "2026-03-17T14:30:00Z",
    "values": [
      {
        "created_at": "2026-03-17T14:30:00Z",
        "option_value_id": "optv_01KEZF7C00HR3J3B0BM7EG3121",
        "position": 0,
        "status": "active",
        "updated_at": "2026-03-17T14:30:00Z",
        "value": "Balcony"
      }
    ]
  },
  "meta": {
    "api_version": "2026-09-07"
  },
  "request_id": "req_01KWM6Q1QWTAXD3WYFF4HPEYTJ"
}

List product variants#

GET/v1/products/{product_id}/variants

Requires scope commerce.products.read or commerce.products.write

Rate limit: read

List product variants. Default order: position ascending, then variant_id ascending.

Path parameters

product_idstringRequired

Flint product ID.

Query parameters

page_sizeinteger

Maximum number of results to return, from 1 to 100. Defaults to 20.

page_tokenstring

Opaque next_page_token from the previous response. Omit on the first request. You may change `page_size` between pages. Reuse it with the same endpoint, filters, sort, and request context. Do not parse or construct tokens. An invalid, expired, or mismatched token returns a validation error.

statusarray of enum

Filter by status. Omitted, the list leaves out archived rows; name archived to include them. Values are trimmed and deduplicated in first-seen order; empty or unknown entries are rejected. Values within this filter are ORed; separate filters are ANDed.

  • active
  • inactive
  • archived
external_reference_idstring

Byte-sensitive whole-value match on ProductVariant.external_reference_id, preserving whitespace, casing, and Unicode.

skustring

Full-value match on ProductVariant.sku, case- and accent-insensitive, with no trimming. Does not search the inventory item's SKU.

inventory_item_idstring

Exact match on ProductVariant.inventory_item_id. Multiple variants may share one inventory item; this does not match or synchronize the inventory item's SKU. On products, all exact variant filters must match the same non-archived variant.

querystring

Search across variant ID, external reference ID, name, SKU, and barcode. Text fields match any part of the value, and %, _ and \ are ordinary characters, not wildcards. IDs match from the start and need the type prefix, such as var_01.

sort_byenum

Field to sort by. Defaults to position; ties are broken by variant_id.

  • position
  • created_at
  • updated_at
  • unit_price_money
sort_directionenum

Sort direction. Defaults to asc for position and desc for other sort fields.

  • asc
  • desc
delivery_profile_idstring

Filter by assigned delivery profile ID.

delivery_configuration_statusarray of enum

Filter by delivery readiness. Values are trimmed and deduplicated in first-seen order; empty or unknown entries are rejected. Values within this filter are ORed; separate filters are ANDed.

  • configured
  • action_required
  • not_applicable
created_afterstring

Inclusive lower bound on created_at.

created_beforestring

Exclusive upper bound on created_at.

updated_afterstring

Inclusive lower bound on updated_at.

updated_beforestring

Exclusive upper bound on updated_at.

expandarray of enum

Include related objects in the response. Supported expansions: modifier_set. Include the requested related resources using scoped batch reads for this page. Each relationship requires its existing target read scopes. Expansion changes the representation without changing pagination tokens. Limits: at most 10 unique expand paths per request.

  • modifier_set

Response · 200

Successful response.

dataarray of objectRequired

The requested product variant data.

metaobject

Additional information about this response.

next_page_tokenstring

Token for the next page. Omitted when there are no more results.

request_idstringRequired

Identifier of this API request, useful when contacting support.

curl https://api.withflintpay.com/v1/products/prod_01KEZF7C00BVSHCNWVH528HB66/variants \
  -H "Flint-Version: 2026-09-07" \
  -H "Authorization: Bearer YOUR_API_KEY"

Successful response.

JSON
{
  "data": [
    {
      "available_for_sale": true,
      "created_at": "2026-03-17T14:30:00Z",
      "delivery_configuration_status": "configured",
      "effective_images": [],
      "images": [],
      "images_inherited": true,
      "inventory_tracking": "not_tracked",
      "merchant_id": "mer_01KEZF7C00MPP0T9S0KH5BB4CC",
      "modifier_set_id": null,
      "position": 0,
      "product_id": "prod_01KEZF7C00027JTCXQ7TQF86X8",
      "selected_options": [],
      "status": "active",
      "unit_price_money": {
        "amount": 2500,
        "currency": "USD"
      },
      "updated_at": "2026-03-17T14:30:00Z",
      "variant_id": "var_01KEZF7C00SDPKNHP9DAVYBQZ9",
      "version": 1
    }
  ],
  "meta": {
    "api_version": "2026-09-07"
  },
  "request_id": "req_01KWM66H24G2C0HA5CB33ERJ2P"
}

Create product variant#

POST/v1/products/{product_id}/variantsIdempotent

Requires scope commerce.products.write

Rate limit: write

Adds a variant to a product that has options. The variant selects one value for every option. Two variants can't select the same combination, and a SKU or barcode that another variant or bundle already uses is refused; both fail with `ALREADY_EXISTS`. A product without active options fails with `ACTION_NOT_ALLOWED` and reason `configuration_required`: change a simple product through its default variant instead. Fails with `ACTION_NOT_ALLOWED` and reason `archived` when the resource, or the parent it belongs to, is archived. The error names the archived resource and its `current_status`.

Path parameters

product_idstringRequired

Flint product ID.

Request body

barcodestring

Barcode assigned to this sellable item.

delivery_profile_idstring

Identifier of the delivery profile to assign to this variant.

external_reference_idstring

Caller-owned identifier for this resource in an external system.

gift_card_configurationobject

Only valid for product_type gift_card. Omission creates a fixed denomination at unit_price_money. Discounted pricing requires an explicit face_value_money and price_mode discounted.

imagesarray of one of

The complete desired gallery in display order. The first image is primary. Send [] to clear the gallery.

inventory_itemobject

New inventory item to create and track for this variant. Send either inventory_item or inventory_item_id.

inventory_item_idstring

Identifier of an existing inventory item to track for this variant. Send either inventory_item_id or inventory_item.

line_item_tax_categoryenum

Flint line-item tax category to store. Omit for a digital product to store digital_goods; explicit categories are preserved. For other product types, omission uses the merchant default.

  • general
  • physical_goods
  • digital_goods
  • software
  • saas
  • services
  • professional_services
  • food
  • prepared_food
  • clothing
  • medical_goods
  • admission
metadatamap of string

Caller-owned metadata. Values can't be empty strings. Keys starting with flint_ are reserved for Flint, are not returned, and cannot be written through the public API.

modifier_set_idstring or null

Attached modifier set. Send null on update to remove it.

namestring

Variant name that distinguishes it from the product's other variants.

positioninteger

Display order among the product's variants, starting at zero. When omitted in the variants array on product create, defaults to the variant's array index. For default_variant, defaults to zero. When creating a variant separately, defaults to the highest position among the product's variants plus one, or zero for the first variant.

selected_optionsarray of object

Option name and value selections for this variant. When present on an update, replaces the entire selection. Omit to leave unchanged. Select one active value for every active option. null is invalid.

skustring

Stock keeping unit assigned to this sellable item.

statusenum

Whether the variant is active or inactive. An inactive variant cannot be sold.

  • active
  • inactive
taxableboolean

Whether this item is subject to tax.

unit_dimensionsobject

Per-unit measurement. Omitted when unknown.

unit_price_moneyobjectRequired

Price charged for one unit, in the currency's smallest unit.

unit_weightobject

Per-unit measurement. Omitted when unknown.

Response · 201

The created product variant.

dataobjectRequired

The requested product variant data.

metaobject

Additional information about this response.

request_idstringRequired

Identifier of this API request, useful when contacting support.

curl -X POST https://api.withflintpay.com/v1/products/prod_01KEZF7C00BVSHCNWVH528HB66/variants \
  -H "Flint-Version: 2026-09-07" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: a-unique-key" \
  -d '{
    "sku": "ga-balcony-001",
    "unit_price_money": {
      "amount": 2500,
      "currency": "USD"
    }
  }'

Get product variant#

GET/v1/products/{product_id}/variants/{variant_id}

Requires scope commerce.products.read or commerce.products.write

Rate limit: read

Returns one variant of a product, including its price, SKU, selected options, inventory settings, and delivery configuration status.

Path parameters

product_idstringRequired

Flint product ID.

variant_idstringRequired

Flint product variant ID.

Query parameters

expandarray of enum

Include related objects in the response. Supported expansions: modifier_set. Expand the attached modifier set. Limits: at most 10 unique expand paths per request.

  • modifier_set

Response · 200

The product variant.

Same response as Create product variant.

curl https://api.withflintpay.com/v1/products/prod_01KEZF7C00BVSHCNWVH528HB66/variants/var_01KEZF7C004GSH2KT8CNYVMHGX \
  -H "Flint-Version: 2026-09-07" \
  -H "Authorization: Bearer YOUR_API_KEY"

The product variant.

JSON
{
  "data": {
    "available_for_sale": true,
    "created_at": "2026-03-17T14:30:00Z",
    "delivery_configuration_status": "configured",
    "effective_images": [],
    "images": [],
    "images_inherited": true,
    "inventory_tracking": "not_tracked",
    "merchant_id": "mer_01KEZF7C00MPP0T9S0KH5BB4CC",
    "modifier_set_id": null,
    "position": 0,
    "product_id": "prod_01KEZF7C00027JTCXQ7TQF86X8",
    "selected_options": [],
    "status": "active",
    "unit_price_money": {
      "amount": 2500,
      "currency": "USD"
    },
    "updated_at": "2026-03-17T14:30:00Z",
    "variant_id": "var_01KEZF7C00SDPKNHP9DAVYBQZ9",
    "version": 1
  },
  "meta": {
    "api_version": "2026-09-07"
  },
  "request_id": "req_01KWM66H24G2C0HA5CB33ERJ2P"
}

Update product variant#

PATCH/v1/products/{product_id}/variants/{variant_id}Idempotent

Requires scope commerce.products.write

Rate limit: write

Applies a sparse update to a variant: only the fields you send change. A SKU or barcode that another variant or bundle already uses, or an option combination that another variant selects, fails with `ALREADY_EXISTS`. Changes to name, price, SKU, and similar fields apply to new checkouts, links, and plans; existing orders keep what they were charged. Setting the variant inactive, or changing its price currency, fails with `RESOURCE_IN_USE` while an open order, checkout session, active payment link, subscription, subscription plan, or active bundle uses it. Send `expected_version` to reject the update when the variant changed since you read it. Fails with `ACTION_NOT_ALLOWED` and reason `archived` when the resource, or the parent it belongs to, is archived. The error names the archived resource and its `current_status`.

Path parameters

product_idstringRequired

Flint product ID.

variant_idstringRequired

Flint product variant ID.

Request body

barcodestring or null

Send null to clear it.

delivery_profile_idstring or null

Send null to clear it.

expected_versioninteger

Resource version last read by the caller. Required when images is present.

external_reference_idstring or null

Caller-owned identifier for this resource in an external system. Send null to clear it.

gift_card_configurationobject

Replaces the complete gift card configuration. Omission preserves it. Null is not accepted. Include expected_version to reject a concurrent variant change.

imagesarray of one of

Replaces the authored variant gallery atomically. Send [] to clear it and resume product-image inheritance.

inventory_itemobject

New inventory item to create and track for this variant. Send either inventory_item or inventory_item_id.

inventory_item_idstring or null

Inventory item tracked by this variant. Send null to stop tracking inventory.

line_item_tax_categoryenum or null

Flint line-item tax category to store. Send null to clear it and use the merchant default. Returns 409 ACTION_NOT_ALLOWED with reason mixed_tax_treatment and blocking_resources if a containing bundle would become mixed.

  • general
  • physical_goods
  • digital_goods
  • software
  • saas
  • services
  • professional_services
  • food
  • prepared_food
  • clothing
  • medical_goods
  • admission
  • null
metadatamap of string or null

Caller-owned metadata. Omit this field to leave metadata unchanged. Send an object to merge by key, set a key to null to remove it, or set metadata to null to clear all caller-owned metadata. An empty object makes no change. Values can't be empty strings. Keys starting with flint_ are reserved for Flint, are not returned, and cannot be written through the public API. This operation also limits the metadata object you send, before merging: at most 50 keys, counting null deletions, each key 1 to 256 Unicode code points, and at most 32768 UTF-8 bytes of keys plus values.

modifier_set_idstring or null

Attached modifier set. Send null on update to remove it.

namestring or null

Send null to clear it.

positioninteger

Display order among the product's variants, starting at zero.

selected_optionsarray of object

Option name and value selections for this variant. When present on an update, replaces the entire selection. Omit to leave unchanged. Select one active value for every active option. null is invalid.

skustring or null

Send null to clear it.

statusenum

Whether the variant is active or inactive. An inactive variant cannot be sold.

  • active
  • inactive
taxableboolean

Whether this item is subject to tax.

unit_dimensionsobject or null

Replaces the complete object. Send null to clear it.

unit_price_moneyobject

Price charged for one unit, in the currency's smallest unit.

unit_weightobject or null

Replaces the complete object. Send null to clear it.

Response · 200

The updated product variant.

Same response as Create product variant.

curl -X PATCH https://api.withflintpay.com/v1/products/prod_01KEZF7C00BVSHCNWVH528HB66/variants/var_01KEZF7C004GSH2KT8CNYVMHGX \
  -H "Flint-Version: 2026-09-07" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: a-unique-key" \
  -d '{
    "expected_version": 1,
    "name": "Balcony admission"
  }'

Retire product variant#

DELETE/v1/products/{product_id}/variants/{variant_id}Idempotent

Requires scope commerce.products.write

Rate limit: write

Archives a variant and returns its final state. Returns the archived resource as a read does. Deleting an already archived resource returns it again without changing its version or `updated_at`, sending a webhook, or checking references. A stale `expected_version` fails with `VERSION_CONFLICT` and the `current_version`, even when the resource is already archived.

Path parameters

product_idstringRequired

Flint product ID.

variant_idstringRequired

Flint product variant ID.

Query parameters

expected_versioninteger

Optional product variant version last read by the caller.

Response · 200

The archived product variant.

Same response as Create product variant.

curl -X DELETE https://api.withflintpay.com/v1/products/prod_01KEZF7C00BVSHCNWVH528HB66/variants/var_01KEZF7C004GSH2KT8CNYVMHGX \
  -H "Flint-Version: 2026-09-07" \
  -H "Authorization: Bearer YOUR_API_KEY"

Was this helpful?