appeeky.spec.yaml # app, stores, targets, and everything Spec reads or watches
appeeky/
store/
app-store/<locale>.yaml # one file per App Store locale (en-US, de-DE, ...)
google-play/<language>.yaml # one file per Play language (en-US, de-DE, ...)
releases/<id>.yaml # one file per push: what changed, why, and its checks
experiments/<id>.yaml # growth experiments outside the listing
screenshots/app-store/<locale>/<display>/ # pulled App Store screenshots
reviews/ # reviews (JSONL per month), summary.md, replies/ to send
competitors/ # one file per competitor, changes.md
visibility/ # what AI assistants answer, per storefront
seo/ # Search Console and Google ranks for the website
search-ads/ # Apple Search Ads on your own key
ads/<date>.yaml # saved by ads --save
social/ # social accounts, posts, drafts/ to send
ugc/ # creator personas, clips, briefs/ to make
briefs/<date>.md # the weekly co-founder review
category.csv # rank in the category chart, one row a day
loops.md # every change being measured (written by status)
playbook.md # what worked and what did not
data/ # only with data.commit: true
.appeeky/ # cache, private data and rendered media, keep out of git
appeeky.spec.yaml
version: 1
app:
name: Sleep Sounds
primaryLocale: en-US
stores:
appStore:
appId: "1234567890" # numeric id or bundle id
via: auto # auto | local | hosted
account: my-team # optional: saved appstore CLI account
searchAds: # optional: Apple Search Ads on your own key
orgId: "1234567"
campaigns: ["987654"]
googlePlay:
packageName: com.example.sleep
via: auto
account: my-co # optional: saved gps CLI account
targets:
us: [sleep sounds, white noise, rain sounds, brown noise]
de: [einschlafgeräusche, weißes rauschen]
storefronts: # optional
us: [en-US, es-MX]
| Key | Meaning |
|---|---|
app.name | Display name, used in reports and as the brand when checking for repeated words |
app.primaryLocale | Default locale for suggest and new files |
stores.appStore.appId | App Store app id. A numeric id also enables rank checks |
stores.appStore.platform | IOS (default), MAC_OS, TV_OS or VISION_OS |
stores.googlePlay.packageName | Google Play package name |
stores.*.via | How to reach the store: local (your CLI and keys), hosted (Appeeky gateway), auto |
stores.*.account | Named account saved with appstore accounts add or gps accounts add; default: the CLI’s default account |
stores.appStore.searchAds.orgId | Apple Search Ads organization, when the key reaches more than one |
stores.appStore.searchAds.campaigns | Only these campaign ids; default: every campaign for this app |
targets | Searches the app should be found for, per storefront (two-letter country code) |
storefronts | Which App Store listing locales a storefront indexes. Leave it out to use Appeeky’s defaults (for example, the US also reads es-MX) |
Everything else in the spec
All optional. Each block is explained on the page that uses it.competitors:
- name: Calm Night
appStore: "1234567891"
googlePlay: com.calmnight.app
ads:
meta:
adAccount: act_1234567890 # optional: defaults to the account picked in Appeeky
campaigns: ["120210000000001"]
tiktok:
advertiser: "7000000000000" # optional
campaigns: ["1790000000000"]
category:
googlePlay: HEALTH_AND_FITNESS # appStore: "6013" to override the genre
chart: top-free # top-free | top-paid | top-grossing
countries: [us, gb]
aiVisibility:
store: app-store # which app to watch when it is on both stores
countries: [us]
models: [chatgpt, claude] # --local: only these assistants
aliases: [Sleep Sounds app]
intents:
us:
- label: fall asleep to rain sounds
prompts:
- what app plays rain sounds to help me sleep
web:
url: https://sleepsounds.app/
searchConsole: sc-domain:sleepsounds.app
keywords:
us: [rain sounds, white noise app]
countries: [us]
device: mobile # mobile | desktop
attribution:
via: self # self | hosted
url: https://analytics.sleepsounds.workers.dev
app: sleep-sounds # or { app-store: …, google-play: … }
data:
commit: false
personas:
- id: sleepless-parent
name: Sleepless parent
description: Up at night with a newborn
searches: [baby sleep sounds, white noise for baby]
| Key | Meaning | See |
|---|---|---|
competitors | Apps you compete with: name, numeric appStore id and/or googlePlay package. competitors discover --add <n> fills it | Commands |
ads.meta, ads.tiktok | The Meta and TikTok campaigns that promote this app; the ad account or advertiser defaults to the one picked in Appeeky. Apple Search Ads needs nothing here | Experiments |
category.appStore | App Store genre id; read from the app when left out | Commands |
category.googlePlay | Google Play category, needed for the Play chart | |
category.chart | top-free, top-paid or top-grossing; default top-paid for a paid App Store app, top-free otherwise | |
category.countries | Storefronts to record; default: the storefronts under targets | |
aiVisibility.store | app-store or google-play, when the app is on both; default App Store | AI answers |
aiVisibility.countries | Storefronts to watch; default: the first under targets | |
aiVisibility.intents | Per storefront: label (what people want, 8+ characters), optional description, and prompts (how they ask, 15+ characters each) | |
aiVisibility.models | Which of chatgpt, claude, gemini, perplexity a --local scan asks; default: every one with a key set | |
aiVisibility.aliases | Other names the assistants use for the app | |
web.url | The app’s website | Website |
web.searchConsole | Search Console property (sc-domain:example.com or https://example.com/); default: the url’s domain | |
web.keywords | Google searches to watch with seo ranks and seo keywords, per storefront; default: the store targets. Only the storefronts in web.countries are watched | |
web.countries | Storefronts to watch; default: the first under keywords or targets | |
web.device | mobile (default) or desktop results page | |
attribution | Appeeky Analytics: via: self with your server’s url and ATTRIBUTION_TOKEN, or via: hosted with APPEEKY_API_KEY; app is one id, or one per store | Commands |
data.commit | true writes data pull output to appeeky/data/ instead of the gitignored .appeeky/data/ | |
personas | Who the app is for: id, name, description, searches. Experiments, screenshots and UGC refer to them by id | Experiments |
Listing files
App Store,appeeky/store/app-store/en-US.yaml:
name: "Sleep Sounds: Rain & Ocean"
subtitle: White noise, fan, lullaby
keywords: insomnia,relax,meditation,storm,thunder,baby,nap,calm,brown,pink
promotionalText: New: brown noise mixes.
description: |
Fall asleep faster with ...
whatsNew: Bug fixes.
supportUrl: https://example.com/support
marketingUrl: https://example.com
appeeky/store/google-play/en-US.yaml:
title: Sleep Sounds - Rain & Ocean
shortDescription: White noise, rain and ocean sounds to fall asleep faster.
fullDescription: |
...
video: https://youtube.com/watch?v=...
- A field you leave out is not managed. It is never diffed or pushed. You can adopt the spec one field or one locale at a time.
""clears a field on the store.- Values are compared the way the stores store them: trailing spaces and line endings do not count as changes.
Limits
| Store | Field | Limit |
|---|---|---|
| App Store | name, subtitle | 30 characters |
| App Store | keywords | 100 characters |
| App Store | promotionalText | 170 characters |
| App Store | description, whatsNew | 4000 characters |
| Google Play | title | 30 characters |
| Google Play | shortDescription | 80 characters |
| Google Play | fullDescription | 4000 characters |
Folders under appeeky/
| Path | Written by | What is in it |
|---|---|---|
releases/<id>.yaml | push, verify | See Releases below |
experiments/<id>.yaml | experiment add, close, verify | Hypothesis, one metric, baseline, target, check date, result |
screenshots/app-store/<locale>/<display>/ | screenshots pull | Live App Store screenshots |
reviews/<store>/<YYYY-MM>.jsonl | reviews pull | One review per line: id, rating, title, body, territory or language, version, date, reply |
reviews/summary.md | reviews pull | Last 30 days against the 30 before, stars per version, low ratings without a reply, words users use that no listing has |
reviews/replies/ | reviews reply | Drafted replies; reviews push sends them |
competitors/<name>.yaml | competitors pull | Listing, version and notes, the 25 searches it ranks best for, where it ranks for your targets, recent low ratings |
competitors/changes.md | competitors pull | New titles, versions, rewritten descriptions, climbs into the top 10 for your targets |
visibility/<country>.yaml | visibility pull, visibility scan --local | Each intent winning, weak or missing, share of answers that name you per assistant, who is named instead, cited pages, fixes |
visibility/changes.md | visibility pull, scan --local | What moved since the last pull |
visibility/history.csv, visibility/answers/<country>.jsonl | visibility scan --local | One row per scan, intent and assistant; every answer |
seo/daily.csv, queries.csv, pages.csv, summary.md | seo pull | Search Console clicks and impressions per day, top queries and pages |
seo/ranks.csv, ranks.md, changes.md | seo ranks | Google positions of the site and the store page, AI Overview citations |
seo/keywords.csv | seo keywords | Volume, difficulty, CPC, intent, related ideas |
seo/domain.md, ranked-keywords.csv | seo domain | What the site ranks for, competing sites |
search-ads/daily.csv, keywords.csv, search-terms.csv, summary.md | search-ads pull | Spend, installs, taps and impressions per day; keywords; search terms that installed and are not targets |
ads/<date>.yaml | ads --save | Spend, installs and cost per install per channel |
social/accounts.yaml, posts.jsonl, summary.md | social pull | Connected accounts, recent posts and their results |
social/drafts/<name>.yaml | you, or the co-founder review | Posts to send after the merge; see Social posts |
ugc/personas.yaml, clips.jsonl | ugc pull | Appeeky creator personas and their clips |
ugc/briefs/<name>.yaml | you, or the co-founder review | Videos to make after the merge |
ugc/personas/<name>.yaml | you | A persona kept in the repo with its look images, for ugc generate --local |
briefs/<YYYY-MM-DD>.md | the co-founder review | What each specialist found, the change, what is for a person |
category.csv | category pull | Category chart rank, one row a day per storefront and store |
loops.md | status, push, verify, experiment | Every release and experiment being measured |
playbook.md | verify --playbook, experiment close --playbook, you | What worked and what did not |
data/ | data pull with data.commit: true | Analytics, ads and revenue CSVs |
.appeeky/ holds the keyword cache (cache/, 7 days), data/ (private business numbers from data pull), media/ (clips rendered with ugc generate --local) and proposal.md (the weekly review’s pull request text). Keep it out of git.
Releases
push writes one file per store per push to appeeky/releases/:
id: 2026-11-02T09-14-00Z-app-store
pushedAt: 2026-11-02T09:14:00.000Z
store: app-store
target: version 2.5.0 (prepare for submission)
via: local
note: Add brown noise to the US keyword field
changes:
- locale: en-US
field: keywords
before: insomnia,relax,meditation,storm
after: insomnia,relax,meditation,storm,brown
baseline:
at: 2026-11-02T09:13:40.000Z
source: Appeeky API
ranks:
us: { brown noise: 48, white noise: 12 }
checks:
- day: 7
verdict: won
improved: [{ country: us, keyword: brown noise, before: 48, after: 21 }]
dropped: []
regressions: []
verify adds the checks. Edit the note freely; leave the rest to the CLI.
Experiments
experiment add writes appeeky/experiments/<id>.yaml with a hypothesis, one metric, a baseline, a target and a check date. experiment close records the measured value and the result. See Experiments and screenshots.
Playbook
appeeky/playbook.md is plain Markdown. verify --playbook and experiment close --playbook append one line per result; you can write in it too. Agents read it before proposing a change, so failed ideas are not repeated.
