<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>gplay | Blog</title><description>gplay is a fast, single-binary Google Play Console CLI. Release Android apps, manage subscriptions and in-app products, verify purchases, and monitor crashes — from the terminal or an AI agent.</description><link>https://playconsolecli.com/</link><language>en</language><item><title>AI-generated paywalls linked to real Google Play products: RevenueCat MCP + gplay</title><link>https://playconsolecli.com/blog/ai-generated-paywalls-google-play-revenuecat/</link><guid isPermaLink="true">https://playconsolecli.com/blog/ai-generated-paywalls-google-play-revenuecat/</guid><description>RevenueCat&apos;s MCP can generate a paywall from a natural-language brief. But a paywall is only useful if it&apos;s wired to real store products. Here&apos;s how to combine RC&apos;s paywall AI with gplay-created Google Play subscriptions to ship a fully-wired paywall in one prompt.</description><pubDate>Sun, 05 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;RevenueCat’s MCP server exposes an &lt;a href=&quot;https://www.revenuecat.com/docs/tools/rc-mcp&quot;&gt;AI paywall generator&lt;/a&gt; — describe what you want, get a rendered paywall. Good, but a paywall pointing at products that don’t exist isn’t a paywall, it’s a mockup. To ship, the paywall needs to be linked to a real Google Play subscription with real base plans, real offers, and real prices.&lt;/p&gt;
&lt;p&gt;That’s where &lt;a href=&quot;https://playconsolecli.com/&quot;&gt;gplay&lt;/a&gt; fits: it creates the actual Play-side objects, RC MCP builds the paywall around them, and your AI agent wires them together.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;div&gt;&lt;h2 id=&quot;the-two-tool-workflow&quot;&gt;The two-tool workflow&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The pattern:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;gplay&lt;/strong&gt; creates the Google Play subscription, base plans, offers, and per-country prices.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RevenueCat MCP&lt;/strong&gt; registers those products, wraps them in an entitlement and offering, and generates a paywall referencing them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RevenueCat MCP&lt;/strong&gt; renders a screenshot for review.&lt;/li&gt;
&lt;li&gt;You (or your agent) iterate on the paywall copy, re-render, repeat.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Every step is scriptable. The Google Play side is the ground truth; the paywall renders on top of that truth.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;full-example-launching-a-pro-paywall&quot;&gt;Full example: launching a “Pro” paywall&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;step-1--create-the-play-side-subscription-with-gplay&quot;&gt;Step 1 — create the Play-side subscription with gplay&lt;/h3&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subscriptions&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--product-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pro&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--listing&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;en-US:title=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Pro&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,description=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Unlimited access&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;baseplans&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--subscription-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pro&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--base-plan-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;monthly&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--billing-period&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;P1M&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--auto-renewing&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;baseplans&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--subscription-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pro&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--base-plan-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;yearly&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--billing-period&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;P1Y&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--auto-renewing&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;baseplans&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prices&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--region&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;US&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--price-micros&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;9990000&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--subscription-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pro&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--base-plan-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;monthly&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;baseplans&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prices&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;convert&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--from-region&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;US&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--subscription-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pro&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--base-plan-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;monthly&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;baseplans&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prices&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--region&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;US&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--price-micros&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;89990000&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--subscription-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pro&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--base-plan-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;yearly&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;baseplans&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prices&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;convert&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--from-region&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;US&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--subscription-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pro&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--base-plan-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;yearly&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;offers&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--subscription-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pro&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--base-plan-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;monthly&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--offer-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;monthly-trial-7d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--phases&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FREE_TRIAL,P7D,0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subscriptions&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;activate&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--product-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pro&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The Play-side is now real: &lt;code dir=&quot;auto&quot;&gt;pro:monthly&lt;/code&gt; at $9.99 with 7-day trial, &lt;code dir=&quot;auto&quot;&gt;pro:yearly&lt;/code&gt; at $89.99, PPP-converted across 175 countries.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;step-2--prompt-your-ai-agent-to-build-the-rc-side--paywall&quot;&gt;Step 2 — prompt your AI agent to build the RC side + paywall&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Open Claude Code (or Cursor, or any of the &lt;a href=&quot;https://playconsolecli.com/guides/ai-agents/&quot;&gt;12 supported agents&lt;/a&gt;) and prompt:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Using the RevenueCat MCP, wire up the “Pro” subscription for &lt;code dir=&quot;auto&quot;&gt;com.example.app&lt;/code&gt; and generate a paywall.&lt;/p&gt;
&lt;p&gt;Products to register (Google Play): &lt;code dir=&quot;auto&quot;&gt;pro:monthly&lt;/code&gt; (subscription &lt;code dir=&quot;auto&quot;&gt;pro&lt;/code&gt;, base plan &lt;code dir=&quot;auto&quot;&gt;monthly&lt;/code&gt;) and &lt;code dir=&quot;auto&quot;&gt;pro:yearly&lt;/code&gt; (subscription &lt;code dir=&quot;auto&quot;&gt;pro&lt;/code&gt;, base plan &lt;code dir=&quot;auto&quot;&gt;yearly&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Create a “Pro” entitlement, attach both products. Create a “default” offering with &lt;code dir=&quot;auto&quot;&gt;$rc_monthly&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;$rc_annual&lt;/code&gt; packages.&lt;/p&gt;
&lt;p&gt;Then generate a paywall:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Hero: “Try Pro free for 7 days”&lt;/li&gt;
&lt;li&gt;Emphasize the yearly plan (25% savings vs monthly)&lt;/li&gt;
&lt;li&gt;Feature list: read from README.md, take the top 4 items&lt;/li&gt;
&lt;li&gt;Style: same brand palette as our existing “starter” paywall (get it from RC to reference)&lt;/li&gt;
&lt;li&gt;Trust signal: “Join 50,000+ Pro users”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When done, render a screenshot and show it to me.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div&gt;&lt;h3 id=&quot;step-3--what-the-agent-does&quot;&gt;Step 3 — what the agent does&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Chains of tool calls, roughly:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;RC MCP &lt;code dir=&quot;auto&quot;&gt;create-product-in-store&lt;/code&gt; × 2 (monthly and yearly).&lt;/li&gt;
&lt;li&gt;RC MCP &lt;code dir=&quot;auto&quot;&gt;create-entitlement&lt;/code&gt; “Pro”.&lt;/li&gt;
&lt;li&gt;RC MCP &lt;code dir=&quot;auto&quot;&gt;attach-products-to-entitlement&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;RC MCP &lt;code dir=&quot;auto&quot;&gt;create-offering&lt;/code&gt; “default”.&lt;/li&gt;
&lt;li&gt;RC MCP &lt;code dir=&quot;auto&quot;&gt;create-packages&lt;/code&gt; (&lt;code dir=&quot;auto&quot;&gt;$rc_monthly&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;$rc_annual&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;RC MCP &lt;code dir=&quot;auto&quot;&gt;attach-products-to-package&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;cat README.md&lt;/code&gt; → extract feature list.&lt;/li&gt;
&lt;li&gt;RC MCP &lt;code dir=&quot;auto&quot;&gt;list-paywalls&lt;/code&gt; → find “starter” → &lt;code dir=&quot;auto&quot;&gt;get-paywall&lt;/code&gt; for style reference.&lt;/li&gt;
&lt;li&gt;RC MCP &lt;code dir=&quot;auto&quot;&gt;create-paywall-ai&lt;/code&gt; with the assembled brief → returns task ID.&lt;/li&gt;
&lt;li&gt;RC MCP poll &lt;code dir=&quot;auto&quot;&gt;get-paywall-ai-task&lt;/code&gt; → wait ~30s.&lt;/li&gt;
&lt;li&gt;RC MCP &lt;code dir=&quot;auto&quot;&gt;render-paywall-screenshot&lt;/code&gt; → PNG saved to &lt;code dir=&quot;auto&quot;&gt;./paywalls/pro-launch.png&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You get back the screenshot. You look at it.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;step-4--iterate&quot;&gt;Step 4 — iterate&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;The magic isn’t the first render, it’s the second and third.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;That looks great but the trial CTA is too subtle. Make it a persistent button at the bottom in accent color, and add “No commitment, cancel anytime” microcopy under it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Agent calls RC MCP &lt;code dir=&quot;auto&quot;&gt;edit-paywall-ai&lt;/code&gt; with the change brief → new task → re-render.&lt;/p&gt;
&lt;p&gt;Or:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Try three variants: the current one, one where yearly is the default selection, one with the testimonial replaced by a screenshot mockup. Render all three and show me side-by-side.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Agent chains &lt;code dir=&quot;auto&quot;&gt;create-paywall-ai&lt;/code&gt; three times with variant briefs → renders all three → shows you the grid.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;step-5--ship-it&quot;&gt;Step 5 — ship it&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;When the paywall’s right, wire your app’s SDK to fetch it via RC’s &lt;code dir=&quot;auto&quot;&gt;Purchases.getOfferings()&lt;/code&gt;. Nothing else to configure — the paywall lives inside RC, references your products, which reference your gplay-created Play subscriptions. Every layer is real.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;verification&quot;&gt;Verification&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Before you ship, double-check the plumbing:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 1. Play-side products actually exist and are active&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subscriptions&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--product-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pro&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Prompt the agent:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Look up the RC customer for a sandbox test user, verify they can see the “default” offering with both packages, and that &lt;code dir=&quot;auto&quot;&gt;$rc_monthly&lt;/code&gt; price matches Google Play US ($9.99).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;RC MCP &lt;code dir=&quot;auto&quot;&gt;get-customer&lt;/code&gt; → &lt;code dir=&quot;auto&quot;&gt;list-purchases&lt;/code&gt; → cross-reference with &lt;code dir=&quot;auto&quot;&gt;gplay baseplans prices list --region US&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If Play says $9.99 and RC says $9.99, you’re wired end-to-end.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;where-this-saves-the-most-time&quot;&gt;Where this saves the most time&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Marketing iteration.&lt;/strong&gt; “Try a version with a countdown timer and social proof.” Fifteen seconds prompt to rendered variant. No designer round-trip.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Localized paywalls.&lt;/strong&gt; “Generate the paywall in Japanese, French, and German using the Play Console’s localized offer names as the source of truth.” RC MCP creates variants; the agent pulls localized names via &lt;code dir=&quot;auto&quot;&gt;gplay offers locales list&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A/B test scaffolding.&lt;/strong&gt; “Set up an experiment: 50% of new users get the current paywall, 50% get the countdown variant. Run for 2 weeks.” RC MCP &lt;code dir=&quot;auto&quot;&gt;list-experiments&lt;/code&gt; → create → assign traffic. Two weeks later, &lt;code dir=&quot;auto&quot;&gt;get-experiment-results&lt;/code&gt; and the agent summarizes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Compliance rerender.&lt;/strong&gt; “The Play Console policy team requires we show the auto-renew disclosure above the fold. Update every paywall in the account and re-render.” Loop of &lt;code dir=&quot;auto&quot;&gt;list-paywalls&lt;/code&gt; → &lt;code dir=&quot;auto&quot;&gt;edit-paywall-ai&lt;/code&gt; → &lt;code dir=&quot;auto&quot;&gt;render-paywall-screenshot&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;fastlane-gradle-play-publisher-hand-coded-paywall-builders&quot;&gt;Fastlane, gradle-play-publisher, hand-coded paywall builders&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Not part of this workflow. Fastlane and gradle-play-publisher don’t touch monetization at all. Hand-coded paywalls (SwiftUI/Jetpack Compose views maintained in your app repo) mean every copy change is a code change, a build, a release. RC’s paywall system + gplay-created products means paywall changes are remote-configured, and product changes are one gplay command.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;getting-started&quot;&gt;Getting started&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tamtom/tap/gplay&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--auto&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Set up the &lt;a href=&quot;https://www.revenuecat.com/docs/tools/rc-mcp&quot;&gt;RevenueCat MCP&lt;/a&gt; with your RC secret key. Install the &lt;a href=&quot;https://github.com/tamtom/gplay-cli-skills&quot;&gt;IAP setup skill&lt;/a&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;skills&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tamtom/gplay-cli-skills&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Then prompt your agent to launch a subscription and paywall in one go. Full subscription reference at &lt;a href=&quot;https://playconsolecli.com/reference/subscriptions/&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;/reference/subscriptions/&lt;/code&gt;&lt;/a&gt;, offers at &lt;a href=&quot;https://playconsolecli.com/reference/offers/&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;/reference/offers/&lt;/code&gt;&lt;/a&gt;, base plans at &lt;a href=&quot;https://playconsolecli.com/reference/baseplans/&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;/reference/baseplans/&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>revenuecat</category><category>paywalls</category><category>ai-agents</category></item><item><title>Automate Google Play Console with Claude Code: a hands-on tutorial</title><link>https://playconsolecli.com/blog/automate-google-play-with-claude-code/</link><guid isPermaLink="true">https://playconsolecli.com/blog/automate-google-play-with-claude-code/</guid><description>Point Claude Code at gplay and it drives your entire Google Play release flow — upload, staged rollout, promotion, listings, subscriptions, crash triage — from natural-language prompts. Here&apos;s how, with real prompt transcripts.</description><pubDate>Sun, 05 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Claude Code is Anthropic’s terminal-native AI coding agent. Combined with &lt;a href=&quot;https://playconsolecli.com/&quot;&gt;gplay&lt;/a&gt; — the AI-agent-native Google Play Console CLI — it’s the closest thing to a “release manager in a chat window” that Android publishing has ever had.&lt;/p&gt;
&lt;p&gt;This tutorial shows the setup, the prompts that actually work, and what to watch out for.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;div&gt;&lt;h2 id=&quot;why-gplay-was-built-for-agents-like-claude-code&quot;&gt;Why gplay was built for agents like Claude Code&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Before we plug them together, it’s worth calling out why this pairing is unusually clean:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;gplay’s output is JSON by default.&lt;/strong&gt; Claude Code parses it directly. No log scraping.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Every command has &lt;code dir=&quot;auto&quot;&gt;--help&lt;/code&gt;.&lt;/strong&gt; Claude Code reads it when it needs to; the CLI is self-documenting.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Every write has &lt;code dir=&quot;auto&quot;&gt;--dry-run&lt;/code&gt;.&lt;/strong&gt; Claude Code can preview a change before executing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Explicit long flags&lt;/strong&gt; — &lt;code dir=&quot;auto&quot;&gt;--package&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;--track&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;--rollout&lt;/code&gt;. Nothing ambiguous to hallucinate.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No interactive prompts.&lt;/strong&gt; Destructive operations use &lt;code dir=&quot;auto&quot;&gt;--confirm&lt;/code&gt;, so Claude Code never has to answer a Y/N.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The result: Claude Code drives gplay reliably without a wrapper, MCP server, or shim. It’s just a CLI it can call.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;one-time-setup&quot;&gt;One-time setup&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;1-install-gplay&quot;&gt;1. Install gplay&lt;/h3&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tamtom/tap/gplay&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;(Install script, Windows PowerShell, and manual binaries at &lt;a href=&quot;https://playconsolecli.com/guides/installation/&quot;&gt;/guides/installation/&lt;/a&gt;.)&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;2-authenticate&quot;&gt;2. Authenticate&lt;/h3&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--auto&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;--auto&lt;/code&gt; handles the whole service-account dance: installs &lt;code dir=&quot;auto&quot;&gt;gcloud&lt;/code&gt; if missing, enables the Android Publisher API, creates a service account, downloads its key, invites it to your Play Console developer account. No dashboard-hunting.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;3-install-the-agent-skills&quot;&gt;3. Install the Agent Skills&lt;/h3&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;skills&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tamtom/gplay-cli-skills&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This gives Claude Code 16 skills — release flow, staged rollouts, IAP setup, subscription localization, purchase verification, vitals monitoring, review management, screenshot automation, testers, metadata sync, Fastlane migration, pre-submission checks, and more.&lt;/p&gt;
&lt;p&gt;Each skill is a small markdown file that teaches Claude Code the &lt;em&gt;real&lt;/em&gt; Play Console workflow: what a track promotion actually looks like end-to-end, which validation to run before hitting Google, what to do when a rollout gets halted.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;example-prompts-that-just-work&quot;&gt;Example prompts that just work&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The pattern is: describe the outcome, not the flags. Claude Code figures out the flags from &lt;code dir=&quot;auto&quot;&gt;--help&lt;/code&gt; and the skills.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;release-an-internal-build-and-promote-when-green&quot;&gt;Release an internal build and promote when green&lt;/h3&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Release &lt;code dir=&quot;auto&quot;&gt;app-release.aab&lt;/code&gt; to the internal track for &lt;code dir=&quot;auto&quot;&gt;com.example.app&lt;/code&gt;. Once it’s live and vitals look clean, promote it to closed alpha at 20% rollout.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Claude Code will:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Run &lt;code dir=&quot;auto&quot;&gt;gplay apps list&lt;/code&gt; to sanity-check access.&lt;/li&gt;
&lt;li&gt;Run &lt;code dir=&quot;auto&quot;&gt;gplay release --track internal --bundle app-release.aab --package com.example.app&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Poll &lt;code dir=&quot;auto&quot;&gt;gplay tracks get --package com.example.app --track internal&lt;/code&gt; until the release status is &lt;code dir=&quot;auto&quot;&gt;completed&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Query &lt;code dir=&quot;auto&quot;&gt;gplay vitals crashes query --package com.example.app --time-range LAST_7_DAYS&lt;/code&gt; and compare against baseline.&lt;/li&gt;
&lt;li&gt;If clean, run &lt;code dir=&quot;auto&quot;&gt;gplay tracks promote --package com.example.app --from internal --to alpha --rollout 0.2&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You watch it work. Every write it’s about to make, it says out loud. You approve or interrupt.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;update-subscriptions-from-a-spec-file&quot;&gt;Update subscriptions from a spec file&lt;/h3&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Read &lt;code dir=&quot;auto&quot;&gt;subs.yaml&lt;/code&gt; — for each subscription, make sure the base plan exists at the listed price, create it if missing, and localize the offer name for &lt;code dir=&quot;auto&quot;&gt;en-US, fr-FR, de-DE, es-ES&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Claude Code will iterate: &lt;code dir=&quot;auto&quot;&gt;gplay subscriptions get&lt;/code&gt;, diff against the YAML, &lt;code dir=&quot;auto&quot;&gt;gplay baseplans create&lt;/code&gt; where needed, &lt;code dir=&quot;auto&quot;&gt;gplay offers create&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;gplay subscriptions locales set&lt;/code&gt; for each locale.&lt;/p&gt;
&lt;p&gt;Fastlane can’t do that. gradle-play-publisher can’t do that. This is the entire monetization surface, driven by natural language.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;triage-crash-regressions&quot;&gt;Triage crash regressions&lt;/h3&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Grab this week’s top crash clusters. Compare user impact vs last week. If any cluster is over 500 users and up more than 30%, file a GitHub issue with the stack trace.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Claude Code chains &lt;code dir=&quot;auto&quot;&gt;gplay vitals crashes query&lt;/code&gt;, does the math itself, and calls &lt;code dir=&quot;auto&quot;&gt;gh issue create&lt;/code&gt;. It doesn’t need a separate observability tool — the Play Console API is the observability tool.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;reply-to-reviews&quot;&gt;Reply to reviews&lt;/h3&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;List all 1-star reviews from the last 14 days that mention “login”. Draft a reply for each and show me the list to approve before sending.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;gplay reviews list --filter&lt;/code&gt; → per-review draft → &lt;code dir=&quot;auto&quot;&gt;--dry-run&lt;/code&gt; reply preview → you approve → &lt;code dir=&quot;auto&quot;&gt;gplay reviews reply&lt;/code&gt;. Zero clicks in the Play Console web UI.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;guardrails-how-to-keep-it-safe&quot;&gt;Guardrails: how to keep it safe&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;A few habits that make this pairing feel controlled instead of chaotic:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use &lt;code dir=&quot;auto&quot;&gt;--dry-run&lt;/code&gt; in the prompt.&lt;/strong&gt; For anything you’re not sure about: &lt;em&gt;“…do it as &lt;code dir=&quot;auto&quot;&gt;--dry-run&lt;/code&gt; first and show me the payload.”&lt;/em&gt; gplay intercepts every write HTTP method and logs the request to stderr without executing. Claude Code will echo the payload back to you before actually running.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Give it a scoped service account.&lt;/strong&gt; Run gplay under a service account that’s granted only the Play permissions it needs (or a per-app grant instead of account-wide). If Claude Code goes off-script, the blast radius is bounded to what that account can touch.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use an edit session for related changes.&lt;/strong&gt; &lt;code dir=&quot;auto&quot;&gt;gplay edit begin&lt;/code&gt; starts a transactional edit; multiple changes commit atomically with &lt;code dir=&quot;auto&quot;&gt;gplay edit commit&lt;/code&gt;. If Claude Code is doing a multi-step listing update and something looks off, &lt;code dir=&quot;auto&quot;&gt;gplay edit cancel&lt;/code&gt; throws it all away.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Read the diff.&lt;/strong&gt; Every write, Claude Code shows you the exact command. Read it before approving. Same rule as reviewing a PR.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;what-about-cursor-openclaw-hermes-agent-codex-gemini-cli&quot;&gt;What about Cursor, OpenClaw, Hermes Agent, Codex, Gemini CLI…?&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Same story. gplay’s design isn’t Claude-specific — it works with every major AI coding agent of 2026: &lt;strong&gt;Claude Code, Cursor, Codex, Gemini CLI, Aider, Cline, Windsurf, Continue, GitHub Copilot CLI, OpenClaw, Hermes Agent, Amazon Q Developer&lt;/strong&gt;. The Agent Skills package is the same one-liner for all of them.&lt;/p&gt;
&lt;p&gt;The reason we lead with Claude Code in tutorials is that its terminal-native design lets it invoke a CLI directly without an MCP wrapper or plugin layer — the shortest path from prompt to &lt;code dir=&quot;auto&quot;&gt;gplay tracks promote&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;getting-started&quot;&gt;Getting started&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tamtom/tap/gplay&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--auto&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;skills&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tamtom/gplay-cli-skills&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Then open Claude Code in the project you want to release from and try:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What can you do with gplay for &lt;code dir=&quot;auto&quot;&gt;com.example.app&lt;/code&gt;?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It’ll enumerate the surface and offer a starting point. Take it from there.&lt;/p&gt;
&lt;p&gt;Full reference at &lt;a href=&quot;https://playconsolecli.com/reference/&quot;&gt;/reference/&lt;/a&gt;, guides at &lt;a href=&quot;https://playconsolecli.com/guides/quickstart/&quot;&gt;/guides/&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>claude-code</category><category>ai-agents</category><category>tutorial</category></item><item><title>Automate your full mobile subscription stack: Claude Code + RevenueCat MCP + gplay</title><link>https://playconsolecli.com/blog/claude-code-revenuecat-mcp-gplay-full-stack/</link><guid isPermaLink="true">https://playconsolecli.com/blog/claude-code-revenuecat-mcp-gplay-full-stack/</guid><description>One AI agent, one prompt, one full launch. Claude Code with the RevenueCat MCP plus gplay&apos;s Google Play CLI can stand up a new subscription tier — Play-side products, RC entitlements, offerings, paywalls, targeting rules — end to end. Here&apos;s what that looks like.</description><pubDate>Sun, 05 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Historically, launching a new mobile subscription tier meant a checklist across three or four dashboards: Play Console for the Android product, App Store Connect for iOS, RevenueCat for entitlements and offerings, some paywall builder for the UI. Each dashboard has its own click paths, its own idempotency semantics, its own way to be wrong.&lt;/p&gt;
&lt;p&gt;With &lt;a href=&quot;https://playconsolecli.com/blog/automate-google-play-with-claude-code/&quot;&gt;Claude Code&lt;/a&gt;, the &lt;a href=&quot;https://www.revenuecat.com/docs/tools/rc-mcp&quot;&gt;RevenueCat MCP server&lt;/a&gt;, and &lt;a href=&quot;https://playconsolecli.com/&quot;&gt;gplay&lt;/a&gt;, the whole thing becomes one prompt.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;div&gt;&lt;h2 id=&quot;the-setup&quot;&gt;The setup&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Three ingredients:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Claude Code&lt;/strong&gt; — Anthropic’s terminal-native AI coding agent. Reads your project, calls tools, writes code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RevenueCat MCP&lt;/strong&gt; — the RevenueCat team’s Model Context Protocol server. Exposes entitlements, offerings, packages, products, paywalls, customers, and revenue analytics as callable tools.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;gplay&lt;/strong&gt; — the AI-agent-native Google Play Console CLI. Covers 250+ commands across 6 Google APIs, JSON-first, &lt;code dir=&quot;auto&quot;&gt;--dry-run&lt;/code&gt; on every write.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Install:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# gplay&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tamtom/tap/gplay&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--auto&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Agent Skills so Claude Code knows the real Play workflows&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;skills&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tamtom/gplay-cli-skills&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;RevenueCat MCP setup: follow &lt;a href=&quot;https://www.revenuecat.com/docs/tools/rc-mcp&quot;&gt;their docs&lt;/a&gt;. Adds a block to your Claude Code config that registers the MCP server with your RC secret key.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;what-claude-code-can-now-do&quot;&gt;What Claude Code can now do&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Every RevenueCat MCP tool + every &lt;code dir=&quot;auto&quot;&gt;gplay&lt;/code&gt; command + &lt;code dir=&quot;auto&quot;&gt;gh&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;git&lt;/code&gt;, your Gradle build. The intersection is powerful.&lt;/p&gt;
&lt;p&gt;Here’s a session that stood up a real subscription tier in one prompt (transcript compressed).&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;the-prompt&quot;&gt;The prompt&lt;/h3&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Launch a new “Pro” subscription for &lt;code dir=&quot;auto&quot;&gt;com.example.app&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Monthly at $9.99 with a 7-day free trial for new users, PPP prices to all supported countries.&lt;/li&gt;
&lt;li&gt;Yearly at $89.99 (25% saving vs monthly).&lt;/li&gt;
&lt;li&gt;Register both as Play products, then map into RevenueCat as a “Pro” entitlement, an “default” offering with $rc_monthly and $rc_annual packages.&lt;/li&gt;
&lt;li&gt;Generate an AI paywall — hero: “Unlock Pro”, features from the app’s README, testimonial style similar to our existing “starter” paywall.&lt;/li&gt;
&lt;li&gt;Add a targeting rule: users on our existing “starter” entitlement get shown a “$rc_annual” upsell offering instead of “default”.&lt;/li&gt;
&lt;li&gt;Show me the final paywall screenshot and a summary of what you shipped. Don’t actually flip anything live — use &lt;code dir=&quot;auto&quot;&gt;--dry-run&lt;/code&gt; for the Play writes so I can review.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;div&gt;&lt;h3 id=&quot;what-claude-code-did-chained-tool-calls&quot;&gt;What Claude Code did (chained tool calls)&lt;/h3&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;gplay apps list&lt;/code&gt; → confirmed access to &lt;code dir=&quot;auto&quot;&gt;com.example.app&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;gplay subscriptions create --product-id pro --listing en-US:...&lt;/code&gt; (dry-run) → showed the JSON payload.&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;gplay baseplans create --base-plan-id monthly --billing-period P1M ...&lt;/code&gt; (dry-run) for both plans.&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;gplay baseplans prices set --region US --price-micros 9990000&lt;/code&gt; → then &lt;code dir=&quot;auto&quot;&gt;gplay baseplans prices convert --from-region US&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;gplay offers create --offer-id monthly-trial-7d --phases FREE_TRIAL,P7D,0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RC MCP&lt;/strong&gt; &lt;code dir=&quot;auto&quot;&gt;create-product-in-store&lt;/code&gt; → registered &lt;code dir=&quot;auto&quot;&gt;pro:monthly&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;pro:yearly&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RC MCP&lt;/strong&gt; &lt;code dir=&quot;auto&quot;&gt;create-entitlement&lt;/code&gt; → “Pro”.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RC MCP&lt;/strong&gt; &lt;code dir=&quot;auto&quot;&gt;attach-products-to-entitlement&lt;/code&gt; → linked both products.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RC MCP&lt;/strong&gt; &lt;code dir=&quot;auto&quot;&gt;create-offering&lt;/code&gt; “default” → &lt;code dir=&quot;auto&quot;&gt;create-packages&lt;/code&gt; for &lt;code dir=&quot;auto&quot;&gt;$rc_monthly&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;$rc_annual&lt;/code&gt; → &lt;code dir=&quot;auto&quot;&gt;attach-products-to-package&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;cat README.md&lt;/code&gt; → extracted feature list for the paywall copy.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RC MCP&lt;/strong&gt; &lt;code dir=&quot;auto&quot;&gt;list-paywalls&lt;/code&gt; → found existing “starter” paywall → &lt;code dir=&quot;auto&quot;&gt;get-paywall&lt;/code&gt; for style reference.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RC MCP&lt;/strong&gt; &lt;code dir=&quot;auto&quot;&gt;create-paywall-ai&lt;/code&gt; with the description → returned task ID.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RC MCP&lt;/strong&gt; poll &lt;code dir=&quot;auto&quot;&gt;get-paywall-ai-task&lt;/code&gt; → 30s later, complete.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RC MCP&lt;/strong&gt; &lt;code dir=&quot;auto&quot;&gt;render-paywall-screenshot&lt;/code&gt; → saved PNG to &lt;code dir=&quot;auto&quot;&gt;./paywalls/pro-launch.png&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RC MCP&lt;/strong&gt; &lt;code dir=&quot;auto&quot;&gt;list-targeting-rules&lt;/code&gt; → confirmed “starter” entitlement exists.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RC MCP&lt;/strong&gt; create targeting rule: &lt;code dir=&quot;auto&quot;&gt;entitlement=starter&lt;/code&gt; → &lt;code dir=&quot;auto&quot;&gt;assign-customer-offering annual-upsell&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Printed a summary table of everything created + the paywall screenshot.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You read the dry-run payloads. If they look right, you say “go” and Claude Code re-runs step 2-9 for real (drops &lt;code dir=&quot;auto&quot;&gt;--dry-run&lt;/code&gt; from the gplay commands; RC MCP calls are idempotent).&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;where-this-pays-off-long-term&quot;&gt;Where this pays off long-term&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The one-prompt launch is the flashy demo. The real value shows up in the ongoing operations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Weekly business review, prompt-driven:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Pull the last 7 days of RevenueCat overview metrics. For any product with &gt;10% trial-to-paid drop-off vs the previous week, cross-reference with gplay’s vitals — are there crashes on the paywall screen? Rank issues by revenue impact and file GitHub issues for the top three.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Chains: RC MCP &lt;code dir=&quot;auto&quot;&gt;get-overview-metrics&lt;/code&gt; → RC MCP &lt;code dir=&quot;auto&quot;&gt;get-chart-data&lt;/code&gt; for trial cohorts → gplay &lt;code dir=&quot;auto&quot;&gt;vitals crashes query&lt;/code&gt; filtered to specific screens → RC MCP &lt;code dir=&quot;auto&quot;&gt;get-benchmarks&lt;/code&gt; for industry context → &lt;code dir=&quot;auto&quot;&gt;gh issue create&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Price experiments, safely:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Add a targeting rule: 10% of new Indian users get the “budget” offering with a ₹299 monthly price. Everyone else stays on default. Wire it up in RC and make sure the ₹299 monthly base plan actually exists on Google Play (create it if not).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Chains: gplay &lt;code dir=&quot;auto&quot;&gt;baseplans list&lt;/code&gt; → conditional &lt;code dir=&quot;auto&quot;&gt;baseplans create&lt;/code&gt; → gplay &lt;code dir=&quot;auto&quot;&gt;baseplans prices set --region IN&lt;/code&gt; → RC MCP &lt;code dir=&quot;auto&quot;&gt;create-offering&lt;/code&gt; “budget” → &lt;code dir=&quot;auto&quot;&gt;create-targeting-rule&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Refund reconciliation:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Pull the last 24 hours of Google Play voided purchases. For each, revoke the RevenueCat entitlement grant for that customer.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Chains: gplay &lt;code dir=&quot;auto&quot;&gt;purchases voided list --start-time &amp;#x3C;24h ago&gt;&lt;/code&gt; → for each: RC MCP &lt;code dir=&quot;auto&quot;&gt;get-customer&lt;/code&gt; by &lt;code dir=&quot;auto&quot;&gt;originalTransactionId&lt;/code&gt; → RC MCP revoke grant.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;why-the-pairing-works-so-well&quot;&gt;Why the pairing works so well&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Both surfaces are agent-native, but they cover different territory:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;gplay is the writer of truth.&lt;/strong&gt; It manipulates Play Console state directly. When you need something to actually exist in Google Play, gplay does it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RC MCP is the reader of state.&lt;/strong&gt; It sees your Play products (via RC’s own webhooks + polling), aggregates the cross-store view, and lets the agent reason about entitlements/customers/revenue across iOS and Android.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Neither alone gets you to “one-prompt launch.” Together they do.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;getting-started&quot;&gt;Getting started&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tamtom/tap/gplay&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--auto&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;skills&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tamtom/gplay-cli-skills&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Add the &lt;a href=&quot;https://www.revenuecat.com/docs/tools/rc-mcp&quot;&gt;RevenueCat MCP&lt;/a&gt; to your Claude Code config with your RC secret key. Then open Claude Code in your Android project and try:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Show me every subscription on Google Play for &lt;code dir=&quot;auto&quot;&gt;com.example.app&lt;/code&gt;, then show me the matching RevenueCat entitlements and offerings. Any drift?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The answer to that question was previously a 30-minute audit across two dashboards. Now it’s a prompt.&lt;/p&gt;
</content:encoded><category>revenuecat</category><category>claude-code</category><category>ai-agents</category><category>subscriptions</category></item><item><title>gplay vs gradle-play-publisher: which is the better Google Play CLI in 2026?</title><link>https://playconsolecli.com/blog/gplay-vs-gradle-play-publisher/</link><guid isPermaLink="true">https://playconsolecli.com/blog/gplay-vs-gradle-play-publisher/</guid><description>gradle-play-publisher is the Gradle-native way to push builds to Google Play. gplay is a standalone CLI that covers the entire Play Console. Here&apos;s an honest comparison across build integration, feature coverage, runtime, and CI ergonomics.</description><pubDate>Sun, 05 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If you build Android apps with Gradle, &lt;a href=&quot;https://github.com/Triple-T/gradle-play-publisher&quot;&gt;gradle-play-publisher&lt;/a&gt; (GPP) has probably crossed your radar as the way to publish to Google Play without leaving your build script. It’s a solid, focused tool. But if you’ve ever needed to do anything on Play Console beyond uploading an AAB and updating a listing — subscriptions, purchase verification, crash reports, reviews, reports — you’ve hit its ceiling.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://playconsolecli.com/&quot;&gt;gplay&lt;/a&gt; takes a different bet: one static binary, all of Play Console.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;div&gt;&lt;h2 id=&quot;the-short-version&quot;&gt;The short version&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GPP is Gradle-native.&lt;/strong&gt; If your entire release flow lives inside &lt;code dir=&quot;auto&quot;&gt;./gradlew&lt;/code&gt;, it fits naturally: &lt;code dir=&quot;auto&quot;&gt;./gradlew publishRelease&lt;/code&gt; and you’re done.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;gplay is Play Console-complete.&lt;/strong&gt; Subscriptions with base plans and offers, in-app products, purchase verification, vitals (crashes, ANRs, startup, battery), reviews, financial reports, users and permissions, Managed Google Play custom apps, Google Checks compliance — 250+ commands across 6 Google APIs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Different runtime shape.&lt;/strong&gt; GPP is a Gradle plugin (JVM, needs your build project). gplay is a static Go binary — no runtime, works from any shell, any container, any language.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;feature-comparison&quot;&gt;Feature comparison&lt;/h2&gt;&lt;/div&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;gplay&lt;/th&gt;
&lt;th&gt;gradle-play-publisher&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Upload AAB/APK to tracks&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Staged rollouts (fractional)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Promote between tracks&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Store listings (title, description, graphics)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Screenshots&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Release notes per track and locale&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deobfuscation &amp;#x26; native debug symbols&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Subscriptions (base plans, offers, prices)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Full&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;In-app products&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Server-side purchase verification&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Vitals: crashes, ANRs, performance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reviews (list + reply)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Financial &amp;#x26; statistics reports (GCS)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Users &amp;#x26; permission grants&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Managed Google Play custom apps&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Google Checks compliance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runtime&lt;/td&gt;
&lt;td&gt;Single 12MB Go binary&lt;/td&gt;
&lt;td&gt;Gradle plugin (JVM + build)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Works outside the Android project&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;⚠️ Needs Gradle context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JSON-first output for agents/scripts&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌ (Gradle output)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;--dry-run&lt;/code&gt; on writes&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;⚠️ Task-level only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;GPP covers the “publish a build” story well. Where gplay pulls ahead is everything &lt;strong&gt;after&lt;/strong&gt; publish: monitor how it’s doing, monetize it, keep it compliant, respond to users.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;when-gpp-is-the-right-pick&quot;&gt;When GPP is the right pick&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Pick gradle-play-publisher if &lt;strong&gt;all&lt;/strong&gt; of these are true:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Your release flow lives entirely inside Gradle and you like it that way.&lt;/li&gt;
&lt;li&gt;You publish AABs and update listings — no subscriptions, no purchase verification, no vitals monitoring from CI.&lt;/li&gt;
&lt;li&gt;Your Play Console work outside of &lt;code dir=&quot;auto&quot;&gt;./gradlew publishRelease&lt;/code&gt; is done manually in the web UI, and you’re happy with that split.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That’s a real use case. If it’s yours, GPP is well-designed for it.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;when-gplay-is-the-right-pick&quot;&gt;When gplay is the right pick&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Pick gplay if &lt;strong&gt;any&lt;/strong&gt; of these are true:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You have subscriptions, in-app products, or offers to manage — GPP does not cover monetization at all.&lt;/li&gt;
&lt;li&gt;You want to verify purchases server-side without pulling in the full Google API Java client and OAuth boilerplate.&lt;/li&gt;
&lt;li&gt;You want to check crashes, ANRs, or performance metrics from CI (fail the pipeline on regressions).&lt;/li&gt;
&lt;li&gt;You reply to reviews or download reports as part of your release ritual.&lt;/li&gt;
&lt;li&gt;You’re driving Play Console from an &lt;strong&gt;AI coding agent&lt;/strong&gt; (Claude Code, Cursor, OpenClaw, Hermes Agent) — gplay’s JSON-first output, explicit long flags, &lt;code dir=&quot;auto&quot;&gt;--dry-run&lt;/code&gt;, and &lt;code dir=&quot;auto&quot;&gt;--help&lt;/code&gt; discoverability are built for agents. GPP was not.&lt;/li&gt;
&lt;li&gt;You want the same tool to work on macOS, Linux CI, Windows PowerShell, and inside minimal Docker containers — no JVM, no Gradle, no project context needed.&lt;/li&gt;
&lt;li&gt;You’re using something other than Gradle (Bazel, Buck, custom build scripts, Kotlin Multiplatform with non-Gradle targets).&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;can-you-use-both&quot;&gt;Can you use both?&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Yes. A common pattern: GPP handles the “upload from &lt;code dir=&quot;auto&quot;&gt;./gradlew&lt;/code&gt;” step for developer ergonomics, and gplay handles everything else in CI — post-release rollout monitoring, subscription changes, crash alerts, review replies, monthly report downloads.&lt;/p&gt;
&lt;p&gt;They don’t conflict. They read the same service account. Nothing stops you from using GPP for uploads and gplay for the rest.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;runtime-footprint&quot;&gt;Runtime footprint&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;This one is easy to overlook until you feel it.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;gradle-play-publisher&lt;/strong&gt; runs inside your Gradle build: JVM startup, plugin resolution, project sync. On a Mac laptop that’s a few seconds; on a cold CI runner it can be 30-60 seconds before the first upload byte moves.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;gplay&lt;/strong&gt; is a 12 MB static Go binary. Cold-start is under 50 ms. In a minimal &lt;code dir=&quot;auto&quot;&gt;alpine:latest&lt;/code&gt; container, &lt;code dir=&quot;auto&quot;&gt;gplay tracks list&lt;/code&gt; returns before Gradle has finished parsing your build.gradle.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For an agent that runs dozens of Play API calls in a single session (checking crashes, updating listings, promoting rollouts) the difference compounds.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-ai-agent-angle&quot;&gt;The AI-agent angle&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;If you’re using an AI coding agent (Claude Code, Cursor, Codex, Gemini CLI, Aider, Cline, Windsurf, Continue, GitHub Copilot CLI, OpenClaw, Hermes Agent), gplay was designed for you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;JSON output by default&lt;/strong&gt; — the agent parses it directly, no scraping.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Explicit long flags&lt;/strong&gt; — &lt;code dir=&quot;auto&quot;&gt;--package&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;--track&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;--rollout&lt;/code&gt;; no ambiguous short flags to hallucinate.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code dir=&quot;auto&quot;&gt;--help&lt;/code&gt; on every command&lt;/strong&gt; — agents discover the surface instead of guessing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code dir=&quot;auto&quot;&gt;--dry-run&lt;/code&gt; on every write&lt;/strong&gt; — safe preview before executing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No interactive prompts&lt;/strong&gt; — nothing blocks on stdin.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/tamtom/gplay-cli-skills&quot;&gt;16 ready-made Agent Skills&lt;/a&gt;&lt;/strong&gt; — install with &lt;code dir=&quot;auto&quot;&gt;npx skills add tamtom/gplay-cli-skills&lt;/code&gt; and your agent knows the real Google Play workflows.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;GPP’s output is Gradle task output. An agent trying to drive it has to parse human-facing logs. It works, but it’s fragile.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;try-gplay-in-one-command&quot;&gt;Try gplay in one command&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tamtom/tap/gplay&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--auto&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;# installs gcloud, enables the API, creates a service account&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apps&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;list&lt;/span&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;# you&apos;re already in&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Full install options (Homebrew, install script, Windows PowerShell, manual binaries) on the &lt;a href=&quot;https://playconsolecli.com/guides/installation/&quot;&gt;installation page&lt;/a&gt;. Full command reference at &lt;a href=&quot;https://playconsolecli.com/reference/&quot;&gt;/reference/&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you’re on GPP today and want to see what “the rest of Play Console” looks like, start with these three: &lt;a href=&quot;https://playconsolecli.com/reference/vitals/&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;gplay vitals crashes query&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://playconsolecli.com/reference/subscriptions/&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;gplay subscriptions list&lt;/code&gt;&lt;/a&gt;, and &lt;a href=&quot;https://playconsolecli.com/reference/reviews/&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;gplay reviews list&lt;/code&gt;&lt;/a&gt;. Chances are you’ll find a workflow you were doing in the web UI that suddenly belongs in CI.&lt;/p&gt;
</content:encoded><category>comparison</category><category>gradle-play-publisher</category></item><item><title>How to release an Android app to Google Play from the terminal</title><link>https://playconsolecli.com/blog/release-android-app-from-terminal/</link><guid isPermaLink="true">https://playconsolecli.com/blog/release-android-app-from-terminal/</guid><description>Skip the Play Console web UI entirely — upload an AAB, assign it to a track, add release notes, and run a staged rollout with a single CLI command.</description><pubDate>Sun, 05 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Publishing to Google Play traditionally means clicking through the Play Console web UI: create a release, drag in an AAB, paste release notes, pick a rollout percentage, review, confirm. It works — but it’s slow, unscriptable, and impossible to automate.&lt;/p&gt;
&lt;p&gt;Under the hood, though, every one of those clicks maps to the &lt;a href=&quot;https://developers.google.com/android-publisher&quot;&gt;Google Play Developer API&lt;/a&gt;’s edit-session model. &lt;strong&gt;gplay&lt;/strong&gt; wraps that whole model into commands you can run from your terminal or CI. Here’s the complete flow.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;div&gt;&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://playconsolecli.com/guides/installation/&quot;&gt;Install gplay&lt;/a&gt; — one static binary, via Homebrew or install script&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://playconsolecli.com/guides/authentication/&quot;&gt;Authenticate&lt;/a&gt; — &lt;code dir=&quot;auto&quot;&gt;gplay setup --auto&lt;/code&gt; does the whole service-account dance for you&lt;/li&gt;
&lt;li&gt;A built AAB (see your Gradle setup or CI pipeline)&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;the-one-command-release&quot;&gt;The one-command release&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;release&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--track&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;internal&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--bundle&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;app.aab&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;That single command creates an edit session, uploads the bundle, assigns the version to the internal track, and commits the edit. If any step fails, nothing is applied — edits are atomic.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;production-with-release-notes-and-a-staged-rollout&quot;&gt;Production, with release notes and a staged rollout&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;release&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--track&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;production&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--bundle&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;app.aab&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--release-notes&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@notes.json&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--rollout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0.1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Two things worth knowing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;--rollout&lt;/code&gt; is a &lt;strong&gt;fraction between 0.0 and 1.0&lt;/strong&gt; — &lt;code dir=&quot;auto&quot;&gt;0.1&lt;/code&gt; means 10% of users. (Not &lt;code dir=&quot;auto&quot;&gt;10&lt;/code&gt;!)&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;--release-notes&lt;/code&gt; takes a JSON file with per-locale notes, or plain text that’s auto-assigned to &lt;code dir=&quot;auto&quot;&gt;en-US&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An example &lt;code dir=&quot;auto&quot;&gt;notes.json&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;&quot;language&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;en-US&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;text&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Bug fixes and performance improvements.&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;&quot;language&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;de-DE&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;text&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Fehlerbehebungen und Leistungsverbesserungen.&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;You can even generate the notes from your git history:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;release-notes&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;generate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;watch-and-expand-the-rollout&quot;&gt;Watch and expand the rollout&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Where is the rollout right now?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rollout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--track&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;production&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Expand 10% → 50%&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rollout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--track&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;production&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--rollout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Crash spike? Halt immediately.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rollout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;halt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--track&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;production&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# All good — ship to everyone&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rollout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;complete&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--track&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;production&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Pair this with &lt;code dir=&quot;auto&quot;&gt;gplay vitals crashes&lt;/code&gt; to gate each expansion on real crash data.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;promote-instead-of-re-uploading&quot;&gt;Promote instead of re-uploading&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Once a build has soaked on internal or beta, don’t upload it again — promote the exact same artifact:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;promote&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;beta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--to&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;production&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--rollout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0.1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;try-everything-safely-first&quot;&gt;Try everything safely first&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Every write command supports &lt;code dir=&quot;auto&quot;&gt;--dry-run&lt;/code&gt;, which logs the HTTP requests it would make without executing them:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--dry-run&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;release&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--track&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;production&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--bundle&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;app.aab&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This is also what makes gplay safe to hand to an AI agent or a new CI pipeline — you can see exactly what would happen before it does.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-same-flow-in-ci&quot;&gt;The same flow in CI&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# GitHub Actions&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Release to internal&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;GPLAY_SERVICE_ACCOUNT&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.PLAY_SA_JSON_PATH }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;GPLAY_PACKAGE&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;gplay release --track internal --bundle app/build/outputs/bundle/release/app-release.aab&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;No Ruby, no Gradle plugin configuration — the same binary and the same command you ran locally.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;next-steps&quot;&gt;Next steps&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://playconsolecli.com/guides/quickstart/&quot;&gt;Quickstart guide&lt;/a&gt; — the 5-minute tour&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://playconsolecli.com/reference/&quot;&gt;Command reference&lt;/a&gt; — every command and flag&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://playconsolecli.com/guides/ai-agents/&quot;&gt;Use gplay with AI agents&lt;/a&gt; — let your agent run the release&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>releases</category><category>tutorial</category></item><item><title>Migrate from Fastlane supply to gplay in an afternoon</title><link>https://playconsolecli.com/blog/migrate-from-fastlane-supply-to-gplay/</link><guid isPermaLink="true">https://playconsolecli.com/blog/migrate-from-fastlane-supply-to-gplay/</guid><description>You have Fastlane metadata in fastlane/metadata/android and a supply lane that ships builds. Here&apos;s how to migrate to gplay without losing your listings, screenshots, or muscle memory — in about an afternoon.</description><pubDate>Sun, 05 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If you’ve been shipping Android with Fastlane for years, you have a directory tree that looks something like:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fastlane/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;├── Fastfile&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;└── metadata/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└── android/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├── en-US/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;│   ├── title.txt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;│   ├── short_description.txt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;│   ├── full_description.txt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;│   ├── images/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;│   │   ├── icon.png&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;│   │   ├── featureGraphic.png&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;│   │   └── phoneScreenshots/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;│   └── changelogs/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;│       └── default.txt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└── fr-FR/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└── ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Good news: &lt;a href=&quot;https://playconsolecli.com/&quot;&gt;gplay&lt;/a&gt; reads this format directly. You don’t have to rewrite your metadata, re-download screenshots, or manually re-enter listings. This post walks through the full migration in an afternoon.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;div&gt;&lt;h2 id=&quot;step-1--install-gplay&quot;&gt;Step 1 — install gplay&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tamtom/tap/gplay&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--auto&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apps&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;list&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;setup --auto&lt;/code&gt; reuses your existing service account if you already have one, or creates a new one. &lt;code dir=&quot;auto&quot;&gt;apps list&lt;/code&gt; confirms the connection.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;step-2--dry-run-your-existing-metadata&quot;&gt;Step 2 — dry-run your existing metadata&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;gplay migrate fastlane&lt;/code&gt; reads &lt;code dir=&quot;auto&quot;&gt;fastlane/metadata/android/&lt;/code&gt; and shows you exactly what will be uploaded, without touching anything:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;migrate&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fastlane&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--metadata-dir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fastlane/metadata/android&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--dry-run&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Output (formatted for readability):&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Locale: en-US&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;title: &quot;MyApp&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;short_description: 80 chars&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;full_description: 3120 chars&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;screenshots (phone): 6 images&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;featureGraphic: 1024x500&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;changelog: 240 chars&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Locale: fr-FR&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Total: 12 locales, 68 screenshots, 12 changelogs&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Total upload size: 41 MB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Read the diff. If anything looks wrong (missing locale, oversized screenshot, empty changelog), fix the source files first. gplay does not truncate — it fails loudly on Google’s validation.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;step-3--run-the-migration&quot;&gt;Step 3 — run the migration&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Drop the &lt;code dir=&quot;auto&quot;&gt;--dry-run&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;migrate&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fastlane&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--metadata-dir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fastlane/metadata/android&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This creates an edit session, uploads every listing, screenshot, and changelog, then commits atomically. If anything fails mid-way, the edit is discarded — Play Console goes back to exactly its previous state.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;step-4--replace-your-supply-lane&quot;&gt;Step 4 — replace your supply lane&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Your Fastfile probably has something like:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;lane &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;deploy&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;gradle(&lt;/span&gt;&lt;span&gt;task&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bundleRelease&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;supply(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;track&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;internal&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;aab&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;app/build/outputs/bundle/release/app-release.aab&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;metadata_path&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;./fastlane/metadata/android&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;skip_upload_apk&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The gplay equivalent:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;./gradlew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bundleRelease&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;release&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--track&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;internal&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--bundle&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;app/build/outputs/bundle/release/app-release.aab&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--listings-dir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fastlane/metadata/android&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;--listings-dir&lt;/code&gt; reuses your existing Fastlane metadata directory on every release. No migration needed for future runs — the source of truth stays where it is.&lt;/p&gt;
&lt;p&gt;Or point it at a different directory if you want to move it out of the &lt;code dir=&quot;auto&quot;&gt;fastlane/&lt;/code&gt; folder later:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;release&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--track&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;internal&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--bundle&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;app-release.aab&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--listings-dir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;play-metadata&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;step-5--port-your-other-supply-lanes&quot;&gt;Step 5 — port your other supply lanes&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Every Fastlane action has a gplay equivalent. The ones you probably use most:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fastlane&lt;/th&gt;
&lt;th&gt;gplay&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;supply(track: &quot;internal&quot;, aab: &quot;...&quot;)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;gplay release --track internal --bundle ...&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;supply(track: &quot;production&quot;, rollout: &quot;0.1&quot;)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;gplay tracks promote --to production --rollout 0.1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;supply(skip_upload_screenshots: false, ...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;gplay release --listings-dir ...&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;download_screenshots(...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;gplay listings pull --output-dir ...&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;upload_to_play_store_internal_app_sharing&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;gplay internal-app-sharing upload --bundle ...&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manual: change track from internal to alpha&lt;/td&gt;
&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;gplay tracks promote --from internal --to alpha&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manual: check listing status&lt;/td&gt;
&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;gplay listings get --package ... --locale en-US&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div&gt;&lt;h2 id=&quot;step-6--bonus-do-things-you-couldnt-before&quot;&gt;Step 6 — bonus, do things you couldn’t before&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Once you’ve migrated, you have 250+ commands available that Fastlane supply doesn’t cover. A few worth trying immediately:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Check crashes from CI:&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vitals&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;crashes&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;query&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--time-range&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LAST_7_DAYS&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Reply to reviews:&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;reviews&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;list&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--filter&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;rating&amp;#x3C;=2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;reviews&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;reply&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--review-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;id&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--message&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Thanks for the feedback...&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Manage subscriptions:&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subscriptions&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;list&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;baseplans&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;list&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--subscription-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;monthly_pro&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Download this month’s earnings:&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;reports&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;financial&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;list&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;reports&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;financial&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;download&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--report-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;earnings_202607&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Fastlane can’t do any of this. It’s not a bug — supply was designed for a narrower job. gplay wasn’t.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;what-you-can-leave-in-place&quot;&gt;What you can leave in place&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;You don’t have to rip Fastlane out. Common hybrid:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keep Fastlane for the parts of iOS release you already have working.&lt;/li&gt;
&lt;li&gt;Keep &lt;code dir=&quot;auto&quot;&gt;fastlane/metadata/android/&lt;/code&gt; as the source of truth for listings (gplay reads it natively).&lt;/li&gt;
&lt;li&gt;Use gplay for Android release, staged rollout, monitoring, subscriptions, purchase verification, and everything post-publish.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;rollback-plan&quot;&gt;Rollback plan&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;If you decide the migration didn’t work out (it will, but hypothetically):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Your Fastlane metadata directory is unchanged. gplay reads it; it doesn’t rewrite it.&lt;/li&gt;
&lt;li&gt;Your service account and Play Console access are unchanged. Fastlane still authenticates fine.&lt;/li&gt;
&lt;li&gt;Your CI can call both. Nothing is exclusive.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Reverting is &lt;code dir=&quot;auto&quot;&gt;git revert&lt;/code&gt; on the workflow change. Zero data loss.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;getting-started&quot;&gt;Getting started&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tamtom/tap/gplay&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--auto&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;migrate&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fastlane&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--metadata-dir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fastlane/metadata/android&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--dry-run&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Full migration reference at &lt;a href=&quot;https://playconsolecli.com/reference/migrate/&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;/reference/migrate/&lt;/code&gt;&lt;/a&gt;. If you want to fully automate the migration and CI rewrite in one shot, install the &lt;a href=&quot;https://github.com/tamtom/gplay-cli-skills&quot;&gt;Fastlane migration skill&lt;/a&gt; and ask your AI agent to walk your repo end-to-end.&lt;/p&gt;
</content:encoded><category>migration</category><category>fastlane</category><category>tutorial</category></item><item><title>Monitor Google Play crashes and ANRs from the terminal (or CI)</title><link>https://playconsolecli.com/blog/monitor-google-play-crashes-anrs-from-terminal/</link><guid isPermaLink="true">https://playconsolecli.com/blog/monitor-google-play-crashes-anrs-from-terminal/</guid><description>The Play Console vitals dashboards are useful — but you can&apos;t grep them, alert on them, or fail a CI job on them. gplay exposes the same data as JSON from your shell. Here&apos;s how to build a real crash and ANR watchdog in ten lines.</description><pubDate>Sun, 05 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Play Console’s vitals dashboards are fine for eyeballing trends. They’re less good when you want to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fail a CI job if the new release’s crash rate is 30% above baseline.&lt;/li&gt;
&lt;li&gt;Post a Slack alert when a new crash cluster affects more than 500 users.&lt;/li&gt;
&lt;li&gt;Diff this week’s ANRs against last week’s before promoting to production.&lt;/li&gt;
&lt;li&gt;Have your AI agent triage the top 10 clusters and file GitHub issues.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;a href=&quot;https://developers.google.com/play/developer/reporting&quot;&gt;Google Play Developer Reporting API&lt;/a&gt; exposes all of that — clusters, sample stack traces, user counts, error rates, per-device breakdowns. &lt;a href=&quot;https://playconsolecli.com/&quot;&gt;gplay&lt;/a&gt; wraps it as CLI commands.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;div&gt;&lt;h2 id=&quot;the-commands&quot;&gt;The commands&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Crash clusters, last 7 days, top 20 by user impact&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vitals&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;crashes&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;query&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--time-range&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LAST_7_DAYS&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--page-size&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--paginate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Full crash report for a specific cluster&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vitals&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;crashes&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;report&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--cluster-name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;crashes/abc123&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# ANR clusters&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vitals&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;errors&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;query&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--issue-types&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;APPLICATION_NOT_RESPONDING&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--time-range&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LAST_7_DAYS&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Performance metrics (startup, rendering, battery)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vitals&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;performance&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;startup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--time-range&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LAST_7_DAYS&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Every command returns minified JSON by default. Add &lt;code dir=&quot;auto&quot;&gt;--output table&lt;/code&gt; if you’re eyeballing in a shell.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;a-ten-line-ci-regression-gate&quot;&gt;A ten-line CI regression gate&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Drop this into a GitHub Actions job that runs an hour after promoting to production:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Fail on crash regression&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;GPLAY_SERVICE_ACCOUNT&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.PLAY_SA_JSON_PATH }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;GPLAY_PACKAGE&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;THIS_WEEK=$(gplay vitals crashes query --time-range LAST_7_DAYS \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;| jq &apos;[.clusters[].distinctUsers // 0] | add&apos;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;LAST_WEEK=$(gplay vitals crashes query --time-range PREVIOUS_7_DAYS \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;| jq &apos;[.clusters[].distinctUsers // 0] | add&apos;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if [ &quot;$THIS_WEEK&quot; -gt $(( LAST_WEEK * 130 / 100 )) ]; then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;Crash-affected users up &gt;30% week over week ($LAST_WEEK -&gt; $THIS_WEEK)&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;exit 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;That’s it. No third-party observability integration, no Play Console screen-scraping, no waiting for a human to notice.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;slack-alerts-for-new-crash-clusters&quot;&gt;Slack alerts for new crash clusters&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Combine &lt;code dir=&quot;auto&quot;&gt;vitals crashes query&lt;/code&gt; with &lt;code dir=&quot;auto&quot;&gt;gplay notify send&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;#!/usr/bin/env bash&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# alert-new-crashes.sh — run hourly from cron&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-euo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pipefail&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;NEW_CLUSTERS&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vitals&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;crashes&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;query&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--time-range&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LAST_24_HOURS&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--output&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;json&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;jq&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-c&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;[.clusters[] | select(.distinctUsers &gt; 500)]&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;COUNT&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$NEW_CLUSTERS&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;jq&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$COUNT&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-eq&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;exit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;MSG&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$NEW_CLUSTERS&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;jq&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-r&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;map(&quot;- \(.name): \(.distinctUsers) users&quot;) | join(&quot;\n&quot;)&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;notify&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;send&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--webhook&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$SLACK_WEBHOOK&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--message&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;🚨 &lt;/span&gt;&lt;span&gt;$COUNT&lt;/span&gt;&lt;span&gt; new high-impact crash clusters in the last 24h:\n&lt;/span&gt;&lt;span&gt;$MSG&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Fastlane and gradle-play-publisher have nothing here. This is entirely Play Console territory, and it’s the kind of workflow that never gets built when it has to live in a web UI.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;ai-agent-triage&quot;&gt;AI agent triage&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Give your AI agent (Claude Code, Cursor, OpenClaw, Hermes Agent, or any of the &lt;a href=&quot;https://playconsolecli.com/guides/ai-agents/&quot;&gt;12 supported agents&lt;/a&gt;) this prompt:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Grab the top 10 crash clusters from the last week for &lt;code dir=&quot;auto&quot;&gt;com.example.app&lt;/code&gt;. For any cluster over 500 users where the top stack frame is in our own code (not Android framework), pull the full report and file a GitHub issue with the stack trace, user count, and affected devices. Skip clusters that already have an open issue.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Claude Code will chain &lt;code dir=&quot;auto&quot;&gt;gplay vitals crashes query&lt;/code&gt; → &lt;code dir=&quot;auto&quot;&gt;gplay vitals crashes report&lt;/code&gt; → &lt;code dir=&quot;auto&quot;&gt;gh issue list&lt;/code&gt; → &lt;code dir=&quot;auto&quot;&gt;gh issue create&lt;/code&gt;. Read the output, approve the writes, done.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;time-ranges-you-can-pass&quot;&gt;Time ranges you can pass&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;gplay vitals&lt;/code&gt; commands accept the same time-range values as the Reporting API:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;LAST_24_HOURS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;LAST_7_DAYS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;LAST_30_DAYS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;PREVIOUS_7_DAYS&lt;/code&gt; (week-over-week comparisons)&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;PREVIOUS_30_DAYS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Custom ranges via &lt;code dir=&quot;auto&quot;&gt;--start-time&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;--end-time&lt;/code&gt; (RFC 3339 timestamps).&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;filtering&quot;&gt;Filtering&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Every &lt;code dir=&quot;auto&quot;&gt;vitals&lt;/code&gt; query accepts filter flags for OS version, device model, app version, and country:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vitals&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;crashes&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;query&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--time-range&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LAST_7_DAYS&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--os-versions&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;34,33&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--app-versions&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;4.2.1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--paginate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;That’s how you isolate a regression to a specific Android version or hardware model, from the terminal, in seconds.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;performance-vitals&quot;&gt;Performance vitals&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Beyond crashes and ANRs, &lt;code dir=&quot;auto&quot;&gt;gplay vitals performance&lt;/code&gt; covers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Startup latency&lt;/strong&gt; — cold and warm start, per device class&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Slow rendering&lt;/strong&gt; — frozen frames, 16 ms budget breaches&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Battery drain&lt;/strong&gt; — background power usage, wakelocks&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Excessive wakeups&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Same JSON-first output, same filter surface, same CI-friendly ergonomics.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;getting-started&quot;&gt;Getting started&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tamtom/tap/gplay&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--auto&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vitals&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;crashes&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;query&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--time-range&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LAST_7_DAYS&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--output&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;table&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Full vitals reference at &lt;a href=&quot;https://playconsolecli.com/reference/vitals/&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;/reference/vitals/&lt;/code&gt;&lt;/a&gt;. If you want a ready-made Slack alert setup, install the &lt;a href=&quot;https://github.com/tamtom/gplay-cli-skills&quot;&gt;vitals monitoring skill&lt;/a&gt; and ask your AI agent to scaffold it against your webhook.&lt;/p&gt;
</content:encoded><category>vitals</category><category>crashes</category><category>ci</category></item><item><title>Set up a Google Play subscription with RevenueCat and gplay CLI</title><link>https://playconsolecli.com/blog/revenuecat-google-play-setup-with-gplay/</link><guid isPermaLink="true">https://playconsolecli.com/blog/revenuecat-google-play-setup-with-gplay/</guid><description>RevenueCat handles the entitlement, offering, and paywall abstraction. gplay handles the actual Google Play subscription, base plan, offers, and per-country pricing. Together they cover the whole subscription stack — from the terminal.</description><pubDate>Sun, 05 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If you’ve set up a Google Play subscription with RevenueCat before, you know the split: RevenueCat manages the cross-store abstraction (entitlements, offerings, paywalls, receipt verification), and you do the Play Console side by hand in a web UI — create the subscription product, create a base plan, create offers, click through 175 country prices, localize the offer names.&lt;/p&gt;
&lt;p&gt;The Play Console side of that story is what &lt;a href=&quot;https://playconsolecli.com/&quot;&gt;gplay&lt;/a&gt; automates. Paired with the &lt;a href=&quot;https://www.revenuecat.com/docs/tools/rc-mcp&quot;&gt;RevenueCat MCP server&lt;/a&gt;, the entire stack now lives in your terminal (or your AI agent’s).&lt;/p&gt;
&lt;!-- more --&gt;
&lt;div&gt;&lt;h2 id=&quot;what-each-tool-owns&quot;&gt;What each tool owns&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Cleanest way to think about it:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Owns&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Google Play native&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;gplay CLI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Subscription, base plans, offers, PPP-converted country prices, offer localization, purchase verification against Google&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-store abstraction&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;RevenueCat MCP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Entitlements, offerings, packages, paywalls, customer view, revenue analytics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Your app SDK&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;RevenueCat SDK&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Present offerings, hand off to Play Billing, sync customer info&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;gplay talks to Google directly. RevenueCat talks to gplay’s output (the Play product IDs). Your app talks to RevenueCat.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-end-to-end-flow&quot;&gt;The end-to-end flow&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Let’s launch a new “Pro” subscription with monthly and yearly base plans, a 7-day free trial offer on monthly, and prices in USD, EUR, GBP, JPY, INR, BRL.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;1-create-the-google-play-subscription-with-gplay&quot;&gt;1. Create the Google Play subscription with gplay&lt;/h3&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subscriptions&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--product-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pro&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--listing&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;en-US:title=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Pro&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,description=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Unlimited access to Pro features&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;2-add-base-plans&quot;&gt;2. Add base plans&lt;/h3&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;baseplans&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--subscription-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pro&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--base-plan-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;monthly&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--billing-period&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;P1M&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--auto-renewing&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;baseplans&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--subscription-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pro&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--base-plan-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;yearly&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--billing-period&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;P1Y&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--auto-renewing&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;3-set-prices-in-the-anchor-country--expand-with-ppp&quot;&gt;3. Set prices in the anchor country + expand with PPP&lt;/h3&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Set the anchor price (US, monthly)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;baseplans&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prices&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--subscription-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pro&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--base-plan-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;monthly&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--region&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;US&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--price-micros&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;9990000&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# $9.99&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Expand to 175 countries using Google&apos;s purchasing-power-parity conversion&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;baseplans&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prices&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;convert&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--subscription-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pro&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--base-plan-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;monthly&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--from-region&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;US&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Same two commands for yearly at &lt;code dir=&quot;auto&quot;&gt;$99.99&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;convert&lt;/code&gt; uses Google’s PPP tables — a $9.99 US price becomes ₹499 in India, R$19.90 in Brazil, ¥1500 in Japan, etc. You can override individual countries after.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;4-add-a-7-day-free-trial-offer-on-monthly&quot;&gt;4. Add a 7-day free-trial offer on monthly&lt;/h3&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;offers&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--subscription-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pro&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--base-plan-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;monthly&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--offer-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;monthly-trial-7d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--phases&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FREE_TRIAL,P7D,0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;5-activate-and-localize&quot;&gt;5. Activate and localize&lt;/h3&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subscriptions&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;activate&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--product-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pro&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Localize offer names for major markets&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;offers&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;locales&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--subscription-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pro&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--base-plan-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;monthly&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--offer-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;monthly-trial-7d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--locales&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;en-US:&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;7-day free trial&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,fr-FR:&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;7 jours gratuits&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,de-DE:&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;7 Tage kostenlos&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,es-ES:&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;7 días gratis&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,ja-JP:&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;7日間無料&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;That’s the Google Play side, done. Take a screenshot of the Play Console page as proof — the subscription, both base plans, the trial offer, and localized names are all there.&lt;/p&gt;
&lt;p&gt;Now wire it up in RevenueCat.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;6-register-the-products-in-revenuecat&quot;&gt;6. Register the products in RevenueCat&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Prompt your AI agent (Claude Code, Cursor, or any of the &lt;a href=&quot;https://playconsolecli.com/guides/ai-agents/&quot;&gt;12 supported agents&lt;/a&gt;):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Using the RevenueCat MCP, register the Google Play products for &lt;code dir=&quot;auto&quot;&gt;com.example.app&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Product &lt;code dir=&quot;auto&quot;&gt;pro:monthly&lt;/code&gt; (subscription &lt;code dir=&quot;auto&quot;&gt;pro&lt;/code&gt;, base plan &lt;code dir=&quot;auto&quot;&gt;monthly&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Product &lt;code dir=&quot;auto&quot;&gt;pro:yearly&lt;/code&gt; (subscription &lt;code dir=&quot;auto&quot;&gt;pro&lt;/code&gt;, base plan &lt;code dir=&quot;auto&quot;&gt;yearly&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then create a “Pro” entitlement and attach both products to it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Under the hood the agent will call the RevenueCat MCP tools: &lt;code dir=&quot;auto&quot;&gt;create-product-in-store&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;create-entitlement&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;attach-products-to-entitlement&lt;/code&gt;. All you did was describe the intent.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;7-create-the-offering-and-packages&quot;&gt;7. Create the offering and packages&lt;/h3&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Create an offering called “default” with two packages: “$rc_monthly” mapped to &lt;code dir=&quot;auto&quot;&gt;pro:monthly&lt;/code&gt; and “$rc_annual” mapped to &lt;code dir=&quot;auto&quot;&gt;pro:yearly&lt;/code&gt;. Attach both to the “Pro” entitlement.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The agent calls &lt;code dir=&quot;auto&quot;&gt;create-offering&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;create-packages&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;attach-products-to-package&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Your app’s &lt;code dir=&quot;auto&quot;&gt;Purchases.getOfferings()&lt;/code&gt; will now return this shape.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;8-generate-a-paywall&quot;&gt;8. Generate a paywall&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;RevenueCat’s MCP includes an AI paywall generator:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Generate a paywall for the “default” offering with a “Try Pro free for 7 days” hero, feature list of “Unlimited exports, No ads, Priority support”, and a testimonial from an existing paywall on file. Render a screenshot when done.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The MCP calls &lt;code dir=&quot;auto&quot;&gt;create-paywall-ai&lt;/code&gt; → returns a task ID → agent polls &lt;code dir=&quot;auto&quot;&gt;get-paywall-ai-task&lt;/code&gt; → then &lt;code dir=&quot;auto&quot;&gt;render-paywall-screenshot&lt;/code&gt; when ready. You get back a PNG.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;9-verify-a-test-purchase-both-sides&quot;&gt;9. Verify a test purchase (both sides)&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;After a tester runs a sandbox purchase in your app:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Play-side verification&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;purchases&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subscriptionsv2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--token&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$PURCHASE_TOKEN&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;And in RevenueCat via the MCP:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Look up the RevenueCat customer for user ID &lt;code dir=&quot;auto&quot;&gt;test-user-42&lt;/code&gt; and show me their subscriptions and entitlements.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The Play &lt;code dir=&quot;auto&quot;&gt;subscriptionState&lt;/code&gt; and the RC entitlement should match. If they don’t, gplay tells you exactly what Google’s servers see; RC tells you what its webhook processed. That’s how you triage sync issues.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;why-this-split-makes-sense&quot;&gt;Why this split makes sense&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Three things you get from doing it this way:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;One source of truth per layer.&lt;/strong&gt; The Play product IDs live in Play Console (managed by gplay). The RC entitlement/offering/paywall live in RevenueCat (managed by MCP). No copy-paste between dashboards.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI-agent-driven.&lt;/strong&gt; Both surfaces are agent-native. A prompt like “add a yearly plan with 30% discount for existing monthly users” becomes: gplay creates the base plan, RC MCP creates the targeting rule, done.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cross-store when you’re ready.&lt;/strong&gt; Everything above works for App Store Connect too — same RC MCP calls, use the &lt;a href=&quot;https://github.com/rudrankriyam/asc&quot;&gt;asc CLI&lt;/a&gt; for the App Store side.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;getting-started&quot;&gt;Getting started&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tamtom/tap/gplay&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--auto&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Install the RevenueCat MCP in your AI agent per &lt;a href=&quot;https://www.revenuecat.com/docs/tools/rc-mcp&quot;&gt;RevenueCat’s setup docs&lt;/a&gt;, and install the &lt;a href=&quot;https://github.com/tamtom/gplay-cli-skills&quot;&gt;subscription skill for gplay&lt;/a&gt; so your agent knows the full Play-side workflow:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;skills&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tamtom/gplay-cli-skills&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Full subscription reference at &lt;a href=&quot;https://playconsolecli.com/reference/subscriptions/&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;/reference/subscriptions/&lt;/code&gt;&lt;/a&gt;, base plans at &lt;a href=&quot;https://playconsolecli.com/reference/baseplans/&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;/reference/baseplans/&lt;/code&gt;&lt;/a&gt;, offers at &lt;a href=&quot;https://playconsolecli.com/reference/offers/&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;/reference/offers/&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>revenuecat</category><category>subscriptions</category><category>tutorial</category></item><item><title>Staged rollouts on Google Play with GitHub Actions</title><link>https://playconsolecli.com/blog/google-play-staged-rollouts-github-actions/</link><guid isPermaLink="true">https://playconsolecli.com/blog/google-play-staged-rollouts-github-actions/</guid><description>A complete GitHub Actions workflow that uploads an AAB, promotes it through internal → alpha → beta → production with fractional rollout, monitors crashes between stages, and rolls back automatically if vitals regress.</description><pubDate>Sun, 05 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Staged rollouts are the safest way to ship an Android release: start at 1% of production, watch the crash rate, ramp to 10%, 20%, 50%, 100%. Doing it manually in the Play Console is tedious. Doing it in CI, gated on real vitals, is where you actually want to live.&lt;/p&gt;
&lt;p&gt;This post walks through a complete GitHub Actions workflow using &lt;a href=&quot;https://playconsolecli.com/&quot;&gt;gplay&lt;/a&gt; that:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Uploads a signed AAB to the internal track.&lt;/li&gt;
&lt;li&gt;Promotes to production at 1% rollout after a soak period.&lt;/li&gt;
&lt;li&gt;Ramps to 20% if crashes stay clean.&lt;/li&gt;
&lt;li&gt;Ramps to 100% if week-over-week crashes are within tolerance.&lt;/li&gt;
&lt;li&gt;Halts the rollout automatically if crashes regress.&lt;/li&gt;
&lt;/ol&gt;
&lt;!-- more --&gt;
&lt;div&gt;&lt;h2 id=&quot;the-workflow&quot;&gt;The workflow&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Save this as &lt;code dir=&quot;auto&quot;&gt;.github/workflows/release.yml&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Release to Google Play&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;on&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;v*&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;workflow_dispatch&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;GPLAY_PACKAGE&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;GPLAY_NO_UPDATE&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;jobs&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;release&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;runs-on&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;ubuntu-latest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;steps&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;actions/checkout@v4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Install gplay&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;curl -sSL https://raw.githubusercontent.com/tamtom/play-console-cli/main/install.sh | bash&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;$HOME/.gplay/bin&quot; &gt;&gt; $GITHUB_PATH&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Write service-account key&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;PLAY_SA_JSON&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.PLAY_SA_JSON }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;$PLAY_SA_JSON&quot; &gt; $RUNNER_TEMP/play-sa.json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;GPLAY_SERVICE_ACCOUNT=$RUNNER_TEMP/play-sa.json&quot; &gt;&gt; $GITHUB_ENV&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Build AAB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;./gradlew bundleRelease&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Upload to internal track&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gplay release \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--track internal \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--bundle app/build/outputs/bundle/release/app-release.aab \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--release-notes &quot;en-US=$(git log -1 --pretty=%B)&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;soak&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;needs&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;release&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;runs-on&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;ubuntu-latest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;steps&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Wait 24 hours in internal&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;sleep 86400&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Check internal crashes&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;GPLAY_SERVICE_ACCOUNT&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.PLAY_SA_JSON_PATH }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CRASH_USERS=$(gplay vitals crashes query \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--time-range LAST_24_HOURS \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;| jq &apos;[.clusters[].distinctUsers // 0] | add&apos;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if [ &quot;${CRASH_USERS:-0}&quot; -gt 50 ]; then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;Internal soak failed: $CRASH_USERS crash-affected users&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;exit 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;promote-1pct&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;needs&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;soak&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;runs-on&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;ubuntu-latest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;steps&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Promote to production at 1%&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;GPLAY_SERVICE_ACCOUNT&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.PLAY_SA_JSON_PATH }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gplay tracks promote \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--from internal \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--to production \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--rollout 0.01&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Notify Slack&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gplay notify send \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--webhook &quot;${{ secrets.SLACK_WEBHOOK }}&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--message &quot;🟢 Production rollout started at 1% for ${{ github.ref_name }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;ramp-20pct&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;needs&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;promote-1pct&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;runs-on&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;ubuntu-latest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;steps&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Soak at 1% for 24h&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;sleep 86400&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Gate on crash regression&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;GPLAY_SERVICE_ACCOUNT&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.PLAY_SA_JSON_PATH }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;THIS=$(gplay vitals crashes query --time-range LAST_24_HOURS \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;| jq &apos;[.clusters[].distinctUsers // 0] | add&apos;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;BASELINE=$(gplay vitals crashes query --time-range PREVIOUS_7_DAYS \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;| jq &apos;[.clusters[].distinctUsers // 0] | add / 7&apos;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if [ &quot;${THIS:-0}&quot; -gt $(( ${BASELINE:-0} * 130 / 100 )) ]; then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gplay tracks halt-rollout --track production&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gplay notify send --webhook &quot;${{ secrets.SLACK_WEBHOOK }}&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--message &quot;🚨 Rollout halted: crashes up &gt;30% vs 7-day baseline&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;exit 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Ramp to 20%&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;GPLAY_SERVICE_ACCOUNT&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.PLAY_SA_JSON_PATH }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gplay tracks update-rollout --track production --rollout 0.20&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;ramp-100pct&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;needs&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;ramp-20pct&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;runs-on&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;ubuntu-latest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;steps&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Soak at 20% for 48h&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;sleep 172800&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Full rollout&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;GPLAY_SERVICE_ACCOUNT&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.PLAY_SA_JSON_PATH }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gplay tracks update-rollout --track production --rollout 1.0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gplay notify send --webhook &quot;${{ secrets.SLACK_WEBHOOK }}&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--message &quot;🎉 100% rolled out for ${{ github.ref_name }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;whats-happening-at-each-stage&quot;&gt;What’s happening at each stage&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;&lt;code dir=&quot;auto&quot;&gt;release&lt;/code&gt;&lt;/strong&gt; — builds the AAB with Gradle and uploads to the &lt;code dir=&quot;auto&quot;&gt;internal&lt;/code&gt; track. Because we don’t set &lt;code dir=&quot;auto&quot;&gt;--rollout&lt;/code&gt;, internal ships at 100% (the standard for tester tracks).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code dir=&quot;auto&quot;&gt;soak&lt;/code&gt;&lt;/strong&gt; — 24-hour hold at internal. Fails the workflow if crash-affected users exceed 50 (a hard number appropriate for a small internal audience; tune to your scale).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code dir=&quot;auto&quot;&gt;promote-1pct&lt;/code&gt;&lt;/strong&gt; — &lt;a href=&quot;https://playconsolecli.com/reference/tracks/&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;gplay tracks promote&lt;/code&gt;&lt;/a&gt; copies the internal release to production at 1% rollout. Note &lt;code dir=&quot;auto&quot;&gt;--rollout&lt;/code&gt; takes a &lt;strong&gt;fraction&lt;/strong&gt; (0.01 = 1%), not a percentage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code dir=&quot;auto&quot;&gt;ramp-20pct&lt;/code&gt;&lt;/strong&gt; — 24-hour soak at 1%, then compare this-week crash-affected users against the previous 7-day average. If we’re 30% or more above baseline, &lt;code dir=&quot;auto&quot;&gt;gplay tracks halt-rollout&lt;/code&gt; freezes production and Slack gets pinged. Otherwise ramp to 20%.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code dir=&quot;auto&quot;&gt;ramp-100pct&lt;/code&gt;&lt;/strong&gt; — 48-hour soak at 20%, then push to 100%.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;manual-rollback&quot;&gt;Manual rollback&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;If something looks off between automated stages:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Halt the current rollout (holds at current fraction)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tracks&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;halt-rollout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--track&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;production&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Resume when ready&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tracks&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;resume-rollout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--track&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;production&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Full rollback: promote the previous production build back in&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tracks&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;promote&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;production&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--to&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;production&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--from-version-code&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;4210&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--rollout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1.0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;adding-release-notes&quot;&gt;Adding release notes&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The workflow above uses the latest commit message as the &lt;code dir=&quot;auto&quot;&gt;en-US&lt;/code&gt; release note. For multi-locale releases:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;release&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--track&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;production&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--bundle&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;app-release.aab&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--release-notes-file&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;release-notes.yaml&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Where &lt;code dir=&quot;auto&quot;&gt;release-notes.yaml&lt;/code&gt; is:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;en-US&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Bug fixes and improvements&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fr-FR&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Corrections de bugs et améliorations&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;de-DE&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Fehlerbehebungen und Verbesserungen&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;es-ES&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Correcciones de errores y mejoras&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ja-JP&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;バグの修正と改善&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Or let your AI agent generate them from git history:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;release-notes&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;generate&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--since&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;v1.4.0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--output&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;release-notes.yaml&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;storing-the-service-account-safely&quot;&gt;Storing the service account safely&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;PLAY_SA_JSON&lt;/code&gt; secret should contain the full contents of your service-account key file. If your org policy forbids storing JSON as a secret, use OIDC to fetch it from Google Secret Manager at job start — gplay reads whatever path &lt;code dir=&quot;auto&quot;&gt;GPLAY_SERVICE_ACCOUNT&lt;/code&gt; points to.&lt;/p&gt;
&lt;p&gt;Never commit the JSON to the repo.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;why-this-ends-up-cleaner-than-a-fastlane-pipeline&quot;&gt;Why this ends up cleaner than a Fastlane pipeline&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;One binary, one install step.&lt;/strong&gt; No Ruby, no bundler, no Fastfile.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JSON output&lt;/strong&gt; — the vitals gating step is a &lt;code dir=&quot;auto&quot;&gt;jq&lt;/code&gt; one-liner, not a scraper.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Halted rollouts are first-class&lt;/strong&gt; — &lt;code dir=&quot;auto&quot;&gt;halt-rollout&lt;/code&gt; / &lt;code dir=&quot;auto&quot;&gt;resume-rollout&lt;/code&gt; / &lt;code dir=&quot;auto&quot;&gt;update-rollout&lt;/code&gt; are separate commands with clean semantics.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Same tool for post-release monitoring.&lt;/strong&gt; Same CLI runs the promotion, gates on vitals, and pings Slack. One dependency.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;getting-started&quot;&gt;Getting started&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tamtom/tap/gplay&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--auto&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Full track reference at &lt;a href=&quot;https://playconsolecli.com/reference/tracks/&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;/reference/tracks/&lt;/code&gt;&lt;/a&gt;, vitals at &lt;a href=&quot;https://playconsolecli.com/reference/vitals/&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;/reference/vitals/&lt;/code&gt;&lt;/a&gt;. If you want a starter Actions workflow for your project, install the &lt;a href=&quot;https://github.com/tamtom/gplay-cli-skills&quot;&gt;rollout-management skill&lt;/a&gt; and ask your AI agent to scaffold it against your app’s package name and typical release cadence.&lt;/p&gt;
</content:encoded><category>ci</category><category>github-actions</category><category>staged-rollout</category><category>tutorial</category></item><item><title>Sync Google Play subscription prices with RevenueCat across 175 countries</title><link>https://playconsolecli.com/blog/sync-google-play-subscription-prices-revenuecat/</link><guid isPermaLink="true">https://playconsolecli.com/blog/sync-google-play-subscription-prices-revenuecat/</guid><description>Keeping Google Play prices, RevenueCat offerings, and per-country pricing in sync manually is a full-time job. gplay handles the Play-side PPP conversion, RevenueCat MCP handles the offering-level equalization. Here&apos;s the whole flow.</description><pubDate>Sun, 05 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Google Play supports subscription pricing in 175 countries. RevenueCat abstracts pricing across stores. Keeping the two in sync — the actual per-country prices on Google Play, the offering-level packages in RevenueCat — is the kind of task that traditionally means opening two dashboards and a spreadsheet.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://playconsolecli.com/&quot;&gt;gplay&lt;/a&gt; covers the Google Play side; the &lt;a href=&quot;https://www.revenuecat.com/docs/tools/rc-mcp&quot;&gt;RevenueCat MCP&lt;/a&gt; covers the RevenueCat side. Together, an AI agent can keep them synchronized in one prompt.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;div&gt;&lt;h2 id=&quot;the-pricing-model-briefly&quot;&gt;The pricing model, briefly&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;On Google Play:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A base plan has a &lt;strong&gt;regional price&lt;/strong&gt; for each country, denominated in that country’s currency, stored in micro-units (1 USD = 1,000,000 micros).&lt;/li&gt;
&lt;li&gt;Google offers a &lt;strong&gt;PPP conversion&lt;/strong&gt; — you set the anchor (e.g. US $9.99), Google proposes prices for every other region weighted by local purchasing power. You accept, override, or reject per country.&lt;/li&gt;
&lt;li&gt;Prices can change over time; you have to explicitly re-set them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;In RevenueCat:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A package (e.g. &lt;code dir=&quot;auto&quot;&gt;$rc_monthly&lt;/code&gt;) points to a store product (e.g. &lt;code dir=&quot;auto&quot;&gt;pro:monthly&lt;/code&gt; on Google Play).&lt;/li&gt;
&lt;li&gt;The price RevenueCat sees is whatever Google Play reports for that product in the user’s region — RC doesn’t set prices, it observes them.&lt;/li&gt;
&lt;li&gt;RC’s &lt;code dir=&quot;auto&quot;&gt;equalize-subscription-prices&lt;/code&gt; tool normalizes prices &lt;em&gt;between&lt;/em&gt; stores (iOS ↔ Android) so that a user seeing $9.99 on iOS also sees the equivalent $9.99 on Android after currency and store-fee adjustments.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The takeaway: &lt;strong&gt;gplay writes the ground truth on Google Play. RevenueCat aligns iOS to match.&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;step-1--set-the-anchor-price-on-google-play&quot;&gt;Step 1 — set the anchor price on Google Play&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;baseplans&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prices&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--subscription-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pro&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--base-plan-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;monthly&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--region&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;US&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--price-micros&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;9990000&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;That’s your US monthly price at $9.99.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;step-2--expand-to-all-175-countries-with-ppp&quot;&gt;Step 2 — expand to all 175 countries with PPP&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;baseplans&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prices&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;convert&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--subscription-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pro&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--base-plan-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;monthly&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--from-region&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;US&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;convert&lt;/code&gt; uses Google’s PPP tables. Your $9.99 US price becomes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;India: ₹499&lt;/li&gt;
&lt;li&gt;Brazil: R$19.90&lt;/li&gt;
&lt;li&gt;Japan: ¥1,500&lt;/li&gt;
&lt;li&gt;Turkey: ₺149&lt;/li&gt;
&lt;li&gt;Argentina: AR$4,999&lt;/li&gt;
&lt;li&gt;Nigeria: ₦4,500&lt;/li&gt;
&lt;li&gt;… 170 more.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can review the whole set:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;baseplans&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prices&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;list&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--subscription-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pro&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--base-plan-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;monthly&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--output&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;table&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Override any single country:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;baseplans&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prices&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--subscription-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pro&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--base-plan-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;monthly&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--region&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;GB&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--price-micros&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;7990000&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# £7.99 flat, not the PPP-converted £8.53&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;step-3--sync-revenuecat-prices-to-match&quot;&gt;Step 3 — sync RevenueCat prices to match&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Once Google Play has the prices you want, tell RevenueCat to align iOS to match:&lt;/p&gt;
&lt;p&gt;Prompt your AI agent (Claude Code, Cursor, or any of the &lt;a href=&quot;https://playconsolecli.com/guides/ai-agents/&quot;&gt;12 supported agents&lt;/a&gt;):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Using the RevenueCat MCP, equalize subscription prices for the “default” offering so the App Store side matches what’s now on Google Play. Show me the diff before executing.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Under the hood the agent calls RC MCP’s &lt;code dir=&quot;auto&quot;&gt;equalize-subscription-prices&lt;/code&gt; — it reads Play Store prices as the source of truth and adjusts App Store prices to match after fee-and-currency normalization.&lt;/p&gt;
&lt;p&gt;Full round trip: gplay writes to Google Play → RC observes → RC MCP equalizes App Store.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;step-4--audit-for-drift&quot;&gt;Step 4 — audit for drift&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Prices drift over time. Google changes its PPP tables (sometimes materially). Local currency swings. Store fee changes.&lt;/p&gt;
&lt;p&gt;Weekly audit prompt:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Compare the current Google Play prices for &lt;code dir=&quot;auto&quot;&gt;com.example.app&lt;/code&gt; pro subscription against the anchor ($9.99 US monthly) using PPP tables from 3 months ago. Flag any country where the current price is more than 15% off the recomputed PPP.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The agent chains &lt;code dir=&quot;auto&quot;&gt;gplay baseplans prices list&lt;/code&gt; → PPP math against the anchor → diff report.&lt;/p&gt;
&lt;p&gt;You can also cross-reference with RevenueCat’s revenue metrics:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;For countries where the Play price is &gt;15% below the PPP anchor, pull the last 30 days of RC revenue metrics. Are we leaving money on the table?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;RC MCP &lt;code dir=&quot;auto&quot;&gt;get-revenue-metric&lt;/code&gt; filtered by region → agent computes revenue-per-user against the reduced-price cohort.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;step-5--coordinated-price-change&quot;&gt;Step 5 — coordinated price change&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;When you decide to raise US monthly from $9.99 to $12.99, you want the change to propagate cleanly.&lt;/p&gt;
&lt;p&gt;Prompt:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Change Google Play US monthly price for pro subscription to $12.99, propagate via PPP to all other regions except IN and BR (keep them at current), then equalize RevenueCat so iOS matches. Show the full diff — old vs new for every affected region and every affected RC package — before executing.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Chains:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;gplay &lt;code dir=&quot;auto&quot;&gt;baseplans prices set --region US --price-micros 12990000&lt;/code&gt; (dry-run)&lt;/li&gt;
&lt;li&gt;gplay &lt;code dir=&quot;auto&quot;&gt;baseplans prices convert --from-region US --exclude IN,BR&lt;/code&gt; (dry-run)&lt;/li&gt;
&lt;li&gt;gplay &lt;code dir=&quot;auto&quot;&gt;baseplans prices list&lt;/code&gt; → diff vs pre-state&lt;/li&gt;
&lt;li&gt;You approve.&lt;/li&gt;
&lt;li&gt;Steps 1-2 re-run for real.&lt;/li&gt;
&lt;li&gt;RC MCP &lt;code dir=&quot;auto&quot;&gt;equalize-subscription-prices&lt;/code&gt; triggers iOS equalization.&lt;/li&gt;
&lt;li&gt;RC MCP &lt;code dir=&quot;auto&quot;&gt;list-prices&lt;/code&gt; to confirm alignment.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A price change that used to be a ticket handed between finance, product, and the mobile team is now a prompt.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;what-fastlane-and-gradle-play-publisher-cant-do&quot;&gt;What Fastlane and gradle-play-publisher can’t do&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Neither supports subscription pricing at all. This isn’t a gap — they were built for a different scope. But if you’re doing anything with subscriptions, they’re not part of the toolchain.&lt;/p&gt;
&lt;p&gt;gplay covers the full monetization surface; combined with RC MCP, that surface stretches across both stores.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;getting-started&quot;&gt;Getting started&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tamtom/tap/gplay&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--auto&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Set up the &lt;a href=&quot;https://www.revenuecat.com/docs/tools/rc-mcp&quot;&gt;RevenueCat MCP&lt;/a&gt; in your agent. Install the &lt;a href=&quot;https://github.com/tamtom/gplay-cli-skills&quot;&gt;PPP pricing skill&lt;/a&gt; so your agent knows the anchor-and-convert pattern by default:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;skills&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tamtom/gplay-cli-skills&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Full base plan pricing reference at &lt;a href=&quot;https://playconsolecli.com/reference/baseplans/&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;/reference/baseplans/&lt;/code&gt;&lt;/a&gt;. Try the audit prompt above against your live subscriptions — most teams find at least three countries out of sync on the first run.&lt;/p&gt;
</content:encoded><category>revenuecat</category><category>pricing</category><category>subscriptions</category><category>tutorial</category></item><item><title>Verify Google Play purchases server-side without the Java SDK</title><link>https://playconsolecli.com/blog/verify-google-play-purchase-server-side/</link><guid isPermaLink="true">https://playconsolecli.com/blog/verify-google-play-purchase-server-side/</guid><description>The official Google Play purchase verification story assumes a JVM backend and OAuth boilerplate. gplay collapses receipt validation into a single command that works in any language, any runtime, any CI job.</description><pubDate>Sun, 05 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Server-side purchase verification on Google Play is one of those tasks where the official docs steer you toward a specific stack — pull in the &lt;a href=&quot;https://github.com/googleapis/google-api-java-client&quot;&gt;Google API Java client&lt;/a&gt;, set up OAuth, wire up the &lt;code dir=&quot;auto&quot;&gt;AndroidPublisher&lt;/code&gt; service, hand-roll the request. If your backend is Node, Go, Python, Rust, or basically anything else, you’re either translating that boilerplate or reaching for a community wrapper.&lt;/p&gt;
&lt;p&gt;There’s a shorter path: &lt;a href=&quot;https://playconsolecli.com/&quot;&gt;gplay&lt;/a&gt; validates receipts from a single command line. It’s the same &lt;a href=&quot;https://developers.google.com/android-publisher/api-ref/rest/v3/purchases.subscriptionsv2/get&quot;&gt;Google Play Android Publisher API&lt;/a&gt; call, just without the ceremony.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;div&gt;&lt;h2 id=&quot;the-full-flow-condensed&quot;&gt;The full flow, condensed&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Say your Android app sent your server a &lt;code dir=&quot;auto&quot;&gt;purchaseToken&lt;/code&gt; and a &lt;code dir=&quot;auto&quot;&gt;productId&lt;/code&gt; after a one-time purchase or subscription. You need to confirm with Google that it’s real, not consumed, and not refunded.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;purchases&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subscriptionsv2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--token&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$PURCHASE_TOKEN&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Output (minified JSON, one line — shown here formatted for reading):&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;kind&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;androidpublisher#subscriptionPurchaseV2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;subscriptionState&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;SUBSCRIPTION_STATE_ACTIVE&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;regionCode&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;US&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;lineItems&quot;&lt;/span&gt;&lt;span&gt;: [{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;productId&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;monthly_pro&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;expiryTime&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;2026-08-05T12:34:56Z&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;autoRenewingPlan&quot;&lt;/span&gt;&lt;span&gt;: { &lt;/span&gt;&lt;span&gt;&quot;autoRenewEnabled&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;acknowledgementState&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;linkedPurchaseToken&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Your backend parses that JSON and decides whether to grant entitlements.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;nodejs-example&quot;&gt;Node.js example&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { execFile } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;node:child_process&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { promisify } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;node:util&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;promisify&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;execFile&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;verifySubscription&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;packageName&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;token&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const { &lt;/span&gt;&lt;span&gt;stdout&lt;/span&gt;&lt;span&gt; } = await &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;purchases&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;subscriptionsv2&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;packageName&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;--token&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;token&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;purchase&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;JSON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;parse&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;stdout&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;active: &lt;/span&gt;&lt;span&gt;purchase&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;subscriptionState&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;SUBSCRIPTION_STATE_ACTIVE&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;productId: &lt;/span&gt;&lt;span&gt;purchase&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;lineItems&lt;/span&gt;&lt;span&gt;?.&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;?.&lt;/span&gt;&lt;span&gt;productId&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;expiresAt: &lt;/span&gt;&lt;span&gt;purchase&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;lineItems&lt;/span&gt;&lt;span&gt;?.&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;?.&lt;/span&gt;&lt;span&gt;expiryTime&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;No &lt;code dir=&quot;auto&quot;&gt;googleapis&lt;/code&gt; dependency. No OAuth token management. gplay handles the service-account auth internally from the file at &lt;code dir=&quot;auto&quot;&gt;GPLAY_SERVICE_ACCOUNT&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;go-example&quot;&gt;Go example&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;package&lt;/span&gt;&lt;span&gt; purchases&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;encoding/json&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;os/exec&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; Verified &lt;/span&gt;&lt;span&gt;struct&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;Active&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;bool&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;json:&quot;active&quot;&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;ProductID&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;json:&quot;productId&quot;&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;ExpiresAt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;json:&quot;expiresAt&quot;&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; v2 &lt;/span&gt;&lt;span&gt;struct&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;State&lt;/span&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;json:&quot;subscriptionState&quot;&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;LineItems&lt;/span&gt;&lt;span&gt; []&lt;/span&gt;&lt;span&gt;struct&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;ProductID&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;json:&quot;productId&quot;&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;ExpiryTime&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;json:&quot;expiryTime&quot;&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;json:&quot;lineItems&quot;&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;func&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Verify&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ctx&lt;/span&gt;&lt;span&gt; context.Context, &lt;/span&gt;&lt;span&gt;pkg&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;token&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;) (Verified, &lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;out&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;err&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;exec&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;CommandContext&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ctx&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;purchases&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;subscriptionsv2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;pkg&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;--token&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;token&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;Output&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;err&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nil&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; Verified{}, &lt;/span&gt;&lt;span&gt;err&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; v2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;err&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;json&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Unmarshal&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;out&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&amp;#x26;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;err&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nil&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; Verified{}, &lt;/span&gt;&lt;span&gt;err&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:=&lt;/span&gt;&lt;span&gt; Verified{&lt;/span&gt;&lt;span&gt;Active&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;State&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;SUBSCRIPTION_STATE_ACTIVE&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;len&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;LineItems&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;v&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ProductID&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;LineItems&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;].&lt;/span&gt;&lt;span&gt;ProductID&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;v&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ExpiresAt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;LineItems&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;].&lt;/span&gt;&lt;span&gt;ExpiryTime&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;v&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;nil&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;python-example&quot;&gt;Python example&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; subprocess&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;verify_subscription&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;package&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;str&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;token&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;str&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; -&gt; &lt;/span&gt;&lt;span&gt;dict&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;out &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; subprocess.&lt;/span&gt;&lt;span&gt;check_output&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;purchases&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;subscriptionsv2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, package, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;--token&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, token,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;purchase &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; json.&lt;/span&gt;&lt;span&gt;loads&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;out&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;active&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;: purchase[&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;subscriptionState&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;SUBSCRIPTION_STATE_ACTIVE&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;product_id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;: purchase[&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;lineItems&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;productId&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; purchase.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;lineItems&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;None&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;expires_at&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;: purchase[&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;lineItems&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;expiryTime&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; purchase.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;lineItems&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;None&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Same command, three languages, same shape.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;one-time-products&quot;&gt;One-time products&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;For consumables and non-consumables:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;purchases&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;products&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--product-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;premium_upgrade&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--token&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$PURCHASE_TOKEN&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;You get back &lt;code dir=&quot;auto&quot;&gt;purchaseState&lt;/code&gt; (&lt;code dir=&quot;auto&quot;&gt;0&lt;/code&gt; = purchased, &lt;code dir=&quot;auto&quot;&gt;1&lt;/code&gt; = canceled, &lt;code dir=&quot;auto&quot;&gt;2&lt;/code&gt; = pending), &lt;code dir=&quot;auto&quot;&gt;consumptionState&lt;/code&gt;, and &lt;code dir=&quot;auto&quot;&gt;orderId&lt;/code&gt;. Same JSON shape, same backend integration.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;acknowledging-purchases&quot;&gt;Acknowledging purchases&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Google requires purchases to be acknowledged within 3 days or they’re auto-refunded. gplay handles both flavors:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Subscriptions&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;purchases&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subscriptions&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;acknowledge&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--subscription-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;monthly_pro&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--token&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$PURCHASE_TOKEN&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Consumable products&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;purchases&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;products&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;consume&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--product-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gems_pack_100&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--token&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$PURCHASE_TOKEN&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Both are idempotent — safe to retry.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;voided-purchases-refunds-chargebacks&quot;&gt;Voided purchases (refunds, chargebacks)&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;To sweep for refunds you missed:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;purchases&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;voided&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;list&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--start-time&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2026-07-01T00:00:00Z&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--paginate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;--paginate&lt;/code&gt; fetches every page automatically. Run it as an hourly cron and revoke entitlements when a &lt;code dir=&quot;auto&quot;&gt;purchaseToken&lt;/code&gt; shows up.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;deployment&quot;&gt;Deployment&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;In your backend service or container, put the service-account JSON at a known path and set:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;GPLAY_SERVICE_ACCOUNT&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;secrets&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;play-sa&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;GPLAY_PACKAGE&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;com&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;example&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;app&lt;/span&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;# optional default&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;GPLAY_NO_UPDATE&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;span&gt;# disable the update check in prod&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;GPLAY_TIMEOUT&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;30s&lt;/span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;span&gt;# tighter timeout for API paths&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;That’s the whole setup. No OAuth flow, no refresh tokens, no client library.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;why-this-ends-up-simpler-than-the-sdk-path&quot;&gt;Why this ends up simpler than the SDK path&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Language-agnostic.&lt;/strong&gt; Your backend team keeps its stack; the CLI is the shared interface.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Static binary.&lt;/strong&gt; 12 MB, no runtime dependency, works in &lt;code dir=&quot;auto&quot;&gt;alpine&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;distroless&lt;/code&gt;, Lambda custom runtime, wherever.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deterministic JSON.&lt;/strong&gt; The output shape is stable — no library version drift.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code dir=&quot;auto&quot;&gt;--dry-run&lt;/code&gt; for tests.&lt;/strong&gt; Sanity-check request payloads in staging without hitting Google.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Same tool for the rest of Play Console.&lt;/strong&gt; The same CLI that verifies purchases also uploads builds, checks vitals, and downloads reports. One dependency, six APIs.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;get-started&quot;&gt;Get started&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tamtom/tap/gplay&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--auto&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;purchases&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subscriptionsv2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--token&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TEST_TOKEN&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Full purchase reference at &lt;a href=&quot;https://playconsolecli.com/reference/purchases/&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;/reference/purchases/&lt;/code&gt;&lt;/a&gt;. If you’re validating high volume, install the &lt;a href=&quot;https://github.com/tamtom/gplay-cli-skills&quot;&gt;purchase-verification skill&lt;/a&gt; and your AI agent will scaffold the backend integration for you.&lt;/p&gt;
</content:encoded><category>purchases</category><category>subscriptions</category><category>tutorial</category></item><item><title>gplay vs Fastlane supply: which Google Play CLI in 2026?</title><link>https://playconsolecli.com/blog/gplay-vs-fastlane-supply/</link><guid isPermaLink="true">https://playconsolecli.com/blog/gplay-vs-fastlane-supply/</guid><description>Fastlane supply pioneered command-line Play publishing, but it stops at uploads and listings. Here&apos;s an honest comparison across releases, monetization, monitoring, and runtime footprint.</description><pubDate>Sat, 04 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://docs.fastlane.tools/actions/supply/&quot;&gt;Fastlane supply&lt;/a&gt; has been the default answer to “how do I publish to Google Play from the command line?” for a decade. It’s battle-tested and it works. But it was designed for one job — pushing builds and metadata — and the Play Console has grown far beyond that: subscriptions with base plans and offers, purchase verification, vitals, reviews, permission management, private app publishing.&lt;/p&gt;
&lt;p&gt;Here’s an honest look at where each tool stands.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;div&gt;&lt;h2 id=&quot;the-short-version&quot;&gt;The short version&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Both handle the classics well&lt;/strong&gt;: AAB/APK upload, track assignment, staged rollouts, store listings, screenshots.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;gplay goes further&lt;/strong&gt;: full monetization stack, purchase verification, vitals, reviews, reports, user management, Managed Google Play.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Runtime footprint is the sleeper difference&lt;/strong&gt;: Fastlane needs a Ruby environment; gplay is one static Go binary.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;feature-comparison&quot;&gt;Feature comparison&lt;/h2&gt;&lt;/div&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;gplay&lt;/th&gt;
&lt;th&gt;Fastlane supply&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Upload AAB/APK, tracks, rollouts&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Store listings &amp;#x26; screenshots&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subscriptions, base plans, offers&lt;/td&gt;
&lt;td&gt;✅ Full&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;In-app products &amp;#x26; one-time purchases&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Purchase verification &amp;#x26; refunds&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vitals: crashes, ANRs, performance&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reviews: read + reply&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Financial &amp;#x26; statistics reports&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Users &amp;#x26; permission grants&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Managed Google Play (private apps)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output format&lt;/td&gt;
&lt;td&gt;Minified JSON&lt;/td&gt;
&lt;td&gt;Human-oriented logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runtime&lt;/td&gt;
&lt;td&gt;Single Go binary&lt;/td&gt;
&lt;td&gt;Ruby + gems&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div&gt;&lt;h2 id=&quot;where-fastlane-still-wins&quot;&gt;Where Fastlane still wins&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Fairness matters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ecosystem breadth&lt;/strong&gt; — Fastlane is a whole automation framework (screenshots via &lt;code dir=&quot;auto&quot;&gt;screengrab&lt;/code&gt;, iOS support, plugins). If you already run a &lt;code dir=&quot;auto&quot;&gt;Fastfile&lt;/code&gt; for both platforms, that inertia is real.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maturity&lt;/strong&gt; — a decade of production use and a huge community.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If all you do is &lt;code dir=&quot;auto&quot;&gt;supply&lt;/code&gt; an AAB once a sprint, Fastlane keeps doing that fine.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;where-gplay-pulls-ahead&quot;&gt;Where gplay pulls ahead&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;1-everything-after-the-upload&quot;&gt;1. Everything after the upload&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Modern Play operations are mostly &lt;em&gt;not&lt;/em&gt; uploads. Checking crash clusters before expanding a rollout, replying to reviews, creating a promotional offer, verifying a purchase token server-side — with Fastlane these all mean opening the web console. With gplay:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vitals&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;crashes&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;query&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2026-06-27&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--to&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2026-07-04&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;reviews&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;reply&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--review-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x3C;ID&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--text&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Fixed in 2.1!&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;purchases&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subscriptionsv2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--token&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x3C;TOKEN&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;2-scriptable-json-first-output&quot;&gt;2. Scriptable, JSON-first output&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Every gplay command emits minified JSON, so it pipes into &lt;code dir=&quot;auto&quot;&gt;jq&lt;/code&gt;, scripts, and AI agents without parsing log text:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tracks&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;list&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;jq&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;.tracks[].track&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;3-zero-runtime-install&quot;&gt;3. Zero-runtime install&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;CI images and dev machines don’t need Ruby. Install is one line, and startup is instant:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tamtom/tap/gplay&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;4-built-for-ai-agents&quot;&gt;4. Built for AI agents&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Explicit flags, &lt;code dir=&quot;auto&quot;&gt;--help&lt;/code&gt; everywhere, &lt;code dir=&quot;auto&quot;&gt;--dry-run&lt;/code&gt; on every write, no interactive prompts — plus &lt;a href=&quot;https://playconsolecli.com/guides/ai-agents/&quot;&gt;ready-made Agent Skills&lt;/a&gt; so Claude Code or Cursor can run your whole release flow.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;migrating-from-fastlane&quot;&gt;Migrating from Fastlane&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Already have a Fastlane &lt;code dir=&quot;auto&quot;&gt;metadata/&lt;/code&gt; directory? gplay imports it directly:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;migrate&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fastlane&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--package&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;com.example.app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--dir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;./fastlane/metadata/android&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Your existing translations, descriptions, and screenshots come along; nothing is lost.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;bottom-line&quot;&gt;Bottom line&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;If your Play workflow is “upload an AAB, sometimes update a listing,” either tool works — pick whichever fits your stack. The moment your workflow touches subscriptions, purchase verification, vitals, reviews, or agents/automation beyond the upload, gplay covers ground Fastlane simply doesn’t.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://playconsolecli.com/guides/installation/&quot;&gt;Install gplay →&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://playconsolecli.com/reference/&quot;&gt;Full command reference →&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>comparison</category><category>fastlane</category></item></channel></rss>