storebound/skills/app-store-promotion/SKILL.md
CRT b63f2fdfbc Store operations: TestFlight wait / expire, IAP review submissions, listing pulls, release notes per language, offers, custom product pages, in-app events, reports, triage, keyword ranks, AdAttributionKit scan
- apple testflight wait --build N (progress, timeout, exit 0/1/7) and expire --keep N | --older-than D (dry run, asks)
- apple iap-submit: IAP / subscription / group versions into a review submission; first of a type only with the app
  version (--with-version), --no-submit keeps the draft; read-only MCP plan
- apple listing pull, play listing pull (diff, --write), catalog pull (config proposal, never rewritten)
- whats-new --languages / check / apply: one file per language, agent or hook translation, per-store limits
- apple offer-codes, win-back, promoted, preorder; Play pre-registration as a guide (no API)
- apple cpp and apple events: list, create, upload, submit (asks)
- reports: Apple sales / finance, Play earnings / sales into .storebound/reports with summary and CSV
- triage: TestFlight crash / feedback and Play error reports grouped by signature, device, OS
- aso rank: iTunes Search API ranks and competitors, rate limited
- ads scan: AdAttributionKit / SKAdNetwork Info.plist and source checks
- tests against recorded, sanitized answers; skills, README, CHANGELOG
2026-10-11 23:58:17 +08:00

7.9 KiB

name description
app-store-promotion App Store promotion tools for a mobile app or game through the App Store Connect API - offer codes for one-time purchases, win-back offers for lapsed subscribers, promoted in-app purchases on the product page, pre-orders per territory, custom product pages (screenshots per audience, deep links) and in-app events, with Apple's limits; Google Play pre-registration as a console guide (no API). Use when the user wants promo codes or discounts for IAPs, to win back subscribers, to feature purchases on the product page, a pre-order or pre-registration launch, ad-specific product pages, or an in-app event.

App Store promotion (offers, promoted IAPs, pre-orders, custom product pages, in-app events)

