App Pricing + App Events usage billing — request for documented answers on undocumented behaviors

We are building a public app on Shopify App Pricing with hybrid plans: a recurring base charge plus a per-unit usage meter (tiered, graduated, with a $0 first tier as the included allowance) and a 7-day free trial. Usage is reported through the App Events endpoint (`POST /app/unstable/events`). We have verified the full flow end to end on development stores, including successful billed events.

Partner support confirmed several behaviors for us but identified the items below as undocumented and advised us to submit them together for an authoritative answer. We plan to build production billing on these behaviors, so we need answers we can rely on, ideally with the documentation updated to match.

Our test observations are included with each question so you can confirm or correct them.

0. Is unstable App Events acceptable for App Store review? (Our blocking question — we need this one in writing first.)

Is using the App Events API at the unstable version acceptable for a production app submitted for App Store review, given Shopify’s general versioning policy? The policy states unstable versions should not be used in production, yet App Events is only published at unstable and is documented as the production mechanism for usage-based billing under App Pricing. Partner support acknowledged this as a genuine conflict in Shopify’s published guidance and directed us here for a written answer. We are targeting App Store submission in roughly two weeks, so this question gates our launch.

1. First billing cycle after a free trial.

When a trial ends, does the first billing cycle start exactly at the trial end, or does it span the trial period? We ask because this determines whether an event timestamped during the trial can ever fall inside a cycle. Observed so far: a trialing subscription has `currentBillingCycle: null`, and a subscription with no trial gets an immediate 30-day cycle.

2. Is a PERIOD_CLOSED refusal terminal?

Events we submitted during a trial returned 202 at transport with billing result `PERIOD_CLOSED` (“Billing cycle is missing or incomplete”). Is that refusal permanent for that event? Does a transport-accepted but billing-refused event consume its idempotency key, or could the same key be resubmitted after a cycle opens?

3. Cycle assignment for late-arriving events.

Our delivery pipeline retries, so an event can arrive minutes or hours after the sale it represents. Is an event assigned to a cycle by its `timestamp` or by receipt time, and what tolerance applies near a cycle boundary? The only constraint we found documented is that timestamps cannot be more than 5 minutes in the future.

4. Billing result codes.

Is there a full enumeration of billing-event result codes (`PERIOD_CLOSED` and its peers) with their meanings? We need it to classify failures for alerting.

5. Negative event values against a $0 first tier.

The 2026-07-09 changelog says events accept negative values, and support confirmed negative values reduce billable quantity. With a graduated meter whose first tier is $0 (an included allowance): does a negative event restore allowance units or only reduce the quantity that has passed into the paid tier? Can a cycle’s total go negative, and if so what happens at invoicing?

6. Trial extension on App Pricing subscriptions.

`appSubscriptionTrialExtend` is documented only under manual pricing, but it worked against an App Pricing subscription in our testing (the extension was cumulative and `currentPeriodEnd` moved with it). Is the mutation supported for App Pricing subscriptions? Separately: can a trial be extended after it has lapsed? That is the case our sales team will actually hit.

7. Uninstall and reinstall during an active trial.

The docs cover the 180-day tracking of consumed trial days. Beyond that, what happens to the subscription contract and to metering when a merchant uninstalls and reinstalls while their trial is active?

8. Limits on the events endpoint.

Are there published rate limits, payload limits, or batching recommendations for `POST /app/unstable/events`?

9. Programmatic access to billing outcomes.

Support confirmed the endpoint always returns 202 at transport and that billing validation failures surface only in the Dev Dashboard, with no webhook and no API. Is a programmatic read of billing-event outcomes planned? Without one, our only production detection of billing failures is reconciling our own ledger against `activeSubscription.usage.quantity`.

10. Stability of App Events while it is `unstable`.

App Events is the only usage-billing mechanism for App Pricing apps, and it is published only at the `unstable` version, which Shopify’s versioning policy says not to use in production. Given production apps have no alternative: does Shopify commit to announcing breaking changes to App Events in the developer changelog with a migration window? Has App Events had any backwards-incompatible change since its introduction?

