AI agents: the content index is at /llms.txt. Every page is also available as Markdown — send the request header Accept: text/markdown, or append .md to the page URL.
Xperience Docs

Cookie consent

We use necessary cookies (opens in a new tab) to run our website and improve your experience while browsing. Additional cookies are only used with your consent. You may revoke your consent on the Cookies Policy (opens in a new tab) page or in your browser at any time.

Configure (opens in a new tab)
  • Documentation
    Administration interface basicsDevelopers and adminsBusiness usersChangelogGlossary
  • Guides
    Welcome to Xperience by KenticoAdministration interface basicsCustomer journeysDigital marketingConfigurationDevelopmentArchitectureUpgrade to Xperience by Kentico
  • Training
    For AdminsFor Business usersFor DevelopersFor Solution architects
  • API Examples
    ConfigurationContent managementDevelopmentDigital marketing
  • Changelog
    Documentation updatesSecurity advisories
  1. Home
  2. Developers and admins
  3. Digital commerce
  4. Promotions
  5. Free shipping

Free shipping


Was this page helpful?

Send us feedback Go back to top
  • Community Questions & Answers (opens in a new tab)
  • Contact support (opens in a new tab)

Advanced license required

Features described on this page require the Xperience by Kentico Advanced license tier.

The free shipping promotion framework allows you to create custom promotion rules that grant free shipping during price calculation. Unlike catalog and order discounts, which reduce a price, free shipping eliminates the shipping cost entirely when the cart meets the rule's conditions. Free shipping always removes the shipping price in full, partial discounts are not possible.

Use free shipping to implement:

  • A minimum amount or quantity that unlocks free shipping (e.g., "Spend $50, get free shipping")
  • Free shipping earned by specific products or categories
  • Free shipping limited to selected shipping methods

Free shipping doesn't compete with other promotion types

A customer can receive free shipping together with any applicable catalog and order discounts.

General promotion capabilities

Free shipping promotions share the same configuration and evaluation framework as catalog and order discounts, so the following capabilities work identically:

  • Coupon codes – Optionally require a discount code for redemption.
  • Customer eligibility – Restrict the promotion to all visitors, registered members only, or a custom eligibility rule.
  • Activation scheduling – Set start and end dates for time-limited offers.

Free shipping promotion rule overview

A promotion rule defines the logic for determining whether an order qualifies for free shipping. You implement promotion rules as classes that inherit from FreeShippingPromotionRule.

The rule receives the calculation data for the whole cart or order – the cart items with their prices after other discounts, the calculation mode (shopping cart or checkout), and, in checkout, the shipping method the customer selected. Based on this data, the rule answers a single question: does the order qualify for free shipping? If it does, the rule can also name the specific products that earned the reward. In the shopping cart mode, the shipping method is not selected yet and the rule checks whether free shipping is available for any free shipping method.

Promotion candidate

A promotion candidate represents a reward that a free shipping rule grants to an order. When a promotion rule determines that the order qualifies, it returns a FreeShippingPromotionCandidate containing:

  • TriggerProductIdentifiers – An optional list of the products that caused the cart to qualify. Leave this empty for a whole-cart condition, such as a minimum order amount. See Restrict the reward to specific products.

When the cart doesn't qualify, return null instead of a candidate.

Unlike CatalogPromotionCandidate and OrderPromotionCandidate, FreeShippingPromotionCandidate doesn't carry a discount amount – the reward is always the full shipping price.

Promotion rule properties

Promotion rules can define configurable properties that store managers set when creating promotions in the administration interface. Properties are defined in a separate class implementing IPromotionRuleProperties and are decorated with editing components to generate the UI. More details are provided in Define promotion rule properties.

Promotion selection logic

When multiple free shipping promotions qualify for the same cart, the system selects one to credit: a promotion activated by the customer's coupon code wins if any exists, otherwise the newest qualifying promotion is used. Every qualifying promotion is still evaluated and returned as a candidate, but only the selected one is marked Applied. The reward for free shipping promotions is always the full shipping price. Only the selected promotion is persisted when the order is created.

Create free shipping promotion rules

Implementing a free shipping promotion rule involves creating two components:

  1. Properties class – Defines configurable settings that store managers can adjust in the administration interface (e.g., minimum order amount, eligible shipping methods). The properties class implements IPromotionRuleProperties or inherits from FreeShippingPromotionRuleProperties.
  2. Logic class – Contains the rule's evaluation logic to determine if the cart qualifies for free shipping. The logic class inherits from FreeShippingPromotionRuleBase or FreeShippingPromotionRule.

