Skip to main content
On this page

Publish an iOS App to the App Store from Windows with the kxstore Command Line

Every kxstore subcommand and option: sign in with an App Store Connect API key, register bundle ids, issue certificates and profiles, upload the .ipa, import metadata and screenshots, submit for review, TestFlight, in-app purchases and pricing — or run the whole release with one command from a script, CI or an AI agent.

Last updated

kxstore commands take the form kxstore <command> [options] and cover the App Store Connect side of a release: sign in with an API key, register bundle ids, issue certificates and profiles, upload the .ipa, import metadata and screenshots, submit for review, distribute on TestFlight, manage in-app purchases and pricing; release runs the whole sequence as one command. It is installed with KXBuild (kxbuild doctor installs it) and works the same on Windows, Linux and macOS. Projects and builds belong to kxbuild, the phone to kxdevice. Every command has --help; the global --json flag prints one JSON object with a fixed shape for scripts and AI agents.

> kxstore --version

Which commands does kxstore have?

CommandWhat it does
login / whoami / accountSign in with an App Store Connect API key, show the account and team
init / doctorWrite kxstore.yaml; read-only check of account, team, bundle id, app, package and listing
releaseRun the whole release from kxstore.yaml, resumable after a failure
bundle / cert / device / profile / signingBundle ids and capabilities, certificates, test devices, provisioning profiles
app / version / buildApp records, App Store versions, build upload and processing
metadata / screenshot / preview / assetStore listing text, screenshots, app previews, the asset library
review / testflightSubmit for review, status and withdrawal, TestFlight groups and testers
iap / pricing / game-centerIn-app purchases, price and territories, Game Center leaderboards
skill / exit-codesSkill file for AI agents, the exit code table

Three things have no endpoint in Apple's App Store Connect API and are done once per app on the website: creating the App record in App Store Connect, filling in App Privacy (the privacy labels) and accepting agreements (such as the Paid Apps agreement). App Clip bundle ids are also registered only on the developer website. When a command reaches one of these steps it returns capability_not_supported (exit code 5) and says where to do it; run the same command again afterwards. Every later version then needs no manual step at all.

How do I sign in to App Store Connect with kxstore login?

kxstore login --api-key-file <AuthKey.json> [--alias name] · kxstore login --api-key-id <Key ID> --api-issuer-id <Issuer ID> --api-key-path <AuthKey.p8>

In App Store Connect open Users and Access → Integrations → Team Keys, generate a key (role App Manager or Admin), download AuthKey_<Key ID>.p8 and note the Issuer ID shown on the page. Sign in once: the key is checked against Apple and stored encrypted on this computer, and every later command authenticates on its own. There is no Apple ID password and no two-factor code, so scripts, CI jobs and AI agents run unattended.

> kxstore login --api-key-id ABC123DEF4 --api-issuer-id 57246542-96fe-1a63-e053-0824d011072a --api-key-path .\AuthKey_ABC123DEF4.p8 --alias ci
> kxstore whoami
  • --api-key-file takes a JSON key file with key_id, issuer_id and key.
  • In CI, skip the login step: set KXSTORE_API_KEY_ID, KXSTORE_API_ISSUER_ID and KXSTORE_API_KEY_PATH (or KXSTORE_API_KEY_P8 with the .p8 contents, or KXSTORE_API_KEY_FILE pointing at the JSON) and any command signs in when it needs to.
  • Name several accounts with --alias and pick one with --account <alias> or KXSTORE_ACCOUNT; account list shows the stored accounts, logout removes the current one and its key.
  • whoami prints the key's Apple ID, team name and team id; team list does the same.

How do I use kxstore init and doctor?

kxstore init --bundle-id <bundle id> --name <app name> [--from-app] · kxstore doctor [--for-release]

Run init in the project folder to write kxstore.yaml: the app's bundle id, name and primary locale, the certificate and profile types to prepare, the version, the .ipa path, the metadata folder and whether to submit. Later commands find the file by walking up from the working directory, so most options can be left out; --from-app also exports the listing that is already online. doctor reads only: it checks the account, team, bundle id, App record, the .ipa (version, signing team, profile type) and the metadata folder one by one, --for-release applies the rules for a submission, and every failed check comes with the fix.

