Skip to main content
Solved

v2 /products returns subscription.duration: null for a fully-configured App Store subscription (pre-purchase)

  • July 9, 2026
  • 4 replies
  • 53 views

Forum|alt.badge.img

My App Store auto-renewable subscriptions are fully configured in App Store Connect (a monthly and a yearly), and RevenueCat *sandbox environment* clearly has them. The dashboard shows the correct products and "Billing Plan: Upfront" on the yearly. But calling GET /v2/projects/{project_id}/products returns both products with a null subscription block:

"store_identifier": "com.myapp.yearly",
"state": "active",
"type": "subscription",
"subscription": { "duration": null, "grace_period_duration": null, "trial_duration": null }

Everything else is correct, only subscription fields are null.

What I've already confirmed (so this isn't a setup mistake):
- App Store Connect API key and In-App Purchase key are connected and show Valid Credentials.
- Both subscriptions are Ready to Submit in App Store Connect with their durations set (1 month / 1 year).
- I've re-imported the products in RevenueCat.
- No purchase has ever been made against these product IDs.

I found an older thread suggesting duration only populates after the first purchase/receipt. If that's accurate:

1. Why is duration null pre-purchase when it's already defined in App Store Connect and RevenueCat can clearly read the product?
2. How do I get subscription.duration populated before any purchase?
3. If a purchase really is required first, what's the point of importing products, if RevenueCat won't expose the details of those products that are fully set in App Store Connect?

Happy to share my real project/product IDs privately if it helps you look up the specific products.

Best answer by erik-macinnis

Hi ​@peyplai,

I’m Erik, happy to help!

You’re right that a purchase needs to be made before subscription.duration is populated. You can resolve this by making a sandbox purchase for each product.

  1. This works this way because the App Store Connect API key is optional, while receipt data is more broadly available. RevenueCat receives the duration alongside the receipt or derives it from the transaction dates.

  2. There currently isn’t a way to populate this field before a purchase. Once RevenueCat processes a sandbox purchase, the API should return the duration, such as P1M or P1Y.

  3. Importing a product still creates the RevenueCat product record and brings in the information needed to configure Offerings, packages, entitlements, and store state. It also supports checking pricing, localization, availability, and other App Store metadata.

We understand this isn’t ideal, and we’re planning an improvement to check App Store Connect directly for the duration when credentials are available.

Let me know if you have any other questions.

Cheers,
Erik – Developer Support Engineer

4 replies

erik-macinnis
RevenueCat Staff
Forum|alt.badge.img+2
  • RevenueCat Staff
  • Answer
  • July 10, 2026

Hi ​@peyplai,

I’m Erik, happy to help!

You’re right that a purchase needs to be made before subscription.duration is populated. You can resolve this by making a sandbox purchase for each product.

  1. This works this way because the App Store Connect API key is optional, while receipt data is more broadly available. RevenueCat receives the duration alongside the receipt or derives it from the transaction dates.

  2. There currently isn’t a way to populate this field before a purchase. Once RevenueCat processes a sandbox purchase, the API should return the duration, such as P1M or P1Y.

  3. Importing a product still creates the RevenueCat product record and brings in the information needed to configure Offerings, packages, entitlements, and store state. It also supports checking pricing, localization, availability, and other App Store metadata.

We understand this isn’t ideal, and we’re planning an improvement to check App Store Connect directly for the duration when credentials are available.

Let me know if you have any other questions.

Cheers,
Erik – Developer Support Engineer


Forum|alt.badge.img
  • Author
  • New Member
  • July 10, 2026

Thanks for the info! I appreciate your response.


Forum|alt.badge.img
  • Author
  • New Member
  • July 10, 2026

If I can ask one more question, I’m integrating RC for the reasons most people do, to unify the different platforms into one api. I would still like to keep SOC records within my db for the products that I offer and the subscription records for users. I’m doing this by writing sync products endpoint that calls RC products endpoint, normalizes the data, then storing it within my db (what this question is mainly about). Then, on the webhook endpoint I wrote, I’m receiving the webhook and then calling the get user subscriptions endpoint, receiving the updated state, normalizing it, then upserting the users subscription record. This was based on your docs recommendations about syncing subscriptions on webhook event rather than parsing through events. Do you see any issues with this approach or gotchas that I might not be aware of?  ​@erik-macinnis 


erik-macinnis
RevenueCat Staff
Forum|alt.badge.img+2
  • RevenueCat Staff
  • July 10, 2026

Hi ​@peyplai ,

Yes, that approach of listening for the webhook, then calling RevenueCat to fetch the customer’s current subscription state, and saving that in your database is recommended.

For product syncing, if the Products API returns the fields you need, that should be fine. You’ll probably also want to manage that copy and keep it updated when products or configuration change in RevenueCat.

One thing to keep in mind is that webhooks can sometimes be delayed or arrive out of order. Since you’re fetching the current state after receiving the webhook, that should help avoid issues from processing individual events as the source of truth. However, webhook delays can cause issues if you rely on them to grant immediate access right after purchase.

Happy to answer any other questions.

Cheers,
Erik – Developer Support Engineer