I’m trying to understand the difference between discountCodeAppUpdate and discountAutomaticAppUpdate on the Admin API 2026-07.
When I send recurringCycleLimit: 0 with appliesOnSubscription: true to discountAutomaticAppUpdate, the discount comes back with recurringCycleLimit: 0. When I send the same input to discountCodeAppUpdate, userErrors is empty, but a follow-up discountNode read of the DiscountCodeApp shows:
"appliesOnSubscription": true,
"recurringCycleLimit": null
The docs describe 0 the same way for both:
The only difference I can see is on the read side. DiscountAutomaticApp.recurringCycleLimit is Int! and DiscountCodeApp.recurringCycleLimit is nullable Int.
On code discounts, 1–99 are stored as sent. 0, 100, and 999 all come back as null. Saving 100 over a previously stored 12 replaces the 12 with null, without an error.
My questions:
- Why is
0 stored as null on code app discounts, when the same 0 is kept on automatic app discounts?
- Does
null on a DiscountCodeApp mean “applies indefinitely” at checkout, the same as 0? If so, is that documented anywhere? The DiscountCodeApp docs only describe 0.
- Is there an upper limit for code discounts? Values above
99 also come back as null, but I can’t find that in the docs.
Best,
Pius
Thanks @Pius_Osuji, currently both discount types save 0. When a code app discount is read back, the API returns null for a value below 1, while an automatic app discount returns the stored 0.
So in this case, null and 0 mean the same thing. The docs don’t explain that difference, so I’ve passed it along to be corrected.
The values above 99 I was unable to replicate that. Can you share the exact mutation variables and include codeAppDiscount { recurringCycleLimit } in the mutation response?
I’d also suggest to send recurringCycleLimit, appliesOnSubscription, and appliesOnOneTimePurchase with every update. If recurringCycleLimit is omitted, it defaults to 1.
I reran this on Admin API 2026-07. I can’t reproduce 100 or 999 coming back as null. Both are stored and returned as sent. 0 still comes back as null in the mutation response.
mutation discountCodeAppUpdate($id: ID!, $codeAppDiscount: DiscountCodeAppInput!) {
discountCodeAppUpdate(id: $id, codeAppDiscount: $codeAppDiscount) {
codeAppDiscount {
discountId
recurringCycleLimit
}
userErrors {
field
message
}
}
}
Variables:
{
"id": "gid://shopify/DiscountCodeNode/1584188194878",
"codeAppDiscount": {
"recurringCycleLimit": 100,
"appliesOnSubscription": true
}
}
Response:
{
"data": {
"discountCodeAppUpdate": {
"codeAppDiscount": {
"discountId": "gid://shopify/DiscountCodeNode/1584188194878",
"recurringCycleLimit": 100
},
"userErrors":
}
}
}
The same mutation with 999 returned recurringCycleLimit: 999. With 0 it returned recurringCycleLimit: null and an empty userErrors list. A follow-up discountNode read matched the mutation response in each case.
Thanks for confirming that null and 0 mean the same thing on code discounts. I’ll treat a read of null as 0.