Discount Function API: no target can both see an entered discount code and the shipping address

Goal: reject an entered discount code when the shipping address country isn’t allowed, using enteredDiscountCodesReject.

The problem

The two inputs I need never appear in the same function run:

  • cart.lines.discounts.generate.run receives enteredDiscountCodes, but cart.deliveryGroups is always [], so there’s no deliveryAddress.countryCode to test.
  • cart.delivery-options.discounts.generate.run receives a populated cart.deliveryGroups with the country — but in my testing it only ever runs while enteredDiscountCodes is empty. Once a code is entered, only the cart-lines target runs.

So the target that can see the code can’t see the country, and the target that can see the country doesn’t run when there’s a code to reject.

Observed function-run logs

Both targets are registered on one function. The discount is created with discountAutomaticAppCreate and discountClasses: [PRODUCT, ORDER, SHIPPING], so the delivery target is definitely active — it does run, just never at the same time as an entered code.

Session 1 — entering code SAVE10 at 11:33:42:

Time Target enteredDiscountCodes cart.deliveryGroups
11:33:36.180 cart.lines [] []
11:33:36.369 cart.delivery-options [] [{deliveryAddress:{countryCode:"GB"}}]
11:33:38.364 cart.lines [] []
11:33:38.411 cart.delivery-options [] [{deliveryAddress:{countryCode:"GB"}}]
11:33:42.785 cart.lines [{code:"SAVE10", rejectable:true}] []

Session 2 — code already applied throughout, 24 seconds, GB shipping address set:

11:49:27  cart.lines  SAVE10(rejectable=true)  deliveryGroups: []
11:49:27  cart.lines  SAVE10(rejectable=true)  deliveryGroups: []
11:49:29  cart.lines  SAVE10(rejectable=true)  deliveryGroups: []
11:49:29  cart.lines  SAVE10(rejectable=true)  deliveryGroups: []
11:49:36  cart.lines  SAVE10(rejectable=true)  deliveryGroups: []
11:49:49  cart.lines  SAVE10(rejectable=true)  deliveryGroups: []
11:49:51  cart.lines  SAVE10(rejectable=true)  deliveryGroups: []

Zero cart.delivery-options runs in that window.

Across both sessions: 12 cart-lines runs with the code entered, 0 delivery-options runs with the code entered.

rejectable stayed true throughout, so the documented delivery-target restriction (“in a delivery target it’s also false when the code already applied a product or order discount”) isn’t what’s blocking me — the delivery target simply doesn’t run.

My reading of the ordering: the delivery target runs when delivery options are recalculated, which happens when the buyer enters their address — before they enter a discount code. Entering the code afterwards recalculates cart-line discounts only. That’s the normal checkout sequence, so the two never coincide.

Verified locally that the logic itself is fine — feeding the delivery target a synthetic input with both the code and a GB address produces the correct rejection:

{"operations":[{"enteredDiscountCodesReject":{
  "codes":[{"code":"SAVE10"}],
  "message":"Sorry you cannot use SAVE10 with that address"}}]}

It just never receives that combination in a real checkout.

On the docs

The Cart.deliveryGroups schema description says deliveryGroups is always empty for the legacy Order Discount and Product Discount APIs, and points to the Discount Function API as the replacement. But I’m seeing it empty in cart.lines.discounts.generate.run on the new API too, and that isn’t stated anywhere I can find. The cart.lines.discounts.generate.run sample input object in the Discount Function API reference does show "deliveryGroups": [], which is consistent with what I observe — but it reads as incidental rather than documented behaviour.

Alternatives I’ve ruled out

  • Cart and Checkout Validation function — it does get cart.deliveryGroups[0].deliveryAddress.countryCode, but its DiscountApplication type exposes no discount code, so I can only detect that a discount was applied, not which one. A rule scoped to one code would fire on any discount.
  • localization.country.isoCode — available in the cart-lines target, but it’s the localized/market country, not the shipping address, so it’s wrong for a buyer on a different market’s storefront.
  • cart.billingAddress — not populated at the point the code is entered.

Questions

  1. Is cart.deliveryGroups intentionally always empty in cart.lines.discounts.generate.run? If so, can that be documented on the Discount Function API page rather than only in the legacy-API note?
  2. Is there any supported way to read the shipping destination country from the cart-lines target?
  3. Is cart.delivery-options.discounts.generate.run expected to re-run when a discount code is entered? If it is, my logs suggest it isn’t happening; if it isn’t, then enteredDiscountCodesReject in that target seems only usable for codes entered before the address is set.
  4. Is there a different recommended approach for “reject this specific code when shipping to country X” that I’ve missed?

Happy to provide full function-run log JSON if useful.

Hey @AdvancedApps - thanks for reaching out and for the logs, happy to look into this a bit further for you.

Could you share:

  • The api_version from your shopify.extension.toml
  • Sanitized full Function run log JSON for one reproduction where the shipping address is set before SAVE10 is entered, including the inputs and outputs for both targets
  • Whether this is standard one-page checkout or an accelerated checkout such as Shop Pay, and what happens if the code is entered before the address or the address is changed after applying the code

I’d just remove any shop, app, customer, or address identifiers from the logs before posting them.

I’ll try to reproduce the same sequence on our end with both targets enabled. That should help confirm whether entering the code is failing to rerun the delivery target, or whether we’re dealing with undocumented target-specific input behaviour.

Hope to hear from you soon.

This has always been the case for cart lines (product&order) functions, both legacy and with new api. This only works with delivery discount functions (is not an empty array)

@AdvancedApps : This is shopify discount function limitation.

Generally currency is tied to shipping address and country so the way out could be that if the currency is not for the country then reject the coupon code.
Logically replacing the rejection base don currency rather then on shipping address.

This might work for some stores but might not work for other stores.

The other option would be to ask in advance for delivery address and then add delivery country as a line item value and use it.

The otehr way around is that use checkout and cart validation to denie the code execution.

If you can share on currency vs shipping country scenerio then there can be some otehr way around.

@Alan_G I’m using the 2026-07 API version and I have DM’ed you the logs

@mateusz and @Satish_Mantri thank you for your comments, I’m hoping we can get confirmation if the behaviour is intended as it seems like a limitation or bug in the current implementation