Unexpected appAccountToken mutation in JWSRenewalInfo during in-app crossgrade

Hello Apple Developer Support / StoreKit Team,

We recently observed a behavior regarding the appAccountToken in App Store Server Notifications v2 that seems to completely contradict the official documentation.

According to the Set App Account Token documentation:

The same appAccountToken continues to apply to renewal transactions if the customer upgrades, downgrades, or cross-grades the subscription.

However, we encountered a scenario where an active in-app crossgrade resulted in an updated/overwritten appAccountToken inside the subsequent JWSRenewalInfoDecodedPayload, despite our backend never calling the Set App Account Token REST API.

Our Observation:

  1. A user subscribes to our 1-month plan (Product A). Apple generates an originalTransactionId bound to their initial appAccountToken (Token A).

  2. Later, a crossgrade to a 1-year plan (Product B) is initiated from within the app while the user is logged into a different account in our system (Token B), but using the same underlying Apple ID on the device.

  3. When the crossgrade takes effect at the next renewal date, we receive a DID_RENEW webhook.

The Anomaly (See Attached Screenshots): Upon decoding the JWSRenewalInfoDecodedPayload from the webhook, we noticed that the appAccountToken had unexpectedly changed to the new token (Token B).

As shown in the attached redacted screenshots:

Screenshot 1 (Before/Original): JWSRenewalInfoDecodedPayload for Product 00001 shows the appAccountToken ending in ...e9a.

Screenshot 2 (After Crossgrade): JWSRenewalInfoDecodedPayload for Product yearly_saver shows the appAccountToken has mutated to ending in ...507, even though the originalTransactionId remains exactly the same.

To reiterate, our server did not call the POST /inApps/v1/subscriptions/appAccountToken/{originalTransactionId} endpoint to manually overwrite this token at any point.

Our Questions:

  1. Is this the intended StoreKit 2 behavior? Does Apple automatically overwrite the base appAccountToken in the RenewalInfo if a new token is somehow associated during an active in-app crossgrade transaction?

  2. If this is intended, could the documentation be clarified? The current phrasing strongly suggests the token is permanently locked to the initial purchase and will never change during crossgrades unless the REST API is explicitly called.

While this behavior is actually quite helpful for our backend to track multi-account users, we want to ensure we aren't relying on an undocumented bug that might be patched unexpectedly.

Any insights from the StoreKit engineering team would be highly appreciated. Thank you!

Unexpected appAccountToken mutation in JWSRenewalInfo during in-app crossgrade
 
 
Q