Use Product Add-Ons with Blocks and the Store API

Use this reference when the storefront uses WooCommerce's Add to Cart with Options Block, Cart and Checkout Blocks, or a headless client. The browser submits customer intent; POM resolves the current publication, conditions, availability, and price on the server.

Prerequisites

  • Enable Product Add-Ons under Settings → POM Tweaks → E-Commerce → Products.
  • Publish an assigned group or a product-specific definition on a public product.
  • Keep the official WooCommerce Product Add-Ons extension inactive while POM owns the storefront.
  • Keep the WooCommerce Nonce or Cart-Token returned for the current cart when making direct Store API requests.

Use WooCommerce Blocks

  1. Go to Appearance → Editor and open the single-product template used by the store.
  2. Add or keep WooCommerce's Add to Cart with Options Block.
  3. Save the template and open an assigned product.
  4. Complete its required add-on fields and add it to the cart.
  5. Open the Cart and Checkout Blocks and confirm each accepted label and value appears below the product.

No separate Product Add-Ons field block is required. Classic and Block product forms execute the same server validation and pricing. A product that requires configuration links to its product page instead of using an archive AJAX add-to-cart shortcut.

Discover the public product model

Read one product before rendering a headless form:

GET /wp-json/wc/store/v1/products/123

The response uses extensions.pom-tweaks-product-addons:

Property Purpose
has_addons Indicates whether the product currently has configurable fields.
configuration Contains the published contexts, fields, options, conditions, presentation data, prices, and current availability.
configuration_error Contains a localized reason when the configuration cannot be exposed safely.
summary Contains a lightweight structural summary only when explicitly requested.

The current public configuration uses schema_version: 6. Treat the schema version as a compatibility signal, not an entity identity. Use context, field, and option UUIDs as identities; labels are translated display text and must never be submitted as identifiers.

The public model includes the effective configuration for the current product or variation, language, currency, country zone, publication generation, conditions, schedules, and reusable-choice availability. It does not expose private configuration for other zones or unrestricted capacity totals. Request it again before submission when a page has remained open for a long time or when the customer changes market context.

The same public representation is available directly:

GET /wp-json/pom-tweaks/v1/product-add-ons/products/123

That direct response is non-cacheable because schedules and shared capacity may change without editing the product.

Keep catalog requests lightweight

For a catalog that only needs discovery, request the extension summary:

GET /wp-json/wc/store/v1/products?per_page=100&_fields=id,name,extensions.pom-tweaks-product-addons.has_addons,extensions.pom-tweaks-product-addons.summary

The summary reports the schema version, an opaque configuration_version, and context, field, configurable-field, and option counts. Compare the opaque version only for equality; do not parse it or use it as a record ID. Fetch the individual product before rendering its form.

If a collection does not need Product Add-Ons, omit extensions from _fields, for example _fields=id,name,prices. POM then skips Product Add-Ons resolution for those rows. Requesting extensions, the complete POM namespace, or configuration returns the full published representation.

Public models are separated by site, product, configuration version, language, currency, schema version, and hidden-option mode. POM removes WooCommerce's product-only Last-Modified header when Product Add-Ons data is selected because a shared group can change without editing each assigned product.

Add a configured product to the cart

Post UUID values from the current model to WooCommerce's normal endpoint:

POST /wp-json/wc/store/v1/cart/add-item
{
  "id": 123,
  "quantity": 2,
  "pom_product_addons": {
    "context-uuid": {
      "single-choice-field-uuid": "option-uuid",
      "checkbox-field-uuid": ["first-option-uuid", "second-option-uuid"],
      "text-field-uuid": "Customer value",
      "custom-price-field-uuid": "12.50"
    }
  }
}

For a variable product, id is the selected, published variation ID. Include WooCommerce's normal variation data when required by that Store API version.

Do not send a calculated add-on price. The server rejects the complete request when it contains unknown identifiers, inactive fields, unavailable choices, invalid customer-defined prices, or missing required fields. Nothing is added partially.

Clients written for the official extension may instead send addons_configuration with the numeric field IDs and positional option indexes returned by the official-compatible APIs. Do not send both pom_product_addons and addons_configuration in one request.