These two components work together: the properties class stores the configuration, and the logic class uses those configured values to evaluate the cart during price calculation.

Sample implementation

See DancingGoatFreeShippingPromotionRule.cs in the Dancing Goat project template for a sample rule implementation.

Define promotion rule properties

The system provides FreeShippingPromotionRuleProperties as a base class with common properties already defined and configurable via the administration interface:

  • MinimumRequirementValueType – Radio group to select no minimum, minimum purchase amount, minimum quantity of items, or minimum single item price
  • MinimumRequirementValue – Decimal input for the minimum value (visible when a minimum requirement other than "none" is selected)
  • ShippingMethodScope – Radio group to select whether the reward covers every shipping method or only selected ones
  • ShippingMethods – Selector for the specific shipping methods the reward covers (visible when the scope is set to specific methods)

You can inherit from this base class to include these standard fields and add your own custom properties. Use editing component annotations to generate the input fields for each property:

C#Properties extending the base properties class
using System.Collections.Generic;

using CMS.ContentEngine;

using Kentico.Xperience.Admin.Base.FormAnnotations;
using Kentico.Xperience.Admin.DigitalCommerce;

namespace Codesamples.Commerce;

public class ProductFreeShippingProperties : FreeShippingPromotionRuleProperties
{
    // Base class properties include: MinimumRequirementValueType, MinimumRequirementValue,
    // ShippingMethodScope, ShippingMethods

    // Add custom properties for your business logic
    // For example, a property allowing editors to select products that will count toward the minimum requirement
    [ContentItemSelectorComponent(
        typeof(ProductSchemaFilter),
        Label = "Eligible products",
        Order = 1)]
    // Not visible when no minimum requirements are needed to qualify for the shipping
    [VisibleIfNotEqualTo(nameof(MinimumRequirementValueType), "none")]
    public IEnumerable<ContentItemReference> EligibleProducts { get; set; } = [];
}

This example uses ProductSchemaFilter (an implementation of IReusableFieldSchemasFilter) to scope the items offered in the selector, so that only content items that are products in the store appear. For other ways to scope a content item selector, see Reference - Admin UI form components.

See the 'ProductSchemaFilter' implementation
C#
using System.Collections.Generic;

using Kentico.Xperience.Admin.Base.FormAnnotations;

public class ProductSchemaFilter : IReusableFieldSchemasFilter
{
    IEnumerable<string> IReusableFieldSchemasFilter.AllowedSchemaNames => new List<string> { IProductFields.REUSABLE_FIELD_SCHEMA_NAME };
}

The base class properties use negative Order values (e.g., -190, -90, -80) to ensure they appear at the top of the form, grouped under their own categories. Keep this in mind when positioning custom properties.

Alternatively, you can directly implement IPromotionRuleProperties, bypassing the inheritance entirely, if you need full control over all configuration properties used by the rule.

Implement the promotion rule

The framework provides two base classes for free shipping promotion rules:

  • FreeShippingPromotionRule – Use when your properties class inherits from FreeShippingPromotionRuleProperties. Provides built-in helper methods that work with the base properties, including minimum requirement evaluation (whole-order minimum, minimum number of items, minimum single item price) and shipping method scoping.
  • FreeShippingPromotionRuleBase – Use when you need fully custom properties implementing IPromotionRuleProperties directly. You must implement all eligibility logic yourself.

Both base classes use the same generic type parameters:

  1. TPromotionRuleProperties – The rule's properties class. Must inherit from FreeShippingPromotionRuleProperties for FreeShippingPromotionRule, or implement IPromotionRuleProperties for FreeShippingPromotionRuleBase.
  2. TPriceCalculationRequest – The price calculation request type.
  3. TPriceCalculationResult – The price calculation result type.

For rule registration, see Register the promotion rule.