Hey @Kasi_Prasad - thanks for putting this together. I have a few answers to your questions here!

  • 202 only confirms that Shopify received the request. Billing validation happens asynchronously, and the Dev Dashboard under Logs → App Billing Event is currently the only place to inspect the result.
  • PERIOD_CLOSED means the event timestamp is outside the current billing cycle, or more than 24 hours after uninstall. Billing-event idempotency is documented as permanent, so I’d treat that key as terminal and avoid reusing it.
  • The documented billing errors are INVALID_ACCOUNT, ACCOUNT_FROZEN, NO_SUBSCRIPTION, SUBSCRIPTION_NOT_METERED, PERIOD_CLOSED, IDEMPOTENCY_KEY_ERROR, INVALID_VALUE, and MISSING_VALUE_KEY.
  • App Events is limited to 500 requests/second per app, one event per request, and a maximum of 15 scalar attributes. Fractional values should be sent as quoted strings, such as "1.5". (more info here)

That said, you’re right that the docs still don’t clearly answer the unstable-version/App Store review question, the exact trial and first-cycle behaviour, how negative values interact with an included $0 allowance, or what happens after a lapsed trial or uninstall/reinstall, so I can definitely look into that further for you.

I just had a few questions to make sure I’m understanding correctly, could you also clarify the use case behind the meter?

  • What does one unit represent, and is it immutable consumption such as a sale/message, or a current count that can later be deleted or corrected?
  • Should usage during the 7-day trial be free, discarded, or billed after the trial ends?

Hope to hear from you soon.

Thanks Alan. Your answers on the error codes, the endpoint limits, PERIOD_CLOSED, and the dashboard location closed several of our questions. Below are our answers to your two questions, then the items we would like you to look into, then some new questions your reply raised.

Your two questions

What one unit represents, and whether it is immutable or correctable.

One unit is one order line item at quantity 1. An order with a line item at quantity 4 is four units. We record the units at order time and send one event per unit, with a whole-number value and one attribute.

Today we do not reverse a unit when the merchant later refunds or cancels the order. We have not decided whether we will ever send a correcting negative event. Question 5 below is what we need to make that decision: how a negative value behaves against the included $0 tier, and whether a cycle total can go below zero. We would rather not commit to “immutable” until we know what a correction would do.

Whether trial usage should be free, discarded, or billed after the trial ends.

Today trial usage cannot be billed at all. Events sent during the 7-day trial are refused, which matches what Partner support told us: a trialing subscription has no billing cycle. We record the usage on our side but never charge for it. So the current behavior is “discarded,” by necessity rather than by choice.

We want to understand which of the three options are available to us in practice:

  • Is “billed after the trial ends” a real mechanism? Can usage that occurred during the trial be submitted into, or carried into, the first paid cycle? If so, how is that done given that the trial has no cycle to bill into and idempotency keys are permanent?
  • Is there any way to include a fixed usage amount with the trial, and have usage beyond that amount move the subscription into its first billing period automatically?
  • If neither exists, is there any supported means to bound usage during a trial? We do not necessarily want a free trial to mean unlimited free usage for 7 days.

The items you offered to look into

Please do pursue these. For each, a written answer or a pointer to documentation is what we need.

Question 0 (unstable App Events and App Store review). Is using the App Events API at its unstable version acceptable for a production app submitted for App Store review? This is the one question that gates our submission. Partner support gave us a yes, then withdrew it in the same conversation and told us to get a written answer.

Question 1 (first cycle after a trial). When the trial ends, does the first billing cycle start at the moment the trial ends, or does it span the trial period?

Question 5 (negative values against a $0 first tier). With a graduated price whose first tier is $0 up to an included quantity, does a negative event value restore units to the included tier, or does it only reduce the quantity that has already passed into the paid tier? Can a cycle total go below zero, and if so what happens at invoicing?

Question 6 (trial extension). Is the trial-extension mutation supported for App Pricing subscriptions? It is documented only for the older billing API. Separately, can a trial be extended after it has lapsed?

Question 7 (uninstall and reinstall during a trial). What happens to the subscription and to metering when a merchant uninstalls and reinstalls while the trial is active? Can the merchant obtain a second trial by reinstalling?

Items from the original post still open

Question 3, second half (tolerance at a cycle boundary). Your answer settled that assignment keys on the event’s own timestamp, not on receipt time. What tolerance applies near a boundary? If an event with a timestamp inside cycle N is delivered after cycle N has closed, is it refused with PERIOD_CLOSED, or is there a grace window after the boundary during which it still lands in cycle N? This is the case that costs us money, so the exact rule matters.

Question 9, second half (programmatic billing outcomes). You confirmed that the Dev Dashboard under Logs then App Billing Event is the only place to see a billing result today. Is a programmatic read of billing-event outcomes planned?