> kxstore init --bundle-id com.example.myapp --name "My App"
> kxstore doctor --for-release

How does kxstore release publish in one command?

kxstore release [--dry-run] [--no-submit] [--skip step,…] [--only step,…] [--from step]

It follows kxstore.yaml through preflight → bundle → certs → profiles → app → version → upload → metadata → screenshots → build-wait → compliance → review-info → submit, plus the TestFlight, in-app purchase, pricing and Game Center steps when they are configured. Every step is idempotent: an existing bundle id, app or version is skipped, a valid certificate is reused, a build number that is already uploaded is not uploaded again, a submitted version is not submitted twice. Progress is written to .kxstore/release-state.json in the project; after a failure, run the same command again and it continues from the failed step.

> kxbuild build .\MyApp.xcodeproj -c Release --export app-store
> kxstore release --dry-run
> kxstore release --no-submit
  • --dry-run reads only and reports what each step would do; --no-submit does everything up to the submission, with the build attached to the version, so you submit later with review submit or on the website.
  • Publishing is not part of the pipeline: with release_type MANUAL, run kxstore version publish 1.2.0 once App Review has approved the version.
  • With --json every step prints one JSON event; a failed step carries retryable and a next_action you can run as is.

How do I create certificates and profiles on Windows?

kxstore bundle create · kxstore cert create · kxstore device add · kxstore profile create · kxstore signing

The private key and the CSR are generated on your computer; after Apple issues the certificate the .p12 is saved to the --out folder. profile create picks the certificate and devices itself, reuses a matching profile and rebuilds an expired one. Put the .p12, its password file and the .mobileprovision into the project's keychain folder and kxbuild signs with them; see Certificates and signing.

> kxstore bundle create com.example.myapp --name MyApp
> kxstore cert create --type IOS_DISTRIBUTION --out .\keychain
> kxstore device add 00008030-000A1B2C3D4E5F67 --name "My iPhone"
> kxstore profile create --name "MyApp AppStore" --type IOS_APP_STORE
  • Certificate types: IOS_DEVELOPMENT for running on your devices, IOS_DISTRIBUTION for the App Store and Ad Hoc; profile types: IOS_APP_DEVELOPMENT, IOS_APP_STORE, IOS_APP_ADHOC. cert types lists them all.
  • The .p12 password comes from KXSTORE_P12_PASSWORD or from the variable named by --p12-password-env; it never appears on the command line.
  • device import devices.txt registers test devices in bulk; bundle capabilities shows and enables push notifications, iCloud and the other capabilities.
  • kxstore signing prepares the bundle id, certificates and profiles listed in the signing section of kxstore.yaml in one go and writes signing/manifest.json.

How do I upload the .ipa and wait for processing?

kxstore build upload <ipa> [--wait] [--timeout 30m] · kxstore build list / wait / compliance · kxstore app list / get · kxstore version create / set / publish

The upload goes through Apple's build upload API, so neither Transporter nor an AppStoreInfo.plist is needed. Apple validates the package during the upload: a wrong signature or a duplicate build number fails right there, so increase CFBundleVersion before each upload. After the upload Apple keeps processing for a few minutes; --wait returns once the build is processed, and the build then appears on the TestFlight tab.

> kxstore build upload .\build\MyApp.ipa --wait
> kxstore build list --app com.example.myapp
> kxstore version create 1.2.0 --build latest
  • --app takes a bundle id or an App Store Connect app id and defaults to app.bundle_id in kxstore.yaml; an upload reads the bundle id from the .ipa and finds the app itself.
  • build compliance --encryption no answers the export compliance question; version set --build 42 attaches a build to the version; version publish releases an approved version.
  • The App record itself is created on the App Store Connect website; app list and app get show it, app set changes the content rights declaration.

How do I import listing text and screenshots?

kxstore metadata export / import / validate · kxstore screenshot upload --locale <locale> <folder> · kxstore preview upload · kxstore asset place

