<?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>Diagnose your Play Console access with gplay auth doctor</title><link>https://playconsolecli.com/blog/gplay-auth-doctor-diagnose-play-console/</link><guid isPermaLink="true">https://playconsolecli.com/blog/gplay-auth-doctor-diagnose-play-console/</guid><description>Google Play API auth breaks in maybe six different ways — missing API enablement, wrong service account role, key rotation, missing scopes, developer invite pending. gplay auth doctor checks all of them in one command, and can auto-fix most.</description><pubDate>Sun, 05 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Google Play API authentication is a small forest of things that can go wrong. Your service account might exist but not have the Android Publisher API enabled on its GCP project. It might be invited to your Play Console developer account but not accepted yet. Its key might be expired. Its scopes might be missing a permission you added recently.&lt;/p&gt;
&lt;p&gt;Debugging any of these by hand is slow. &lt;code dir=&quot;auto&quot;&gt;gplay auth doctor&lt;/code&gt; runs every check in one shot.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;div&gt;&lt;h2 id=&quot;what-it-checks&quot;&gt;What it checks&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;auth&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;doctor&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 (all-green case):&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;✓ Service account file readable at /Users/you/.gplay/keys/play-sa.json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;✓ Service account email: play-cli@my-project.iam.gserviceaccount.com&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;✓ Client ID present&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;✓ Private key valid (RSA 2048)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;✓ GCP project &apos;my-project&apos; exists&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;✓ Android Publisher API enabled on my-project&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;✓ Reporting API enabled on my-project (needed for vitals)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;✓ Service account accepted in Play Console developer account 4571234567890&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;✓ Developer role: Admin (Access all apps)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;✓ Test API call: gplay apps list → 3 apps returned&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;✓ Configuration:&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_package: 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;&lt;/span&gt;&lt;span&gt;timeout: 120s&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;upload_timeout: 5m&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;Everything looks good.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Any red or yellow line tells you exactly which step is broken. No more guessing.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;common-failure-modes&quot;&gt;Common failure modes&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;android-publisher-api-not-enabled&quot;&gt;“Android Publisher API not enabled”&lt;/h3&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;✗ Android Publisher API NOT enabled on my-project&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;→ Enable it: https://console.cloud.google.com/apis/library/androidpublisher.googleapis.com?project=my-project&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;→ Or run: gplay auth doctor --fix&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;--fix --confirm&lt;/code&gt; will call the GCP API to enable it for you:&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;auth&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;doctor&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--fix&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--confirm&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Takes about 30 seconds to propagate.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;service-account-not-accepted-in-play-console&quot;&gt;“Service account not accepted in Play Console”&lt;/h3&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;✗ Service account play-cli@my-project.iam.gserviceaccount.com found in Play Console&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;invitation list but not accepted.&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;→ Open https://play.google.com/console/u/0/developers/4571234567890/users-and-permissions&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;and click Accept next to this email.&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 one is manual — Google requires a human accept the developer-account invite. gplay tells you exactly where to click.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;developer-role-has-insufficient-permissions&quot;&gt;“Developer role has insufficient permissions”&lt;/h3&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;⚠ Developer role: Marketing (Access some apps)&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;→ Reason: your role can read listings but not tracks. Some gplay commands will fail.&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;→ Fix: escalate role in Play Console, or grant per-app permissions&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 grants create --package com.example.app --permissions ...)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Some commands need admin, some need track edit. &lt;code dir=&quot;auto&quot;&gt;auth doctor&lt;/code&gt; flags the gap without waiting for a specific command to fail.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;reporting-api-not-enabled&quot;&gt;“Reporting API not enabled”&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Only matters if you use &lt;code dir=&quot;auto&quot;&gt;gplay vitals&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;⚠ Play Developer Reporting API NOT enabled — vitals commands will fail&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;→ Fix: gplay auth doctor --fix --confirm&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;private-key-expired-or-rotated&quot;&gt;“Private key expired or rotated”&lt;/h3&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;✗ Private key rejected by Google (invalid_grant)&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;→ The key at /Users/you/.gplay/keys/play-sa.json is likely rotated or revoked&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;→ Fix: gplay auth login --service-account /path/to/new-key.json&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’ll see this the day someone in your org enforces the 90-day key rotation policy.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;timeout-too-aggressive-for-uploads&quot;&gt;“Timeout too aggressive for uploads”&lt;/h3&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;⚠ upload_timeout is 30s — AAB uploads over slow networks may fail&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;→ Recommended: 5m or higher via GPLAY_UPLOAD_TIMEOUT or ~/.gplay/config.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;fix-flags&quot;&gt;Fix flags&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;--fix&lt;/code&gt; proposes remediations. &lt;code dir=&quot;auto&quot;&gt;--confirm&lt;/code&gt; executes them non-interactively (needed in CI). Together:&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;auth&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;doctor&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--fix&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--confirm&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;What &lt;code dir=&quot;auto&quot;&gt;--fix&lt;/code&gt; can do without asking:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Enable Android Publisher API on the linked GCP project.&lt;/li&gt;
&lt;li&gt;Enable Play Developer Reporting API.&lt;/li&gt;
&lt;li&gt;Update &lt;code dir=&quot;auto&quot;&gt;~/.gplay/config.yaml&lt;/code&gt; timeouts to safer defaults.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What it &lt;strong&gt;won’t&lt;/strong&gt; do (needs a human):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Accept a Play Console developer invitation (Google policy).&lt;/li&gt;
&lt;li&gt;Rotate a service-account key (you should verify).&lt;/li&gt;
&lt;li&gt;Escalate a developer role (permissions change).&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;ci-usage&quot;&gt;CI usage&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Run it as the first step of your release pipeline. If auth is degraded, fail early:&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;Verify Play API auth&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;gplay auth doctor --output json | jq -e &apos;.status == &quot;healthy&quot;&apos;&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;--output json&lt;/code&gt; returns a structured verdict with per-check pass/fail. &lt;code dir=&quot;auto&quot;&gt;jq -e&lt;/code&gt; fails the step if the overall status isn’t &lt;code dir=&quot;auto&quot;&gt;healthy&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Beats a Play API call failing 20 minutes into an upload.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;alongside-a-new-service-account&quot;&gt;Alongside a new service account&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The order of operations &lt;code dir=&quot;auto&quot;&gt;setup --auto&lt;/code&gt; runs, if you want to know what &lt;code dir=&quot;auto&quot;&gt;doctor&lt;/code&gt; is checking:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Detect or install &lt;code dir=&quot;auto&quot;&gt;gcloud&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Prompt for or create a GCP project.&lt;/li&gt;
&lt;li&gt;Enable Android Publisher API and Reporting API on that project.&lt;/li&gt;
&lt;li&gt;Create service account.&lt;/li&gt;
&lt;li&gt;Download JSON key to &lt;code dir=&quot;auto&quot;&gt;~/.gplay/keys/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Print the service-account email and ask you to invite it in Play Console.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;gplay auth doctor&lt;/code&gt; re-validates steps 3-6 any time you run it.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;when-to-run-it&quot;&gt;When to run it&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;After &lt;code dir=&quot;auto&quot;&gt;gplay setup --auto&lt;/code&gt; — sanity check.&lt;/li&gt;
&lt;li&gt;When your CI job starts failing with &lt;code dir=&quot;auto&quot;&gt;invalid_grant&lt;/code&gt; or &lt;code dir=&quot;auto&quot;&gt;unauthorized&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;After rotating a key.&lt;/li&gt;
&lt;li&gt;After adding a new API (subscriptions → in-app products → reporting).&lt;/li&gt;
&lt;li&gt;Weekly as a cron in a lightweight monitoring workflow.&lt;/li&gt;
&lt;li&gt;Before assuming “gplay is broken” — it’s usually auth.&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;div&gt;&lt;div&gt;&lt;span&gt;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;auth&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;doctor&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;If you get a red line, read what it says — that’s where the fix is. If you’re not sure, &lt;code dir=&quot;auto&quot;&gt;gplay auth doctor --fix --confirm&lt;/code&gt; handles the fixable ones automatically. Full auth reference at &lt;a href=&quot;https://playconsolecli.com/reference/auth/&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;/reference/auth/&lt;/code&gt;&lt;/a&gt;, full setup at &lt;a href=&quot;https://playconsolecli.com/guides/authentication/&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;/guides/authentication/&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>authentication</category><category>troubleshooting</category><category>tutorial</category></item><item><title>Google Play ASO: from keyword research to submission with gplay and an AI agent</title><link>https://playconsolecli.com/blog/google-play-aso-research-to-submission/</link><guid isPermaLink="true">https://playconsolecli.com/blog/google-play-aso-research-to-submission/</guid><description>Google Play ASO — App Store Optimization — used to mean spreadsheets, screenshots of competitors, and a lot of copy-pasting. Combining gplay with an AI agent turns it into a one-hour audit-to-submission loop across all your locales.</description><pubDate>Sun, 05 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;App Store Optimization on Google Play is where a well-worded title, a keyword-rich short description, and localized long descriptions turn into installs. It’s also traditionally where the most tedious mobile-marketing work happens: research competitor keywords, audit your current listings, draft variants, translate, get approvals, click through the Play Console to update each locale, wait, ship.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://playconsolecli.com/&quot;&gt;gplay&lt;/a&gt; CLI plus an AI agent collapses this into a single afternoon workflow. Here’s how.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;div&gt;&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The four phases of a Play ASO cycle:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Research&lt;/strong&gt; — what keywords should you rank for, and how do competitors position?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Audit&lt;/strong&gt; — what does your current listing look like, per locale, across all supported markets?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimize&lt;/strong&gt; — draft revised titles, short descriptions, long descriptions, and localize them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ship&lt;/strong&gt; — push the changes atomically inside an edit session and monitor rankings.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;gplay covers phases 2 and 4 directly, and enables phases 1 and 3 by giving your AI agent structured data to reason about.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;phase-1--research-agent-assisted&quot;&gt;Phase 1 — research (agent-assisted)&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Give your AI agent a research prompt:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I want to improve ASO for &lt;code dir=&quot;auto&quot;&gt;com.example.app&lt;/code&gt; — a habit-tracking app that competes with Streaks, Habitica, and Way of Life. Research the top 20 keyword phrases those competitors’ listings target on Google Play. For each keyword, note whether it’s in the title, short description, or long description, and estimate search intent volume qualitatively. Return a ranked list with rationale.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Any of the &lt;a href=&quot;https://playconsolecli.com/guides/ai-agents/&quot;&gt;12 supported agents&lt;/a&gt; with web-browsing (Claude Code, Cursor, ChatGPT with browsing, etc.) can do the competitor scan. You get back a ranked keyword list — the raw material for phase 3.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;phase-2--audit-your-current-listings&quot;&gt;Phase 2 — audit your current listings&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Pull every locale you currently ship in:&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;listings&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;--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;Then export each locale’s listing to JSON for the agent to inspect:&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;mkdir&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;./aso-audit&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;listings&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;/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;-r&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;.[].language&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;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;while&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;read&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LOCALE&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;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;listings&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;--locale&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$LOCALE&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;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;./aso-audit/${&lt;/span&gt;&lt;span&gt;LOCALE&lt;/span&gt;&lt;span&gt;}.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;done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Now your agent has a per-locale snapshot. Prompt:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Read every JSON file in &lt;code dir=&quot;auto&quot;&gt;./aso-audit/&lt;/code&gt;. For each locale, tell me:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Does the current title include any of the top keywords from the research doc?&lt;/li&gt;
&lt;li&gt;Is the short description using its full 80-character budget?&lt;/li&gt;
&lt;li&gt;Is the long description repeating any keyword more than 3 times (over-optimization penalty risk)?&lt;/li&gt;
&lt;li&gt;Rank locales by improvement potential (biggest gap between current and ideal).&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;You get a per-locale gap analysis in one pass.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;phase-3--optimize-copy-per-locale&quot;&gt;Phase 3 — optimize copy per locale&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;For your top-priority locale (usually &lt;code dir=&quot;auto&quot;&gt;en-US&lt;/code&gt;), prompt:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Rewrite the &lt;code dir=&quot;auto&quot;&gt;en-US&lt;/code&gt; title, short description, and full description for &lt;code dir=&quot;auto&quot;&gt;com.example.app&lt;/code&gt; to target the top 5 keywords from research. Constraints: title ≤ 30 chars, short description ≤ 80 chars, full description ≤ 4000 chars. Preserve the “gentle, non-nagging” brand voice from the current listing. Return three variants of each.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Pick the variant you like. Then propagate to other locales:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Take the chosen &lt;code dir=&quot;auto&quot;&gt;en-US&lt;/code&gt; variant and translate it into fr-FR, de-DE, es-ES, es-419, pt-BR, ja-JP, ko, zh-CN, zh-TW, and ar. Preserve keyword semantic intent — don’t just literally translate the keywords, use whatever people search for in that language. Note per-locale where the keyword substitution changes character count materially.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Write results to &lt;code dir=&quot;auto&quot;&gt;metadata/&amp;#x3C;locale&gt;/&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;phase-4--dry-run-review-ship&quot;&gt;Phase 4 — dry-run, review, ship&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Preview what will be sent to Play:&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;listings&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;push&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;--listings-dir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;./metadata&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;Diff against production to double-check:&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;# Save current state&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;listings&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;/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;-r&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;.[].language&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;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;while&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;read&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;L&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;gplay&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;listings&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;--locale&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$L&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;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;./aso-audit/before-${&lt;/span&gt;&lt;span&gt;L&lt;/span&gt;&lt;span&gt;}.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;done&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;# Compare against your metadata/ files with jq or diff&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Ship inside an edit session so everything commits atomically:&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;listings&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;push&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;--listings-dir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;./metadata&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;If anything fails mid-way, gplay discards the edit — the Play Console goes back to exactly its previous state, no partial half-updated locales.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;post-ship-monitor-rankings&quot;&gt;Post-ship: monitor rankings&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Google doesn’t expose keyword-rank APIs, so the honest answer is: install rankings tracking (App Radar, AppTweak, Sensor Tower) and correlate the rank change to your ship date. gplay can’t do this piece.&lt;/p&gt;
&lt;p&gt;What gplay &lt;em&gt;can&lt;/em&gt; do is monitor install/impression trends via &lt;code dir=&quot;auto&quot;&gt;gplay reports statistics&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;# Install statistics report&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;statistics&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;/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;reports&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;statistics&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;\&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;--report-type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;INSTALLS&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;--year&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2026&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--month&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;7&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;./reports/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Compare month-over-month against your ship date. Not as precise as a rank tracker but free and useful.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-full-end-to-end-workflow-as-a-single-prompt&quot;&gt;The full end-to-end workflow, as a single prompt&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Once you’ve done this cycle once by hand, you can compress it. In Claude Code:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Run a Google Play ASO audit for &lt;code dir=&quot;auto&quot;&gt;com.example.app&lt;/code&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Pull every current listing to &lt;code dir=&quot;auto&quot;&gt;./aso-audit/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Web-research the top 20 keywords the top 5 habit-tracking apps target on Play (Streaks, Habitica, Way of Life, Loop, Done).&lt;/li&gt;
&lt;li&gt;Rank my locales by improvement potential.&lt;/li&gt;
&lt;li&gt;For the top 3 locales, draft 3 title/short/long description variants each.&lt;/li&gt;
&lt;li&gt;Wait for me to pick the winners.&lt;/li&gt;
&lt;li&gt;Translate winners to the remaining locales.&lt;/li&gt;
&lt;li&gt;Dry-run &lt;code dir=&quot;auto&quot;&gt;gplay listings push&lt;/code&gt; and show me the payload.&lt;/li&gt;
&lt;li&gt;Wait for my approval, then push.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Use the metadata-sync skill from tamtom/gplay-cli-skills for the file layout.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;An afternoon’s worth of clickwork becomes a review session.&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;Full listings reference at &lt;a href=&quot;https://playconsolecli.com/reference/listings/&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;/reference/listings/&lt;/code&gt;&lt;/a&gt;. For deeper locale handling see &lt;a href=&quot;https://playconsolecli.com/blog/manage-google-play-locales-from-terminal/&quot;&gt;Managing 80+ Google Play locales from the terminal&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The ASO cycle is where CLIs and AI agents together outperform every other workflow — the tedious parts (audit, translate, push) are exactly the parts machines do best, and the judgment parts (brand voice, keyword tradeoffs) stay with you.&lt;/p&gt;
</content:encoded><category>aso</category><category>listings</category><category>ai-agents</category><category>tutorial</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>Managing 80+ Google Play locales from the terminal</title><link>https://playconsolecli.com/blog/manage-google-play-locales-from-terminal/</link><guid isPermaLink="true">https://playconsolecli.com/blog/manage-google-play-locales-from-terminal/</guid><description>Google Play supports over 80 store-listing locales. Setting them up in the web UI is a click-fest; doing it from a terminal is a for-loop. Here&apos;s how to discover, validate, and localize listings and release notes with gplay.</description><pubDate>Sun, 05 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Google Play supports more than 80 store-listing locales — from &lt;code dir=&quot;auto&quot;&gt;af&lt;/code&gt; (Afrikaans) to &lt;code dir=&quot;auto&quot;&gt;zu&lt;/code&gt; (Zulu), including regional variants like &lt;code dir=&quot;auto&quot;&gt;es-419&lt;/code&gt; (Latin American Spanish), &lt;code dir=&quot;auto&quot;&gt;pt-BR&lt;/code&gt; (Brazilian Portuguese), and &lt;code dir=&quot;auto&quot;&gt;zh-TW&lt;/code&gt; (Traditional Chinese). Setting them all up in the Play Console web UI means clicking through a language dropdown once per locale, per app, and re-doing it for every listing change.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://playconsolecli.com/&quot;&gt;gplay&lt;/a&gt; turns that into a scriptable batch job. Here’s the full toolkit.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;div&gt;&lt;h2 id=&quot;discover-the-supported-locales&quot;&gt;Discover the supported locales&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;listings&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;locales&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Returns the full list Google Play accepts, as JSON:&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;code&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;af&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;name&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Afrikaans&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;code&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;am&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;name&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Amharic&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;code&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;ar&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;name&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Arabic&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;code&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;az-AZ&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;name&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Azerbaijani&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;/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;code&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;zh-TW&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;name&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Chinese (Traditional)&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;code&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;zu&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;name&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Zulu&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;Or as a table:&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;listings&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;--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;Validate a code before you use it (avoid typos wasting a Play API round-trip):&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;listings&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;--validate&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ar-XB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# → error: unknown locale &apos;ar-XB&apos;. Did you mean &apos;ar&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;h2 id=&quot;see-what-you-currently-have&quot;&gt;See what you currently have&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Which locales does your app currently have listings for?&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;listings&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;--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;Diff against the full supported set to find gaps:&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;CURRENT&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;listings&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;-r&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;.[].language&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;&lt;span&gt;sort&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;SUPPORTED&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;listings&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;|&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;.[].code&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;&lt;span&gt;sort&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;comm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-23&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x3C;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$SUPPORTED&lt;/span&gt;&lt;span&gt;&quot;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x3C;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$CURRENT&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 tells you exactly which of the 80+ locales you’re missing.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;get-a-single-locales-full-listing&quot;&gt;Get a single locale’s full listing&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;listings&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;--locale&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;de-DE&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Returns title, short description, full description, video URL. Perfect for pulling translations into a review tool.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;bulk-create-listings-from-a-directory&quot;&gt;Bulk-create listings from a directory&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;--listings-dir&lt;/code&gt; flag reads Fastlane-format metadata:&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;metadata/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;├── en-US/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;│   ├── title.txt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&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;│   ├── full_description.txt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;│   └── changelogs/default.txt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&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;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;├── de-DE/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;├── es-ES/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;├── ja-JP/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;├── ko/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;├── pt-BR/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;└── zh-TW/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Push them all in one shot:&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;listings&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;push&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;--listings-dir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;./metadata&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 locale that has a directory gets its listing created or updated inside a single edit session — atomic commit.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;preview-before-pushing&quot;&gt;Preview before pushing&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;--dry-run&lt;/code&gt; intercepts the write and shows you the payload:&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;listings&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;push&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;--listings-dir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;./metadata&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;You see exactly what will be sent to Google, per locale, without touching production.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;per-locale-release-notes&quot;&gt;Per-locale release notes&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Release notes shipped alongside a track update. YAML format:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;release-notes.yaml&lt;/span&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;New Pro features and bug fixes&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;en-GB&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;New Pro features and bug fixes&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;Nouvelles fonctionnalités Pro et corrections de bugs&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;Neue Pro-Funktionen und Fehlerbehebungen&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;Nuevas funciones Pro y correcciones de errores&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-419&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Nuevas funciones Pro y correcciones de errores&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;pt-BR&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Novas funcionalidades Pro e correções de bugs&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;新しいProの機能とバグ修正&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;ko&lt;/span&gt;&lt;span&gt;: &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;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;zh-TW&lt;/span&gt;&lt;span&gt;: &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;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;zh-CN&lt;/span&gt;&lt;span&gt;: &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;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ar&lt;/span&gt;&lt;span&gt;: &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;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Apply on release:&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;--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;Or generate the whole file from git history in your default locale, then translate:&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;v4.1.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;p&gt;If you pass a plain string as &lt;code dir=&quot;auto&quot;&gt;--release-notes&lt;/code&gt;, gplay auto-assigns it to &lt;code dir=&quot;auto&quot;&gt;en-US&lt;/code&gt;. Explicit is better — but this shortcut is handy for internal builds.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;locale-aware-validation&quot;&gt;Locale-aware validation&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Google will reject a release if:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Any locale has an empty title.&lt;/li&gt;
&lt;li&gt;Full description exceeds 4,000 characters.&lt;/li&gt;
&lt;li&gt;Short description exceeds 80 characters.&lt;/li&gt;
&lt;li&gt;A locale exists in your listings but not in the supported set (typo, stale data).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Preflight:&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;listings&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;validate&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;--listings-dir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;./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;ai-agent-driven-localization&quot;&gt;AI-agent-driven localization&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Combine gplay with your AI agent for the tedious parts. Prompt:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Pull the &lt;code dir=&quot;auto&quot;&gt;en-US&lt;/code&gt; listing and full description for &lt;code dir=&quot;auto&quot;&gt;com.example.app&lt;/code&gt;. Translate the short and full descriptions into French, German, Spanish (both es-ES and es-419), Portuguese (both pt-BR and pt-PT), Japanese, Korean, Chinese (both zh-CN and zh-TW), and Arabic. Preserve technical terms (product names, feature names). Write each to &lt;code dir=&quot;auto&quot;&gt;metadata/&amp;#x3C;locale&gt;/short_description.txt&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;full_description.txt&lt;/code&gt;. When done, dry-run &lt;code dir=&quot;auto&quot;&gt;gplay listings push&lt;/code&gt; so I can review.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Chains: &lt;code dir=&quot;auto&quot;&gt;gplay listings get --locale en-US&lt;/code&gt; → LLM translations to files → &lt;code dir=&quot;auto&quot;&gt;gplay listings push --dry-run&lt;/code&gt;. You review, then run without &lt;code dir=&quot;auto&quot;&gt;--dry-run&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Every one of the &lt;a href=&quot;https://playconsolecli.com/guides/ai-agents/&quot;&gt;12 supported agents&lt;/a&gt; — Claude Code, Cursor, Codex, Gemini CLI, Aider, Cline, Windsurf, Continue, GitHub Copilot CLI, Amazon Q Developer, OpenClaw, Hermes Agent — can do this. Install &lt;a href=&quot;https://github.com/tamtom/gplay-cli-skills&quot;&gt;the metadata-sync skill&lt;/a&gt; and they’ll know the file layout by default.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;screenshots-per-locale&quot;&gt;Screenshots per locale&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Localized listings often need localized screenshots (screenshots with translated in-image text). &lt;code dir=&quot;auto&quot;&gt;gplay listings push --screenshots-dir&lt;/code&gt; reads the same Fastlane-format tree:&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;screenshots/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;├── en-US/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;│   └── phoneScreenshots/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;│       ├── 1.png&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;│       ├── 2.png&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;span&gt;├── fr-FR/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&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;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Or use &lt;code dir=&quot;auto&quot;&gt;--skip-screenshots&lt;/code&gt; if you only want to update text.&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;listings&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;--output&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;table&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;listings&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;--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 listings reference at &lt;a href=&quot;https://playconsolecli.com/reference/listings/&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;/reference/listings/&lt;/code&gt;&lt;/a&gt;. If you’re maintaining a large locale matrix, install &lt;a href=&quot;https://github.com/tamtom/gplay-cli-skills&quot;&gt;the metadata-sync skill&lt;/a&gt; and let your AI agent drive the day-to-day translation-review-push cycle.&lt;/p&gt;
</content:encoded><category>localization</category><category>listings</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>Pre-flight Google Play subscription validation: catch problems before Play rejects</title><link>https://playconsolecli.com/blog/pre-submission-google-play-subscription-validation/</link><guid isPermaLink="true">https://playconsolecli.com/blog/pre-submission-google-play-subscription-validation/</guid><description>Google Play subscription rejections have long feedback loops — you submit, wait, get rejected for something obvious like a missing localization. Run gplay&apos;s pre-flight validation first and catch every issue in seconds.</description><pubDate>Sun, 05 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Google Play subscription reviews have brutal feedback loops. Submit a new subscription with a missing German localization or an offer without an eligibility rule, and you’ll get a rejection notice hours or days later, with a “please fix and resubmit” wall of text that doesn’t quite tell you which field.&lt;/p&gt;
&lt;p&gt;This is entirely avoidable. Every rejection reason maps to something you can check locally, in seconds, with &lt;a href=&quot;https://playconsolecli.com/&quot;&gt;gplay&lt;/a&gt;.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;div&gt;&lt;h2 id=&quot;the-checklist-that-gets-you-rejected&quot;&gt;The checklist that gets you rejected&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;From reviewing hundreds of rejection cases, the recurring pattern:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Subscription created but never activated.&lt;/strong&gt; &lt;code dir=&quot;auto&quot;&gt;status: PENDING&lt;/code&gt; — Google won’t display it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Base plan created but no price set&lt;/strong&gt; in one or more countries. Google won’t publish a base plan that’s missing prices in Play’s list of supported markets.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Free-trial offer with no eligibility rule.&lt;/strong&gt; Google requires an eligibility rule (usually “new subscribers only”) or rejects the offer as promo-abuse-prone.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Offer name not localized&lt;/strong&gt; for locales where the app is listed. Google flags this even for locales where you’d say “who cares, they read English fine.”&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Introductory-price offer where the intro period equals the full billing period.&lt;/strong&gt; Fails Google’s promo-value check.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Auto-renewing base plan without any offer&lt;/strong&gt; (fine) alongside a legacy prepaid offer of the same product (not fine).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Product ID contains a character&lt;/strong&gt; Google doesn’t accept in the current API version (mostly caught, but happens).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Subscription referenced by a paywall on your site&lt;/strong&gt; that doesn’t exist. Not technically rejected by Play but breaks store link discoverability.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You want to catch every one of these before the submit button.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-command&quot;&gt;The 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;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;validate&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;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Runs every check above against the current state in Play Console and prints a report:&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;Validating subscription &apos;pro&apos; for 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;✓ Subscription status: ACTIVE&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;✓ 2 base plans: monthly (P1M), yearly (P1Y)&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;Base plan: monthly&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;✓ Auto-renewing&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;✓ Billing period valid: P1M&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;✓ Prices set in 175 regions&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;⚠ Price missing in 2 regions: TR, AR&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;✓ 1 offer: monthly-trial-7d&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;Offer: monthly-trial-7d&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;✓ Phases valid: FREE_TRIAL P7D 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;✗ Eligibility rule missing — Google requires this for FREE_TRIAL offers&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;⚠ Offer name localized in 8/12 supported locales&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;Missing: es-419, pt-BR, ja-JP, ko&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;Base plan: yearly&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;✓ Auto-renewing&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;✓ Billing period valid: P1Y&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;✓ Prices set in 175 regions&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;✓ No offers&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;Summary: 2 errors, 3 warnings.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Would likely be rejected on submission.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Fix the errors (missing eligibility rule, missing prices), address the warnings, re-run. Repeat until it says “OK to submit.”&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;what---strict-adds&quot;&gt;What &lt;code dir=&quot;auto&quot;&gt;--strict&lt;/code&gt; adds&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;By default &lt;code dir=&quot;auto&quot;&gt;validate&lt;/code&gt; runs the required checks (things Google definitely rejects). Add &lt;code dir=&quot;auto&quot;&gt;--strict&lt;/code&gt; to also flag ergonomics issues that don’t necessarily fail review but hurt conversion:&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;validate&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--strict&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;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;--strict&lt;/code&gt; adds:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Offer names shorter than 15 characters (“Trial” instead of “Try free for 7 days”).&lt;/li&gt;
&lt;li&gt;Base plans with no &lt;code dir=&quot;auto&quot;&gt;renewal_type&lt;/code&gt; set (defaults to auto-renewing but explicit is better).&lt;/li&gt;
&lt;li&gt;Prices where the yearly is not at least 15% cheaper than 12x monthly (weird pricing signal).&lt;/li&gt;
&lt;li&gt;Missing tag on offers (&lt;code dir=&quot;auto&quot;&gt;SPECIAL_INTRO&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;LEGACY_INTRO&lt;/code&gt;) that RevenueCat and other billing wrappers use.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Not blocking, but worth reviewing.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;batch-validate-every-subscription&quot;&gt;Batch validate every subscription&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;subscriptions&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;/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;-r&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;.[].productId&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;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;xargs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-I&lt;/span&gt;&lt;span&gt;{} gplay subscriptions validate --package com.example.app --product-id {}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Prints a report per subscription. Use this before any batch price change or when auditing an app you inherited.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;pre-flight-in-ci&quot;&gt;Pre-flight in CI&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Fail the pipeline if any subscription has errors:&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;Validate subscriptions before promoting&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 subscriptions list --package 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;&lt;/span&gt;&lt;span&gt;| jq -r &apos;.[].productId&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;| while read PRODUCT; 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;&lt;/span&gt;&lt;span&gt;RESULT=$(gplay subscriptions validate \&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;--package 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;&lt;/span&gt;&lt;span&gt;--product-id &quot;$PRODUCT&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;--output 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;ERRORS=$(echo &quot;$RESULT&quot; | jq &apos;.errors | length&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;$ERRORS&quot; -gt 0 ]; 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;❌ $PRODUCT has $ERRORS errors:&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;echo &quot;$RESULT&quot; | jq -r &apos;.errors[] | &quot;  - \(.field): \(.message)&quot;&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;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;done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Runs every time you’re about to promote a build, in seconds.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;fixing-the-common-errors&quot;&gt;Fixing the common errors&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Missing eligibility rule for a free trial:&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;offers&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;\&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;--eligibility&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DEVELOPER_ELIGIBILITY_NEW_SUBSCRIBERS_ONLY&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;Missing prices in specific regions:&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;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;TR&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;149000000&lt;/span&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;# ₺149&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 expand from an anchor:&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;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;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;--only-missing&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;Missing offer name localizations:&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;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;es-419:&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Prueba gratis 7 días&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,pt-BR:&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;7 dias grátis&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;span&gt;,ko:&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;&lt;strong&gt;Subscription still in &lt;code dir=&quot;auto&quot;&gt;PENDING&lt;/code&gt;:&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;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;div&gt;&lt;h2 id=&quot;ai-agent-driven-fix-loop&quot;&gt;AI-agent-driven fix loop&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Give your agent the validate output and let it fix in a loop:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Run &lt;code dir=&quot;auto&quot;&gt;gplay subscriptions validate&lt;/code&gt; for every subscription on &lt;code dir=&quot;auto&quot;&gt;com.example.app&lt;/code&gt;. For each error, fix it. For each warning, ask me before touching. When everything’s green, print the final validate output.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Chains: &lt;code dir=&quot;auto&quot;&gt;subscriptions list&lt;/code&gt; → per-subscription &lt;code dir=&quot;auto&quot;&gt;validate&lt;/code&gt; → per-error remediation command → re-validate → summary.&lt;/p&gt;
&lt;p&gt;Works with all &lt;a href=&quot;https://playconsolecli.com/guides/ai-agents/&quot;&gt;12 supported agents&lt;/a&gt;. The &lt;a href=&quot;https://github.com/tamtom/gplay-cli-skills&quot;&gt;IAP setup skill&lt;/a&gt; teaches the fix mappings so agents don’t need to guess:&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;div&gt;&lt;h2 id=&quot;what-validate-does-not-check&quot;&gt;What &lt;code dir=&quot;auto&quot;&gt;validate&lt;/code&gt; does NOT check&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;To be honest about the tool’s limits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Google’s discretionary review.&lt;/strong&gt; Play reviewers occasionally reject on judgment calls (e.g. “your feature description doesn’t match the subscription value”). No local tool catches this.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Policy changes since the check was written.&lt;/strong&gt; Google updates review policies quarterly-ish; validate ships known-good checks, not future ones.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-store consistency.&lt;/strong&gt; If your App Store subscription has different phases than your Play one, validate won’t flag it (that’s &lt;a href=&quot;https://playconsolecli.com/blog/sync-google-play-subscription-prices-revenuecat/&quot;&gt;what RevenueCat MCP is for&lt;/a&gt;).&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;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;validate&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;&amp;#x3C;your-sub-id&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;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;. Run it before every submission — it takes about a second and catches every rejection reason we’ve seen.&lt;/p&gt;
</content:encoded><category>subscriptions</category><category>validation</category><category>submission</category></item><item><title>Read and edit Google Play Console app info from the terminal</title><link>https://playconsolecli.com/blog/read-edit-play-console-app-info-terminal/</link><guid isPermaLink="true">https://playconsolecli.com/blog/read-edit-play-console-app-info-terminal/</guid><description>You don&apos;t need the Play Console web UI to inspect an app&apos;s state — listings, tracks, releases, review status, subscriptions. gplay reads and writes all of it. Here&apos;s a cheat sheet of the commands you&apos;ll use every day.</description><pubDate>Sun, 05 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The Play Console web UI is fine for browsing. It’s not great for scripting, comparing state across apps, or feeding your AI agent structured data. &lt;a href=&quot;https://playconsolecli.com/&quot;&gt;gplay&lt;/a&gt; exposes the same information as JSON from your terminal — usually one command per screen you’d have clicked through.&lt;/p&gt;
&lt;p&gt;This is a cheat sheet of the commands you’ll use daily.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;div&gt;&lt;h2 id=&quot;basic-app-info&quot;&gt;Basic app info&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;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;# every app the service account can see&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;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;# one app&apos;s full details&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;For a quick scan use table output:&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;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;--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;div&gt;&lt;h2 id=&quot;listings-title-description-screenshots-graphics&quot;&gt;Listings (title, description, screenshots, graphics)&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;# All locales for an 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;listings&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;--output&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;table&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;# One locale, full detail&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;listings&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;--locale&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;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Update a single field&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;listings&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;\&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;--locale&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;en-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;--short-description&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;The best way to X&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;Full localization workflow: &lt;a href=&quot;https://playconsolecli.com/blog/manage-google-play-locales-from-terminal/&quot;&gt;Managing 80+ Google Play locales from the terminal&lt;/a&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;version-and-track-state&quot;&gt;Version and track state&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;# What&apos;s live where?&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;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;--output&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;table&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;# One track&apos;s current release&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;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;--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;# What&apos;s the version code / name of the latest bundle in a track?&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;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;--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;|&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;.releases[0] | &quot;\(.name) (\(.versionCodes[0]))&quot;&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;h2 id=&quot;uploaded-bundles-and-apks&quot;&gt;Uploaded bundles and APKs&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;# Every AAB uploaded&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;bundles&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;/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;# Every APK&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;apks&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;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Handy for finding older builds you can roll back to, or checking whether a specific version code made it up.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;subscriptions-base-plans-offers&quot;&gt;Subscriptions, base plans, offers&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;subscriptions&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;--output&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;table&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;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;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;--subscription-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;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;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;--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;/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;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;The last one is the moment you realize a spreadsheet isn’t your friend anymore.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;in-app-products-one-time-purchases&quot;&gt;In-app products (one-time purchases)&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;iap&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;--output&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;table&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;iap&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;premium_upgrade&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;reviews&quot;&gt;Reviews&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;# Recent reviews&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;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;--output&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;table&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;# Only 1-2 star reviews from the last 14 days that mention &quot;login&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;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;--filter&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;rating&amp;#x3C;=2 AND text CONTAINS &apos;login&apos;&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;--time-range&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LAST_14_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;Yes, that filter syntax works. Great for triaging support-worthy issues without opening the web UI.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;testers-closed-testing-tracks&quot;&gt;Testers (closed testing tracks)&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;testers&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;--track&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;alpha&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;testers&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;--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;alpha&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--email&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tester@example.com&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;in-app-messaging-real-time-developer-notifications&quot;&gt;In-app messaging: real-time developer notifications&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;pubsub&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;topic&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;# which Pub/Sub topic Play publishes to&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Useful when you’re setting up server-side purchase acknowledgment against real-time notifications.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;reports-financial-statistics&quot;&gt;Reports (financial, statistics)&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;# List available financial reports (earnings, sales, taxes)&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;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;/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;# Download last month&apos;s earnings CSV from GCS&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;\&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;--report-type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EARNINGS&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;--year&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2026&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--month&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;6&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;./reports/&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;editing-safely-with-edit-sessions&quot;&gt;Editing safely with edit sessions&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Play Console API changes go through an &lt;strong&gt;edit session&lt;/strong&gt; — a transactional workspace that either fully commits or gets thrown away. gplay wraps this:&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;# Start a session, save the ID&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;EDIT_ID&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;edit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;begin&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;-r&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;.id&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;# Make multiple changes referencing $EDIT_ID&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;listings&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;--edit-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$EDIT_ID&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--locale&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;en-US&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--title&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;New Title&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;listings&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;--edit-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$EDIT_ID&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--locale&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fr-FR&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--title&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Nouveau titre&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;# Commit atomically&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;edit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;commit&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;--edit-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$EDIT_ID&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;# Or throw everything away&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;edit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cancel&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;--edit-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$EDIT_ID&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Most &lt;code dir=&quot;auto&quot;&gt;gplay&lt;/code&gt; commands manage the edit session for you internally — you only need to think about it when you want atomicity across multiple writes. If Claude Code or another AI agent is making a multi-step update, prompt it to use an explicit edit session and cancel-on-error.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;everything-as-json-by-default&quot;&gt;Everything as JSON, by default&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Every command above returns minified JSON if you don’t add &lt;code dir=&quot;auto&quot;&gt;--output&lt;/code&gt;. Perfect for &lt;code dir=&quot;auto&quot;&gt;jq&lt;/code&gt;, agent parsing, storing snapshots for diffing.&lt;/p&gt;
&lt;p&gt;Table output (&lt;code dir=&quot;auto&quot;&gt;--output table&lt;/code&gt;) is human-readable. Markdown (&lt;code dir=&quot;auto&quot;&gt;--output markdown&lt;/code&gt;) is what you’d paste into a PR description.&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;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;--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 reference at &lt;a href=&quot;https://playconsolecli.com/reference/&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;/reference/&lt;/code&gt;&lt;/a&gt;. Every command supports &lt;code dir=&quot;auto&quot;&gt;--help&lt;/code&gt; — that’s usually faster than searching docs.&lt;/p&gt;
</content:encoded><category>tutorial</category><category>listings</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>Your first 5 commands after gplay setup --auto</title><link>https://playconsolecli.com/blog/first-commands-after-gplay-setup/</link><guid isPermaLink="true">https://playconsolecli.com/blog/first-commands-after-gplay-setup/</guid><description>You installed gplay and ran gplay setup --auto. What next? Here are the five commands to run first — to prove the auth works, understand your app&apos;s current state, and start automating things.</description><pubDate>Sun, 05 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;You just ran:&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;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; installed &lt;code dir=&quot;auto&quot;&gt;gcloud&lt;/code&gt; if missing, enabled the Google Play Android Publisher API in a fresh GCP project, created a service account, downloaded its key, and (assuming you accepted its Play Console invite) hooked it into your developer account.&lt;/p&gt;
&lt;p&gt;Now what? Here are the five commands to run before you do anything else. Together they prove auth works, show you your app’s current state, and set you up to automate the rest.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;div&gt;&lt;h2 id=&quot;1-gplay-auth-doctor--sanity-check-the-setup&quot;&gt;1. &lt;code dir=&quot;auto&quot;&gt;gplay auth doctor&lt;/code&gt; — sanity-check the setup&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;auth&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;doctor&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Runs every check that could bite you later:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Does the service-account JSON parse?&lt;/li&gt;
&lt;li&gt;Does its email have Play Console developer access?&lt;/li&gt;
&lt;li&gt;Does the linked GCP project have the Android Publisher API enabled?&lt;/li&gt;
&lt;li&gt;Do all the requested scopes work with the current key?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If everything’s green, you’re wired up. If not, &lt;code dir=&quot;auto&quot;&gt;--fix --confirm&lt;/code&gt; tries to auto-remediate:&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;auth&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;doctor&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--fix&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--confirm&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;2-gplay-apps-list--see-what-you-can-touch&quot;&gt;2. &lt;code dir=&quot;auto&quot;&gt;gplay apps list&lt;/code&gt; — see what you can touch&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;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;--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;Every app the service account has access to. If you expected an app to show up and it doesn’t, the service account isn’t invited to it yet — do that in Play Console → Users and Permissions.&lt;/p&gt;
&lt;p&gt;For scripting, drop &lt;code dir=&quot;auto&quot;&gt;--output table&lt;/code&gt; — the JSON is what agents want.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;3-gplay-tracks-list--where-does-each-app-live&quot;&gt;3. &lt;code dir=&quot;auto&quot;&gt;gplay tracks list&lt;/code&gt; — where does each app live?&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Pick the app you’re focused on:&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;--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;Shows every track (internal, alpha, beta, production, custom closed-testing) with the version code and rollout fraction currently live in each.&lt;/p&gt;
&lt;p&gt;This is the map you’ll return to every time you’re about to promote or roll back.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;4-gplay-listings-list--what-does-the-store-look-like-right-now&quot;&gt;4. &lt;code dir=&quot;auto&quot;&gt;gplay listings list&lt;/code&gt; — what does the store look like right now?&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;listings&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;--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;Every localized store listing — title, short description, full description length, whether screenshots and graphics are set. If your app supports 10 locales, all 10 rows are here.&lt;/p&gt;
&lt;p&gt;To fetch a single locale’s full 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;listings&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;--locale&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;en-US&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;5-gplay-vitals-crashes-query--how-healthy-is-the-app&quot;&gt;5. &lt;code dir=&quot;auto&quot;&gt;gplay vitals crashes query&lt;/code&gt; — how healthy is the app?&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;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;--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;Top crash clusters over the last 7 days. If you were about to promote a release, this tells you whether the current production build is healthy enough to build on.&lt;/p&gt;
&lt;p&gt;Pair it with:&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;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;For startup latency baseline.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;bonus-the-two-flags-to-memorize&quot;&gt;Bonus: the two flags to memorize&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Once you know your way around, these two flags change everything:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code dir=&quot;auto&quot;&gt;--dry-run&lt;/code&gt;&lt;/strong&gt; — every write command intercepts the HTTP request and logs it to stderr without executing. Safe preview:&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;listings&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;--dry-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;--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;--locale&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;en-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;--short-description&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;New tagline&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;&lt;code dir=&quot;auto&quot;&gt;--paginate&lt;/code&gt;&lt;/strong&gt; — every list command auto-fetches all pages:&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;--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;--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;Together they cover 90% of what you’ll do.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;bonus-hand-it-off-to-an-ai-agent&quot;&gt;Bonus: hand it off to an AI agent&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Now that auth works and you know your app’s shape, install the Agent Skills:&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 in Claude Code (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;What can you tell me about &lt;code dir=&quot;auto&quot;&gt;com.example.app&lt;/code&gt;? Summarize current tracks, active locales, top crashes, and any subscriptions.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The agent chains the five commands above (plus &lt;code dir=&quot;auto&quot;&gt;gplay subscriptions list&lt;/code&gt;) and gives you a one-paragraph brief. First-day productivity.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;what-comes-next&quot;&gt;What comes next&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Depending on why you installed gplay:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Automating releases?&lt;/strong&gt; Read &lt;a href=&quot;https://playconsolecli.com/blog/release-android-app-from-terminal/&quot;&gt;How to release an Android app to Google Play from the terminal&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Migrating from Fastlane?&lt;/strong&gt; Read &lt;a href=&quot;https://playconsolecli.com/blog/migrate-from-fastlane-supply-to-gplay/&quot;&gt;Migrate from Fastlane supply to gplay in an afternoon&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Managing subscriptions?&lt;/strong&gt; Read &lt;a href=&quot;https://playconsolecli.com/blog/revenuecat-google-play-setup-with-gplay/&quot;&gt;Set up a Google Play subscription with RevenueCat and gplay CLI&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitoring vitals in CI?&lt;/strong&gt; Read &lt;a href=&quot;https://playconsolecli.com/blog/monitor-google-play-crashes-anrs-from-terminal/&quot;&gt;Monitor Google Play crashes and ANRs from the terminal&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Full command reference at &lt;a href=&quot;https://playconsolecli.com/reference/&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;/reference/&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>onboarding</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>