When your properties class inherits from FreeShippingPromotionRuleProperties, FreeShippingPromotionRule provides you access to the following built-in methods and features:

  • MeetsMinimumRequirement(items) – Evaluates the given cart items against MinimumRequirementValueType and MinimumRequirementValue from the base properties, which can be configured by store managers in the administration. Price and quantity requirements count the item price after all discounts, since the free shipping step runs after order promotions. See Define promotion rule properties for the breakdown of what the MinimumRequirementValueType property can represent.
  • GetItemsMeetingMinimumRequirement(items) – Returns the items that earned the reward. For a minimum single item price requirement, only the items whose own price meets the threshold; for the other requirement types (none, minimum purchase amount, minimum quantity), the whole given set, since those are evaluated on the total across all items rather than any one item. Useful for building TriggerProductIdentifiers in the promotion candidate.
  • IsApplicable with automatic shipping method scoping – FreeShippingPromotionRule overrides IsApplicable so the promotion is evaluated only when the shipping method selected in the order is covered by the promotion, as determined by ShippingMethodScope and ShippingMethods from the base properties. In the shopping cart mode no shipping method is selected yet, so the check passes and the evaluation always runs. See Filter promotion applicability if you need additional conditions – call base.IsApplicable from your override to keep the shipping method check.
  • GetDiscountValueLabel() – Already overridden to return a localized "Free shipping" label for display. Override it again only if you need custom wording.

The following sample demonstrates a minimal rule that uses the base properties:

C#Free shipping promotion rule
using System;
using System.Collections.Generic;
using System.Linq;

using CMS.Commerce;

using Kentico.Xperience.Admin.DigitalCommerce;

namespace Codesamples.Commerce;

public class MinimumOrderFreeShippingRule
    : FreeShippingPromotionRule<FreeShippingPromotionRuleProperties,
        PriceCalculationRequest, PriceCalculationResult>
{
    public const string IDENTIFIER = "Acme.MinimumOrderFreeShipping";

    public override FreeShippingPromotionCandidate GetPromotionCandidate(
        IPriceCalculationData<PriceCalculationRequest, PriceCalculationResult> calculationData)
    {
        if (!MeetsMinimumRequirement(calculationData.Result.Items))
        {
            // The cart doesn't qualify -- no reward
            return null;
        }

        // For a minimum item price requirement, the qualifying items are trigger products.
        // For the other requirement types, the whole cart met the requirement together, so
        // leaving TriggerProductIdentifiers empty stores the reward once at the order level.
        List<ProductIdentifier> triggerProducts;
        if (Properties.MinimumRequirementValueType.ToString().Equals("ItemPrice", StringComparison.OrdinalIgnoreCase))
        {
            triggerProducts = GetItemsMeetingMinimumRequirement(calculationData.Result.Items)
                .Select(item => item.ProductIdentifier)
                .ToList();
        }
        else
        {
            triggerProducts = [];
        }

        return new FreeShippingPromotionCandidate
        {
            TriggerProductIdentifiers = triggerProducts
        };
    }
}

Register the promotion rule

Register the rule using RegisterPromotionRuleAttribute with the following parameters:

  1. A unique string identifier for the rule.
  2. PromotionType.Shipping to indicate this is a free shipping promotion.
  3. A display name shown in the administration interface.

Without registration, the rule doesn't appear in the administration interface.

C#Register a free shipping promotion rule
[assembly: RegisterPromotionRule<MinimumOrderFreeShippingRule>(
    MinimumOrderFreeShippingRule.IDENTIFIER,
    PromotionType.Shipping,
    "Minimum order -- free shipping")]

Filter promotion applicability

Override the IsApplicable method to add high-level conditions that determine whether the promotion should be evaluated at all. This method runs once per promotion before GetPromotionCandidate, making it useful for performance optimization when you can skip the entire promotion early.

Built-in shipping method validation

When using FreeShippingPromotionRule, the base class IsApplicable implementation automatically validates the shipping method scope based on ShippingMethodScope and ShippingMethods properties. Unlike OrderPromotionRule, it does not validate the minimum purchase requirement – call MeetsMinimumRequirement explicitly inside GetPromotionCandidate instead, as shown above. You only need to override IsApplicable if you have additional custom conditions.

The evaluation pipeline works as follows:

  1. Get active promotions – The system retrieves all active free shipping promotions.
  2. Check IsApplicable – For each promotion, IsApplicable is called once. If it returns false, the promotion is skipped entirely.
  3. Calculate the reward – For promotions that pass, GetPromotionCandidate is called to determine if the cart qualifies.
  4. Select and grant free shipping – Every promotion for which GetPromotionCandidate returns a candidate qualifies. The system then selects one of the qualifying candidates to credit (see Promotion selection logic).