Question 10 (stability of App Events while unstable). This one was not addressed. Does Shopify commit to announcing breaking changes to App Events in the developer changelog with a migration window, while it stays at the unstable version? Has App Events already had a backwards-incompatible change? App Events is the only usage-billing path for App Pricing apps, so a breaking change with no notice would stop billing for every merchant at once.

New questions

Question 11 (the 24-hour post-uninstall window). Your answer says PERIOD_CLOSED can mean the event timestamp is more than 24 hours after uninstall. Within those 24 hours, is a usage event still billed normally? Is the window judged on the event’s own timestamp or on when Shopify receives it? Does the merchant’s billing cycle stay open long enough for an event delivered during the window to land in it? We have sales that complete shortly before an uninstall, and we need to know whether delivering them inside the window recovers them.

Question 12 (app-imposed usage limit during the trial). Partner support told us this is an app review question rather than a documentation one, so I am also raising it through the Partner Dashboard support channel, but I would appreciate a view here as well. Is an app-imposed usage limit during the free trial allowed under App Store policy? For example, blocking further orders once a merchant has used a set number of units during the trial.

Question 13 (Partner API activeSubscription at release-candidate version). This is a different API from App Events. Questions 0 and 10 are about App Events on the path where we emit usage. This question is about the Partner API activeSubscription(appId:, shopId:) query, which is the only source we have for a merchant’s subscription status, billing cycle dates, trial end, current usage count, plan handle, and included allowance. It exists only in the 2026-07 release candidate and in unstable; 2026-04 rejects the field. Is it safe to build a production app on it now? Is there a target date for 2026-07 stable? And are the following behaviors, which we observed in live reads and could not find documented, intended and stable:

  • PriceTier order in the response has the upTo: null tier first, not last as documented.
  • A cancelled subscription still returns an object rather than null.
  • On a development store every price in the response reads 0.0.

Thanks again for taking these on.

Hey @Kasi_Prasad - thanks for all the details here.

One important update is that App Events is now documented at stable 2026-07. I’d just like to confirm whether you see the same billing behaviour using POST https://api.shopify.com/app/2026-07/events, this might work for some of your use cases here.

For the edge cases around negative usage, trial extensions, and the cancelled activeSubscription response, some private test details would help. If you’re open to it, I can set up a DM so you can share the app ID, test shop, event timestamps, and relevant App Billing Event results without posting them publicly.

I’m happy to look into this with you and will loop back here with anything we can share publicly.

Thanks Alan. Two updates first, then four observations from a full run against our development app on 2026-08-28, and two new questions.

Stable App Events. Confirmed. We pinned 2026-07 on 2026-08-26, and on 2026-08-28 we sent two usage events to POST https://api.shopify.com/app/2026-07/events, each with a timestamp equal to the order’s processed_at. Both returned 202 and both show as OK under Logs > App Billing Event. The behavior matches what we saw on unstable. That withdraws question 0 (unstable and App Store review) and question 10 (breaking-change notice while unstable), and the first two parts of question 13 (“safe to build on now” and “target stable date”). The three observed behaviors in question 13 still stand, since they are not documented for any version.

DM. Yes, please. I will share the app ID, the test shops, the event timestamps and the App Billing Event results there, and I will keep this thread updated with whatever can be said publicly so the record stays here.

Store names below are ours; timestamps are UTC.

1. app_subscriptions/update is delivered under App Pricing. When a merchant approved a plan on the hosted plan page, Shopify delivered an app_subscriptions/update webhook to our app within two seconds (triggeredAt 2026-08-28T18:35:06Z, API version 2026-04). Partner support told us on 2026-08-18 that this topic belongs to the older Billing API and is not supported for App Pricing, and we built accordingly: we treat it as a prompt to re-read the subscription from the Partner API, never as the source of any fact. Is delivery on plan approval intended and documented for App Pricing apps, and which other subscription changes send it?

