Cart actions

Change carts, discount codes, and restock requests through secure hosted forms.

POST /cart/add
{% form 'cart_add' %}

quantity defaults to 1; variant_id is required for products with variants.

Form fields

product_id Required

Product to add.

variant_id Optional

Required when the product has variants.

quantity Optional

Defaults to 1.

properties[<namespace>.<key>]… Optional

Declared buyer-writable cart-line properties.

_format Optional

Set to json when submitting with fetch; omit for the normal browser redirect.

client_mutation_id Optional

Unique retry key, up to 128 characters. Reusing it for the same action prevents a successful mutation from being applied twice for 24 hours.

Validation

Positive product/variant ids; variant must belong to the product; quantity, inventory, purchase state, B2B availability, and declared buyer-writable properties are validated server-side.

Limits

Quantity defaults to 1. The final line quantity cannot exceed product.quantity_max (or the selected variant's quantity_max), which resolves stock, backorder capacity, and the merchant's variant → product → store order cap. At most 20 properties per line.

Response

{
  success,
  cart,
  user_errors
}

cart is the current enriched cart and is unchanged when refused.

POST /cart/clear
{% form 'cart_clear' %}

Empties the cart.

Form fields

_format Optional

Set to json when submitting with fetch; omit for the normal browser redirect.

client_mutation_id Optional

Unique retry key, up to 128 characters. Reusing it for the same action prevents a successful mutation from being applied twice for 24 hours.

Validation

Requires a valid visitor/customer cart owner and is unavailable on the B2B runtime.

Limits

No action-specific numeric limit.

Response

{
  success,
  cart,
  user_errors
}
POST /cart/notify-me
{% form 'cart_notify_me' %}

Back-in-stock subscription; guests supply contact details, customers default to their account email.

Form fields

product_id Required

Out-of-stock product to watch.

variant_id Optional

Specific variant to watch.

contact_type Optional

email or sms; defaults to email.

email Optional

Required for guests using email contact.

phone Optional

Required when contact_type is sms.

Validation

Positive product/variant ids; restock alerts and guest requests must be enabled; contact_type is email or sms. Logged-in email requests use the account email. SMS requires an active messaging provider and stores a valid E.164 number.

Limits

Email is limited to 256 characters. Requests expire after the merchant-configured 1–365 day window. Repeated requests are not deduplicated.

Response

{
  success,
  response,
  user_errors
}

response is the localized saved message.

POST /cart/remove
{% form 'cart_remove' %}

Removes the product (or specific variant) line.

Form fields

product_id Required

Cart line's product.

variant_id Optional

Cart line's variant, when the product has variants.

line_id Optional

Required to target a line carrying properties.

_format Optional

Set to json when submitting with fetch; omit for the normal browser redirect.

client_mutation_id Optional

Unique retry key, up to 128 characters. Reusing it for the same action prevents a successful mutation from being applied twice for 24 hours.

Validation

Positive product/variant ids; line_id targets one exact line. Bare product/variant targeting only matches a property-less line.

Limits

No action-specific numeric limit.

Response

{
  success,
  cart,
  user_errors
}
POST /cart/update
{% form 'cart_update' %}

Sets the line to quantity; 0 removes it.

Form fields

product_id Required

Cart line's product.

variant_id Optional

Cart line's variant, when the product has variants.

quantity Required

New quantity; 0 removes the line.

line_id Optional

Required to target a line carrying properties.

_format Optional

Set to json when submitting with fetch; omit for the normal browser redirect.

client_mutation_id Optional

Unique retry key, up to 128 characters. Reusing it for the same action prevents a successful mutation from being applied twice for 24 hours.

Validation

Positive product/variant ids and integer quantity; quantity 0 removes. line_id is required for a line with properties and cannot select another visitor's line.

Limits

The final line quantity cannot exceed the current cart line's quantity plus quantity_can_add. Inventory, backorder, preorder, and the resolved variant → product → store order cap are enforced.

Response

{
  success,
  cart,
  user_errors
}

cart is the current enriched cart and is unchanged when refused.

POST /cart/discount/apply
{% form 'discount_code_apply' %}

Validates the code against the current cart, customer, market, and stacking rules, then stores it only when it can apply. Unknown or ineligible codes leave the cart unchanged.

Form fields

code Required

The discount code to apply.

_format Optional

Set to json when submitting with fetch; omit for the normal browser redirect.

client_mutation_id Optional

Unique retry key, up to 128 characters. Reusing it for the same action prevents a successful mutation from being applied twice for 24 hours.

Validation

Non-empty code; the same store-scoped pricing evaluator used to advertise available discounts validates code existence, activity, eligibility, stacking, market, customer, and current cart conditions before the cart changes.

Limits

Applied-code count and discount limits come from order-pricing configuration.

Response

{
  success,
  cart,
  user_errors
}

ineligible codes return the unchanged current cart.

POST /cart/discount/remove
{% form 'discount_code_remove' %}

Removes the applied code (no-op when absent).

Form fields

code Required

The applied code to remove.

_format Optional

Set to json when submitting with fetch; omit for the normal browser redirect.

client_mutation_id Optional

Unique retry key, up to 128 characters. Reusing it for the same action prevents a successful mutation from being applied twice for 24 hours.

Validation

Non-empty code; removing a code that is not applied is an idempotent no-op.

Limits

No action-specific numeric limit.

Response

{
  success,
  cart,
  user_errors
}

User error codes#

Code

When

STOREFRONT_CART_PRODUCT_INVALID

product_id missing or not purchasable.

STOREFRONT_CART_VARIANT_INVALID

variant_id missing for a variable product, or wrong.

STOREFRONT_CART_OWNER_REQUIRED

No usable visitor session — reload the page.

Quantity, stock, and purchase-state validation failures come back the same way — as user_errors with the CURRENT unchanged cart, never as a broken state. After a successful action, ctx.cart reflects the change on the next render.

Line properties#

A cart line can carry declared properties — the personalization channel (engraving text, a gift note). Every property key must match one of the store's cart line definitions (Settings → Metafield definitions → Cart lines, or an app's declaration), written as ".". The theme adds one input per property; the platform validates the key and the value against the definition.

vein
{% form 'cart_add' %}
  <input type="hidden" name="product_id" value="{{ product.id }}">
  <input name="properties[custom.engraving]" maxlength="30" placeholder="{{ 'products.engraving' | t }}">
  <button type="submit">{{ 'products.add_to_cart' | t }}</button>
{% endform %}

Properties are part of LINE IDENTITY: the same product with different property values is a SEPARATE cart line (two mugs, two engravings), each with its own line_id. Cart line forms (quantity steppers, remove buttons) must post the line's line_id — bare product/variant targeting matches only the property-less line, never silently one of several personalized lines.

vein
{% for line in cart.items %}
  {% for key, value in line.properties %}<p>{{ value }}</p>{% endfor %}
  {% form 'cart_remove' %}
    <input type="hidden" name="product_id" value="{{ line.product_id }}">
    <input type="hidden" name="line_id" value="{{ line.line_id }}">
    <button type="submit">{{ 'cart.remove' | t }}</button>
  {% endform %}
{% endfor %}

Code

When

CART_PROPERTY_UNKNOWN

The key matches no cart line definition on this store.

CART_PROPERTY_INVALID

The value fails the definition's type or rules.

CART_PROPERTY_NOT_WRITABLE

The definition is not buyer-writable (hidden), or an app wrote outside its namespace.

CART_PROPERTY_TOO_MANY

More properties than one line allows.