The listing is a folder: config.yaml holds categories, the age rating and the review information, metadata/<locale>/ holds one text file per field (name.txt, subtitle.txt, description.txt, keywords.txt, release_notes.txt, privacy_url.txt, …) and screenshots sit in subfolders per device. Export first to get what is online, edit the files, then import; import writes only the fields whose files exist and are not empty, and skips values that have not changed. Screenshots are sorted into the 6.7-inch, 6.5-inch, iPad and other slots by their pixel size; --replace uploads the new set before deleting the old one.

> kxstore metadata export --dir .\metadata-bundle
> kxstore metadata import --dir .\metadata-bundle
> kxstore screenshot upload --locale en-US .\screenshots\en-US --replace
  • metadata validate checks text limits, locale codes, screenshot sizes and file names offline.
  • asset upload and asset place use the asset library: an image is uploaded once and then placed on any locale, custom product page or product page test; iPhone Duo screenshots and the product page header and search result assets go through it too.
  • custom-page manages custom product pages, page-test manages product page A/B tests.

How do I submit for review and distribute on TestFlight?

kxstore review submit / status / cancel · kxstore review info set · kxstore testflight distribute --build <number> --group <group>

review submit sends the current version together with the in-app purchases, leaderboards and other items on the draft; it is idempotent, so a submitted version is not submitted again. When Apple refuses the submission the reasons are listed one by one (missing screenshots, missing privacy policy URL, unpublished privacy labels, …): fix them and run it again. review status shows the state of the version and its submissions, review cancel withdraws a submission that is still waiting. testflight distribute hands a build to a tester group; testflight group and testflight tester manage groups and testers.

> kxstore review submit --yes
> kxstore testflight distribute --build 42 --group "Internal QA"

How do I manage in-app purchases, pricing and Game Center?

kxstore iap list / create / set / export / import · kxstore pricing get / set / territories · kxstore game-center status / sync

In-app purchases, with their localized text and review screenshots, are exported and imported as a folder too; pricing set --free or --price 0.99 sets the price and --territories the countries; game-center sync keeps leaderboards and their localized names in step with kxstore.yaml. On a first release the price and the in-app purchases must be submitted together with the version, which is why the release pipeline runs them before submit.

How does an AI agent publish with kxstore?

kxstore skill --install user · kxstore exit-codes

The skill command writes a skill file with the rules of use (always --json, keys never on the command line, which actions not to take on its own), the exit codes and retry policy, the common flows and the full command index: --install user puts it in ~/.claude/skills for every project, --install project only in the current one, and --format agents prints text to append to AGENTS.md. Projects created with kxbuild project create already describe the upload and release commands in their AGENTS.md.

> kxstore skill --install user
> kxstore skill --format agents >> AGENTS.md
  • Rules for the agent: always pass --json --yes; keys only through --api-key-file or environment variables; a status of skipped or reused counts as success; when retryable is true, wait and run next_action again; on capability_not_supported hand the website step to the person.
  • Let the agent run doctor --for-release and release --dry-run first, then release --no-submit once the plan looks right; whether to submit stays your decision.

FAQ

What is the difference between kxstore and Transporter?

Transporter only uploads the package. kxstore also handles certificates, profiles, metadata, screenshots, review submission, TestFlight, in-app purchases and pricing, works the same on Windows, Linux and macOS, and every command can be driven by a script or an AI agent.

Which steps must be done on the App Store Connect website?

Creating the App record, filling in App Privacy (the privacy labels), accepting agreements, and registering an App Clip bundle id. Apple's API has no endpoint for them; each is done once per app, and every later version runs entirely from the command line.

The upload says the build number already exists. What now?

App Store Connect refuses a repeated build number within one version. Increase CFBundleVersion in the project (or override it with kxbuild build's -D CURRENT_PROJECT_VERSION=<new number>), build again and upload; the release pipeline skips the upload step when it finds the build number already uploaded.

Does kxstore need a license?

Yes, the same license as kxbuild: once kxbuild license activate or kxbuild license trial has run on this computer, kxstore works; exit-codes, skill and help need no license.

Next steps