PromotionsService class (accessed via client.promotions) handles active store discounts, marketing campaigns, and coupon validation logic. Promotions support an image field (the default/desktop banner) plus an optional image_mobile field for a phone-sized banner — render image_mobile on small screens and fall back to image everywhere else.
Discounts are calculated server-side:
calculatePromotionDiscount evaluates one promotion
against a cart, and calculateBestPromotion selects the highest-value one. Both return the
same figure the merchant sees in their POS, which a storefront reimplementation of
subtotal × percentage, bundle math, or BOGO logic would have to keep matching as those
rules change. See Calculating a discount below.Methods
listPromotions
Retrieve the promotions running on the storefront today. This is useful for building a “Deals” or “Coupons” page. Only live promotions are returned: status defaults to active, and any other accepted value returns an empty list.
Response
cartTotal
Providing a cartTotal allows the engine to pre-filter promotions that the customer is actually eligible for based on their current shopping bag value (compared against each promotion’s min_purchase).
getPromotion
Fetch full configuration data for a promotion, including its exact discount logic, validity dates, and the products it applies to.
Response
Getting the products in a promotion
Thetype field tells you how a promotion works and which product fields to read.
The easy way —
expand: 'products'. Pass expand: 'products' and Galactic Core resolves every product id in the promotion for you, embedding the product details inline. The response gains bundle_products_resolved, bogo_required_products_resolved, and bogo_free_products_resolved — same entries as the id arrays, but each carries a product object (or null if that product is unavailable). No second lookup:
Response
expand, the product fields hold product ids — fetch each one’s full detail with client.products.getProduct(...) yourself:
discount and fixed promotions there are no products to resolve — render value / min_purchase, and call calculatePromotionDiscount (below) to show the customer the exact amount they’ll save on their current cart.
Calculating a discount
UsecalculatePromotionDiscount to compute exactly what a promotion takes off a given cart. The server does the math, so the figure you display always matches the merchant’s POS — you never multiply a subtotal by a percentage, sum bundle savings, or work out BOGO yourself.
Send the cart you intend to charge. Each line is { product_id, quantity, price }, where price is the unit price you’re charging for that product:
type—discount(percentage) andfixedare cart-wide discounts;bundleandbogoare computed from the products present in your cart.- When the cart doesn’t qualify, you get
eligible: false,discount: 0, and areason(minimum purchase not met, the promotion is outside its active date/time window, or required products are absent). Show thereasonto nudge the shopper (e.g. “Add $30 more to unlock this deal”).
fixed — it’s how you turn “10% off” into a concrete dollar amount for the current cart.
Promotions limited to a market
A promotion carriestarget_countries and target_regions. When both are empty the promotion applies to every shopper, which is what a promotion with no targeting set carries. When either holds values, the shopper’s location must match for the promotion to be eligible.
calculatePromotionDiscount and calculateBestPromotion accept the shopper’s country (ISO 3166-1 alpha-2) and region alongside the cart:
eligible: false with a reason naming the restriction, and calculateBestPromotion passes over it entirely rather than returning a deal that would be refused at checkout. The same evaluation runs when the order is placed, so a targeted promotion cannot be applied by sending a different country than the one the order ships from.
Targeting composes with every other condition: a promotion may be limited to a market and carry a minimum purchase, a date window, and time-of-day restrictions, and all of them must pass.
Auto-applying the best promotion
calculateBestPromotion evaluates all of the store’s active promotions against a cart and returns the single one that saves the customer the most — ideal for auto-applying a deal at checkout without making the shopper hunt for a code:
promotion is null and discount is 0:
Marketplace storefronts
When a sponsored ad placement or a curated collection hands you apromotion_id, pass the storeId you received alongside it (the merchant_store_id). It works the same way across all four calls here — getPromotion (including expand: 'products'), calculatePromotionDiscount, and calculateBestPromotion:
Data Schema
Promotion Structure
PromotionCalculation (returned by calculatePromotionDiscount)
BestPromotion (returned by calculateBestPromotion)
Response Codes
Every method here accepts both publishable and secret keys, andcalculatePromotionDiscount / calculateBestPromotion return 200 (they compute a result, they don’t create a resource). Marketplace keys may call getPromotion, calculatePromotionDiscount, and calculateBestPromotion (each with storeId) but not listPromotions. The status query parameter is validated against a strict enum. A promotion that is not live (a draft, a paused promotion, or one not yet started) is not returned by listPromotions, and getPromotion answers 404 for it.