2. Uninstall schedules a cancellation at the end of the cycle; it does not cancel. On two development stores in a paid cycle, uninstalling the app at 13:41 produced cancelAtEndOfCycle: true on the Partner API’s activeSubscription, with the current cycle still open and ending 2026-09-20. No cancellation followed that day. After one of those stores reinstalled the app the same afternoon, the Admin API’s currentAppInstallation.activeSubscriptions returned an empty list, while the Partner API still returned the cancel-scheduled subscription, and the hosted plan page showed “Subscription expires September 20, 2026. Select a plan to keep using this app without interruption.” Three questions, which extend questions 7 and 11: (a) between uninstall and the end of the cycle, is the merchant entitled to the app, and is usage in that window billable? (b) For a reinstalled shop whose subscription is cancel-scheduled, which API is authoritative: the Admin API that shows nothing, or the Partner API that shows the subscription? (c) Does the twenty-four-hour post-uninstall window from your 2026-08-20 reply still apply when the subscription itself stays live to the end of the cycle?

3. (New, question 14.) A plan switch starts the new plan’s free trial. A development store had held our Starter plan in a paid billing cycle since 2026-08-18. On 2026-08-28 the merchant switched to our Pro plan and the new subscription came back with trialEndsAt seven days out and no current billing cycle. Switching back to Starter did the same. Switching to a third plan did the same, and so did switching back again: four switches, four fresh trials, on one store in one afternoon. All of our plans carry a seven-day trial today. The documentation says trial days are tracked per shop over 180 days with consumed days subtracted. Is the trial tracked per plan rather than per shop, so that a merchant obtains a new trial on each plan they have not tried? If so, is that intended for upgrades and downgrades? During each of those trials the subscription has no billing cycle, so any usage the merchant generates in that week cannot be billed, which is why we need to know.

4. (New, question 15.) Editing a plan’s usage tiers does not reach existing subscriptions. We changed a plan’s first usage tier upper bound and saved. A store already subscribed to that plan kept the old tier in activeSubscription.items[].price.tiers, with pendingUpdate: null, when read immediately and again seven minutes later. A store that subscribed after the edit got the new tier. Is that the rule: plan edits apply to new subscriptions only, and existing subscriptions are never migrated? If an existing subscription can be moved onto an edited plan, what triggers it, and does pendingUpdate carry it?

For completeness, the run also re-confirmed two points from question 13: development-store subscription records return every price as 0.0, including on a plan with no zero-priced tier at all, so a plan’s included allowance cannot be identified by price on a development store; and a trialing subscription reports currentBillingCycle: null.

Thanks again.

Thanks Alan. One more question, raised on the advice of Partner support.

Question 16 (event-level billing outcomes for App Events). On 2026-09-02 we asked Partner support whether any API, webhook or alert reports the billing outcome of an individual App Event. Their answer: none exists today. A 202 confirms receipt, not billing validation. The per-event result and rejection reason appear only in the Dev Dashboard under Logs > App Billing Event, with 30-day retention. The Partner API’s activeSubscription returns the current cycle’s usage quantity and cost per metered item, and the events query returns the downstream CHARGE_USAGE records, but neither ties an idempotency_key to its validation result, its assigned cycle or its resulting charge. They also said the Dev Dashboard may not be scraped, and that there is no publicly committed timeline for a per-event read API. They suggested raising it here, so this extends question 9.

We are a usage-billed app. Our ledger records every unit we send and compares its totals with activeSubscription. That tells us a cycle is short by N events. It does not tell us which events Shopify rejected or why. We cannot resend to find out: the original key is deduplicated and returns the original 202, and a new key double-bills any event that was in fact accepted. So a mismatch today means a person opens the Dev Dashboard and reads the log, event by event, inside 30 days. Our merchants will ask us what they were billed for, per unit, and we cannot answer that from what the platform exposes.

(a) Is a per-event read of App Events billing outcomes on the roadmap: for an app and an idempotency_key, whether the event was accepted or rejected, the rejection reason from the list in your 2026-08-20 reply, the billing cycle it was assigned to, and the charge it contributed to? If not, please treat this post as the requirement. A webhook or notification on rejection would cover the alerting half in the meantime.

(b) Until then, can the App Billing Event log be exported or downloaded, and can its retention be extended past 30 days? A billing dispute often arrives after 30 days. Once the log has aged out, what record does Shopify hold of a rejected event, and can a partner obtain it through support?

(c) Does a rejected event consume its idempotency_key? Partner support’s description of resends implies that it does. If it does, the only correction path is a new key, which is the double-billing risk above. If it does not, a corrected resend under the same key is safe. Which is it?

(d) What does the merchant see for usage on their Shopify invoice: one aggregate usage charge per cycle, or a per-event breakdown? We need to reconcile what we recorded with what the merchant was billed, and to explain any difference to them.

Thanks again.