Read cart data

Accepted cart items expose POM results under extensions.pom-tweaks-product-addons. The response can include:

  • has_addons, flat_fee_total, and the current purchase charge_context;
  • the server-applied economic_result and bounded regional_context;
  • a session-bound edit_token and safe edit_url.

Official-compatible clients also receive extensions.addons.addons_data. Store API raw_prices remain integer strings at WooCommerce's advertised raw-price precision.

A flat fee is charged once per cart line. WooCommerce distributes that line fee across quantity when it presents a unit price. Quantity-based and percentage-based amounts scale with product quantity. The selected variation supplies the authoritative base price for percentage calculations.

Edit a cart item

The cart item's edit_url restores its non-file selections on the product form. Browsers cannot repopulate a local file input, so a required file must be selected again.

A headless client can edit atomically through:

POST /wp-json/wc/store/v1/cart/extensions

Use namespace pom-tweaks-product-addons-edit-item. Its data object accepts edit_token, product or variation id, optional quantity and variation, and either pom_product_addons or addons_configuration. A stale or invalid edit leaves the original cart line unchanged.

Upload a file from a headless client

The JSON add-to-cart request cannot contain file bytes.

  1. Send a multipart file request to POST /wp-json/pom-tweaks/v1/product-addons/files.
  2. Send the current WordPress REST nonce, Store API nonce, or Cart-Token.
  3. Read the opaque file_id from the successful response.
  4. Submit that file_id as the file field value in pom_product_addons, using the same cart identity.

POM rechecks ownership, the active field, filename, extension, detected MIME type, size, and current configuration. It never accepts a public URL or filesystem path as a new file selection.

Operational limits

Oversized or excessively nested input is rejected as one operation; it is never truncated. Important defaults are:

Limit Default
Effective groups / fields / options per product 50 / 200 / 5,000
Submitted values per cart line 200
Customer text / decoded request payload 10,000 characters / 1 MiB
Runtime array depth / entries 8 / 5,000
Files per cart line 10
File size / combined file size 10 MiB / 20 MiB before platform limits
Public add-to-cart attempts 60 per WooCommerce session per 60 seconds
Multipart upload attempts 10 per WooCommerce session per 60 seconds

The effective file limit is also bounded by PHP, WordPress, and multisite settings. Rejected size requests use HTTP 413 where applicable; session rate limits use HTTP 429.

Measured stores can adjust the bounded limit array with pom_tweaks_product_addons_operational_limits. Keep the callback in a site plugin and test the intended boundary plus one value above it. Lowering an effective-model limit below an existing configuration makes new add-on purchases unavailable until the configuration is reduced; historical order snapshots remain readable.

pom_tweaks_product_addons_limit_rejected provides privacy-safe rejection metadata for monitoring. It excludes customer values, filenames, URLs, request bodies, user IDs, session IDs, and IP addresses.

Verify the integration

  1. Submit one valid and one incomplete selection; confirm the incomplete request adds no line.
  2. Add the same product twice with different values and confirm the lines remain separate.
  3. Change quantity and confirm a flat fee remains once per line while per-unit adjustments scale.
  4. Repeat with two variations and confirm each selected variation supplies its own base and purchasability.
  5. Complete a Block checkout and compare the cart, order line, order REST response, and webhook values.
  6. Upload a permitted file, submit its file_id, and confirm another cart session cannot use that identifier.
  7. Edit a cart line with its current token, then retry the stale token and confirm the second request changes nothing.

Troubleshooting

  • Missing fields: confirm the group and reusable choices are published, the product matches the assignment, and the template contains Add to Cart with Options.
  • Rejected selection after rendering: fetch the product again; a publication, condition, schedule, capacity, language, currency, or country may have changed.
  • Rejected file ID: confirm the upload and cart request use the same nonce or Cart-Token and that the field still permits the detected type.
  • Different client preview and cart price: use the server result. Browser previews are advisory.
  • Missing extension data in a collection: check _fields; omitting extensions intentionally skips Product Add-Ons resolution.

Related: Cart and order persistence, Storefront fields and pricing, and Official Product Add-Ons compatibility.