Use IsApplicable for checks that apply to the entire promotion context. For example, to restrict a promotion to registered members while keeping the built-in shipping method validation:

C#Restrict promotion to registered members
using System.Threading;
using System.Threading.Tasks;

using CMS.Commerce;

// ...

public override async Task<bool> IsApplicable(
    IPriceCalculationData<PriceCalculationRequest, PriceCalculationResult> calculationData,
    CancellationToken cancellationToken)
{
    BuyerIdentifier buyerIdentifier = calculationData.Request.BuyerIdentifier;

    // Checks if the buyer is a registered member
    if (buyerIdentifier == null || buyerIdentifier.Kind != BuyerIdentifierKind.Member)
    {
        return false;
    }

    // Keeps the automatic shipping method check
    return await base.IsApplicable(calculationData, cancellationToken);
}

Restrict the reward to specific products

A free shipping promotion doesn't have to depend on the whole cart. To grant free shipping only when the cart contains specific products or products from specific categories, first add a property that lets store managers select the eligible products or categories, such as EligibleProducts in the Define promotion rule properties example.

EligibleProducts holds GUID-identified content item references, while cart items are keyed by the int-identified ProductIdentifier – the two can't be compared directly. Match on product data instead, extended with the product's content item GUID – see Customize price calculation:

C#Extend product data with a content item GUID
using System;

using CMS.Commerce;

public record ProductDataWithContentItemGuid : ProductData
{
    public Guid ContentItemGuid { get; init; }
}

Then evaluate the scoped cart items in GetPromotionCandidate and return the matching products as TriggerProductIdentifiers:

C#Free shipping rule scoped to eligible products
using System.Linq;

// ...

public override FreeShippingPromotionCandidate GetPromotionCandidate(
    IPriceCalculationData<PriceCalculationRequest, PriceCalculationResult> calculationData)
{
    var scopeItems = calculationData.Result.Items
        .Where(item => item.ProductData is ProductDataWithContentItemGuid product
            && Properties.EligibleProducts.Any(eligible => eligible.Identifier == product.ContentItemGuid));

    if (!MeetsMinimumRequirement(scopeItems))
    {
        return null;
    }

    return new FreeShippingPromotionCandidate
    {
        TriggerProductIdentifiers = GetItemsMeetingMinimumRequirement(scopeItems)
            .Select(item => item.ProductIdentifier)
            .ToList()
    };
}

Recording trigger products matters beyond eligibility – when the order is created, the system stores the granted free shipping reward once per trigger item instead of once for the whole order. This lets a later return or refund of a trigger item identify the free shipping that the item earned. Leave TriggerProductIdentifiers empty for whole-cart conditions (such as a minimum order amount that isn't scoped to specific products), and the reward is stored once at the order level instead.

Sample implementation

See DancingGoatFreeShippingPromotionRule.cs and DancingGoatFreeShippingProperties.cs in the Dancing Goat project template for a real implementation that scopes the reward by category, product, or tag.

Access promotion data in calculation results

After price calculation, you can access promotion information from the result to display eligibility to customers or for reporting purposes.

Granted free shipping promotion candidates are stored in the PromotionData.FreeShippingPromotionCandidates collection on the IPriceCalculationResult returned by price calculation. Each candidate is wrapped in IPriceCalculationPromotionCandidate<FreeShippingPromotionCandidate>, exposing the promotion ID, the coupon code that activated it (if any), and the candidate itself:

C#Access free shipping promotion data
var qualifiesForFreeShipping = calculationResult.PromotionData.FreeShippingPromotionCandidates.Count > 0;

foreach (IPriceCalculationPromotionCandidate<FreeShippingPromotionCandidate> candidate in calculationResult.PromotionData.FreeShippingPromotionCandidates)
{
    var promotionId = candidate.PromotionID;
    var triggerProducts = candidate.PromotionCandidate.TriggerProductIdentifiers;
    var couponCode = candidate.CouponCode;
}

On this page

  • General promotion capabilities
  • Free shipping promotion rule overview
    • Promotion candidate
    • Promotion rule properties
    • Promotion selection logic
  • Create free shipping promotion rules
    • Define promotion rule properties
    • Implement the promotion rule
    • Register the promotion rule
    • Filter promotion applicability
    • Restrict the reward to specific products
  • Access promotion data in calculation results

Related pages

  • Catalog discounts
  • Order discounts
  • Coupon codes
  • Customize customer eligibility