Skip to main content
The 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.
A merchant’s promotions can also be advertised automatically. When Google Ads or Meta Ads is connected, Galactic Core can generate a campaign that features a running promotion’s real discount — timed to its window — and launch it when the promo goes live and pause it when it ends. This is a merchant-side capability configured in the admin, not an API call. See Advertising.
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
Filter: 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

The type 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
The manual way. Without expand, the product fields hold product ids — fetch each one’s full detail with client.products.getProduct(...) yourself:
For 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

Use calculatePromotionDiscount 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:
The response is the same shape for every promotion type:
  • type — discount (percentage) and fixed are cart-wide discounts; bundle and bogo are computed from the products present in your cart.
  • When the cart doesn’t qualify, you get eligible: false, discount: 0, and a reason (minimum purchase not met, the promotion is outside its active date/time window, or required products are absent). Show the reason to nudge the shopper (e.g. “Add $30 more to unlock this deal”).
This is the right call for every type, including percentage and fixed — it’s how you turn “10% off” into a concrete dollar amount for the current cart.

Promotions limited to a market

A promotion carries target_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:
Both fields are optional. When you omit them, Galactic Core reads the shopper’s country from the network edge, so a browser calling directly is located without you sending anything — send them explicitly when your storefront renders on a server, or when you are previewing a market other than the caller’s own. A promotion the shopper’s location excludes returns 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:
When nothing applies, promotion is null and discount is 0:

Marketplace storefronts

When a sponsored ad placement or a curated collection hands you a promotion_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:
A marketplace key may read and calculate a single merchant’s promotions this way; it cannot list promotions.

Data Schema

Promotion Structure

PromotionCalculation (returned by calculatePromotionDiscount)

BestPromotion (returned by calculateBestPromotion)


Pro Marketing Tip: Use the image field to build high-impact promotional carousels. By associating visual assets directly with your discount logic, you can ensure that your marketing banners always reflect the current campaign status and validity.

Response Codes

Every method here accepts both publishable and secret keys, and calculatePromotionDiscount / 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.