Everything here is App Store Connect API (cli/lib/offers.ts, cli/lib/cpp-events.ts cite the endpoints and Apple's Help pages). Each command has --dry and --json. What customers can see or redeem is the owner's step: those commands show the dry run, then ask in the terminal, and none of them is an MCP tool. MCP has the read-only and nothing-public parts (apple_cpp_list | create | upload, apple_events_list | create | upload, apple_preorder_status).

Offer codes for one-time purchases

Config, on the product (consumable, non-consumable, non-renewing):

products: [{ id: 'com.example.app.pro', type: 'non_consumable', usd: 9.99, listings: { ... },
  offerCodes: [{ name: 'Launch week', eligibility: ['NON_SPENDER'], usd: 4.99,          // or free: true
    customCodes: [{ code: 'LAUNCH26', count: 1000, expires: '2026-12-31' }],
    oneTimeCodes: [{ count: 500, expires: '2027-01-31' }] }] }],
  • storebound apple offer-codes --dry, then without --dry: creates missing campaigns (by name) with prices for every territory (the USA price point of usd and its equalizations, or territories: ['USA', 'DEU']). A campaign alone lets nobody redeem anything.
  • Codes only with --codes (asks): custom codes (anyone who has the code, up to 25,000 redemptions, letters and digits up to 64) and one-time-use batches (500-25,000, expiry at most 6 months). One-time codes are saved to .storebound/offer-codes/ (gitignored): treat them like vouchers, never paste them into chat, issues or commits.
  • Apple's limits (Help, "Create offer codes for In-App Purchases"): 10 active offers per app, 1 million codes per app per quarter; eligibility NON_SPENDER (never bought in the app), ACTIVE_SPENDER (bought in the last 30 days), CHURNED_SPENDER (bought more than 30 days ago). Needs iOS 16.3+. Subscription offer codes are offerCodes on a subscription (storebound apple subscriptions --codes, store-subscriptions skill).
  • Play promo codes have no API: Play Console > Monetize > Promo codes.

Win-back offers (subscriptions)

winBack: [{ id: 'come-back-3m', type: 'pay-up-front', duration: 'P3M', usd: 9.99,
  paidMonths: 6, lapsedMonths: { min: 2, max: 12 }, waitMonths: 6, start: '2026-11-01', end: '2026-11-30',
  priority: 'high', promote: false }],

storebound apple win-back --dry, then the owner runs it (asks). Rules (Help, "Set up win-back offers"): the subscription must be approved; paid at least 1-24 months or 3, 4, 5 years; lapsed between min and max months (1-24, both required); wait between offers 2-24 months; start at the earliest tomorrow and run at least 3 days; at most 5 running per storefront and 350 per subscription. Reference name, offer id and price cannot change later; the offer id must not be reused in the group. An offer goes live by itself on its start date (Manage Subscriptions, the in-app offer sheet on iOS 18+, the App Store with promote: true and an approved subscription image).

Promoted in-app purchases

apple: { promoted: { products: ['com.example.app.pro', 'com.example.app.coins_100'] } } (order = display order, at most 20). storebound apple promoted --dry shows the status and the plan: missing ones are created disabled, the order is set. --enable switches them on (asks; they show on the product page once approved). Each needs its promotional image in App Store Connect, and the app must handle purchases that start on the App Store (StoreKit PurchaseIntent; StoreKit 1 paymentQueue(_:shouldAddStorePayment:for:)). Without apple.promoted the command only prints the status.

Pre-orders

  • storebound apple preorder status (MCP: apple_preorder_status): per territory available, release date, pre-order on, publish date, content status.
  • storebound apple preorder enable --release-date 2026-12-01 [--territories USA,DEU] --dry, then for real (asks). Apple: a version must be approved first; the release date is 2-180 days ahead for a new app (up to 365 days for an existing app in a new region); a territory where the app is already released cannot go on pre-order.
  • storebound apple preorder end [--territories USA] ends the pre-order and releases the app there now (asks).
  • Google Play pre-registration has no API (the androidpublisher v3 reference lists no such resource): Play Console

    Grow users > Pre-registration (set it up on a testing / production track that is not yet available, optional pre-registration reward as a one-time product). Pre-registration ads: Google Ads.

Custom product pages

apple: { customProductPages: [{ name: 'Students', variant: 'students', deepLink: 'https://example.com/open/students',
  promotionalText: { en: 'Class notes that sync.', de: '...' } }] },
  1. Screenshots: storebound shots render --variant students (a shots.variants entry), store-screenshots skill.
  2. storebound apple cpp create --dry, then for real (MCP apple_cpp_create): the page, its first version and a localization per App Store locale.
  3. storebound apple cpp upload --name Students --dry (MCP apple_cpp_upload): the variant's renders per locale, MD5-idempotent.
  4. Owner: storebound apple cpp submit --name Students (asks; App Review, independent of an app update), after approval storebound apple cpp visible --name Students (asks). apple cpp list shows each page's URL for ads. Limits: 70 pages per app; promotional text 170; the deep link works from iOS / iPadOS 18 once approved. Screenshot sets are deprecated in API 4.5.1 in favour of the App Asset Library; they still work and storebound uses them.

In-app events

apple: { events: [{ referenceName: 'Winter Challenge', badge: 'CHALLENGE', purpose: 'KEEP_ACTIVE_USERS_INFORMED',
  schedule: { publishStart: '2026-12-01T00:00:00Z', eventStart: '2026-12-10T00:00:00Z', eventEnd: '2026-12-20T00:00:00Z' },
  listings: { en: { name: 'Winter Challenge', shortDescription: 'Finish ten lists before New Year', longDescription: '...' } },
  media: { card: 'store/events/winter/{lang}-card.png', details: 'store/events/winter/{lang}-details.png' } }] },

storebound apple events create --dry / upload --name ... (MCP apple_events_create / apple_events_upload), then the owner runs storebound apple events submit --name ... (asks; once approved the event publishes by its schedule). Limits (Help, "Offer In-App Events"): reference name 64, event name 30, short description 50, long description 120; 15 minutes to 31 days; published at most 14 days before it starts; 10 published and 15 approved events per app. Media: event card 16:9 (1920x1080 to 3840x2160), details page 9:16 (1080x1920 to 2160x3840), PNG or JPEG; storebound checks the size before uploading. Event text is held to the same honesty rules as the listing: a real event in the app.

Gotchas

  • Free offer prices (offer codes, win-back) are sent as territories without a price point, as the API schema allows; not yet verified against a live account. A refusal names the field: create that one offer in App Store Connect.
  • Everything with a start date goes live without another click: read the dates with the owner.
  • No manipulative offers: no fake scarcity in event text, no misleading "free" when a purchase is needed (purchaseRequirement says it).