Going Live

Releasing with the asc CLI

The manual checklist walks you through shipping ShipThatApp with the App Store Connect web console and Xcode's Organizer. That works — but it's a maze of point-and-click steps that you can't diff, can't replay, and can't run from CI. The automation track does the same flow from your terminal with asc, a fast, scriptable App Store Connect CLI. This page is its foundation: install asc, authenticate it, and learn the handful of flags and idioms every later command leans on.

asc does not replace the manual guide — it automates it. Everything in the shipping checklist about production secrets, the AI proxy, account deletion, Restore Purchases, and the camera usage string still applies. asc is the layer that turns "archive, upload, submit" into a command you can run twice and get the same result.

What asc talks to

asc is a client for Apple's App Store Connect API. It authenticates with an API key you generate in App Store Connect — not your Apple ID password — so it never touches your personal credentials and is safe to run unattended in CI. The local build commands (asc xcode …) wrap xcodebuild and are macOS-only; everything else is pure API and runs anywhere.

Why the CLI Over the Web Console

The web console is fine for a one-off. The CLI earns its keep the moment you ship more than once:

  • Reproducible. A release becomes a script you can read, review, and re-run. No "wait, which dropdown did I change last time?"
  • Scriptable & CI-friendly. API-key auth and environment variables mean GitHub Actions or Xcode Cloud can archive, upload, and submit without a human clicking through Safari.
  • Fast. No page loads, no spinners — asc builds list answers in the time it takes a browser tab to open.
  • Diffable. Metadata, pricing, and localizations can live as files in your repo (see Metadata & localizations, authored alongside this track), so changes show up in pull requests.
  • Deterministic artifacts. asc xcode archive/export produce an .xcarchive and .ipa at exact paths you choose, which the upload and submit commands consume directly.

You'll still open the console occasionally — to eyeball a build in TestFlight or read a rejection — but the shipping path itself becomes code.

Install asc

The recommended install is via Homebrew, using the project's tap:

brew install rudrankriyam/tap/asc

There's also an install script for macOS and Linux:

curl -fsSL https://asccli.sh/install | bash

Windows users (and anyone who wants a pinned version) can download signed binaries from the GitHub releases page.

Confirm the binary works before you wire up auth:

asc version
asc --help

asc version should print the version you installed (this guide is written against 2.2.0). asc --help prints the full command map — auth and getting-started commands at the top, then app management, TestFlight & builds, review & release, monetization, and signing.

The local-build commands need a Mac

asc xcode archive and asc xcode export shell out to xcodebuild, so they only execute on macOS — but the subcommands are visible on every platform so docs and CI definitions stay consistent. The API commands (apps, builds, publish, status, metadata, …) work on any OS.

Create an App Store Connect API Key

asc authenticates with an App Store Connect API key — a three-part credential:

  • a Key ID (e.g. ABC123DEFG),
  • an Issuer ID (a UUID for your account),
  • and a private key file (AuthKey_XXXX.p8) that you download once.

Generate one at appstoreconnect.apple.com/access/integrations/api:

  1. Go to Users and Access → Integrations → App Store Connect API.
  2. Create a key with a role that covers what you'll do. App Manager is enough to manage builds, versions, and submissions; Admin is required for some account-wide operations.
  3. Note the Issuer ID (top of the page) and the new key's Key ID.
  4. Download the .p8 file immediately — Apple lets you download it exactly once. Store it somewhere safe (e.g. ~/.asc/AuthKey_ABC123.p8); if you lose it you must revoke the key and make a new one.

Treat the .p8 like a password

The private key grants programmatic access to your App Store Connect account. Never commit it to git, never paste it into a screenshot, and prefer a key scoped to the narrowest role that does the job. For CI, store it as an encrypted secret (see the env-var section below), not as a file in the repo.

Authenticate with asc auth login

asc auth login registers the key and stores it in your system keychain (with a config-file fallback), so commands keep working even if you later move the original .p8:

asc auth login \
  --name "ShipThatApp" \
  --key-id "ABC123DEFG" \
  --issuer-id "12345678-abcd-1234-abcd-123456789012" \
  --private-key ~/.asc/AuthKey_ABC123.p8 \
  --network

The flags, all confirmed via asc auth login --help:

  • --name — a friendly label for this profile (you can register several and switch between them with asc auth switch).
  • --key-id, --issuer-id, --private-key — the three parts of the credential.
  • --key-typeteam (default) or individual.
  • --network — validate the credential against the API as part of login, so you catch a bad key immediately instead of on your first real command.

For a CI runner or any headless machine where the keychain isn't available, store credentials in a config file instead:

asc auth login \
  --bypass-keychain \
  --name "ShipThatApp-CI" \
  --key-id "ABC123DEFG" \
  --issuer-id "12345678-abcd-1234-abcd-123456789012" \
  --private-key ~/.asc/AuthKey_ABC123.p8

--bypass-keychain writes to ~/.asc/config.json (restricted permissions); add --local to write a repo-scoped ./.asc/config.json instead, which takes precedence over everything else when present.

Verify: asc auth status and asc doctor

Two commands tell you whether auth is healthy. Run both after your first login:

asc auth status --validate
asc doctor

asc auth status shows the active profile and every stored credential; --validate performs a live network check on each one. asc doctor (an alias for asc auth doctor) runs a broader health check across keychain availability, config files, stored profiles, the private-key files, and environment variables — and will tell you exactly what's missing or misconfigured. If something's wrong, start here:

asc doctor --output json

A clean doctor is your green light

Before you run any release command, asc auth status --validate should report your profile as valid and asc doctor should come back clean. If doctor flags a keychain problem on a CI box, switch to the env-var or --bypass-keychain flow below.

Environment-Variable Auth for CI

In CI you don't run an interactive login — you inject the credential as environment variables. asc reads these as a fallback for any field a stored profile doesn't supply:

export ASC_KEY_ID="ABC123DEFG"
export ASC_ISSUER_ID="12345678-abcd-1234-abcd-123456789012"
export ASC_PRIVATE_KEY_PATH="$HOME/.asc/AuthKey_ABC123.p8"

If your CI provider can't write a file to disk, pass the key inline as base64 instead of a path:

export ASC_PRIVATE_KEY_B64="LS0tLS1CRUdJTi..."

The credential-resolution order is: the selected profile (keychain or config) first, then environment variables for any missing fields. Set ASC_STRICT_AUTH=true (or pass --strict-auth) if you want asc to fail when sources are mixed, rather than silently filling gaps from the environment — a good safety net in CI so you know exactly which credential is in play.

Set a default app with ASC_APP_ID

Almost every release command takes an --app flag — the App Store Connect app ID, bundle ID, or exact app name. Set it once as an environment variable and you can omit --app everywhere:

export ASC_APP_ID="1234567890"

With ASC_APP_ID exported, asc status and asc publish appstore (and the rest) fall back to it when --app is absent. The flag, when given, always wins.

Don't echo secrets into your shell history or logs

In CI, set ASC_PRIVATE_KEY_B64 (or the others) through your provider's encrypted-secrets mechanism, not an export in a committed script. Locally, prefer the keychain (asc auth login) so the key material never sits in a plaintext env var at all.

Output Formats, Confirmation, and Pagination

A few global idioms recur in every later page. Learn them once here.

--output table | json | markdown

asc is TTY-aware: when stdout is a terminal it prints human-friendly output, and when piped it defaults to JSON so you can parse it. Be explicit when it matters:

asc apps list --output table       # human-readable columns
asc apps list --output json --pretty   # parseable, indented
asc builds list --app "1234567890" --output markdown   # paste into a PR or doc

Use json (optionally --pretty) in scripts, table when you're reading at the keyboard, and markdown when you're pasting results into a pull request or these docs. You can set a personal default with ASC_DEFAULT_OUTPUT, but an explicit --output always wins.

--confirm gates destructive operations

Anything that mutates App Store Connect — submitting for review, applying a price change, deleting a tester — requires --confirm. Without it, asc plans the change and stops, which makes dry runs safe by default. The canonical ship command is the clearest example:

# Plans the publish + submit, but does nothing irreversible:
asc publish appstore --app "1234567890" --ipa app.ipa --version "1.0.0" --submit --dry-run

# Actually submits for review:
asc publish appstore --app "1234567890" --ipa app.ipa --version "1.0.0" --submit --confirm

--submit requires --confirm; many commands also accept --dry-run to preview the exact mutation sequence before you commit to it.

--paginate fetches every page

List commands page their results. When you need the whole set — every build, every tester — add --paginate:

asc builds list --app "1234567890" --paginate --output table

Discovering Commands

Two habits keep you accurate without guessing at flags:

  • --help on anything. Every command and subcommand documents its own flags and shows worked examples. When in doubt, append --helpasc publish appstore --help, asc xcode archive --help, asc builds list --help. This is the source of truth; don't assume a flag exists until --help confirms it.
  • asc docs ships embedded guides you can read offline:
asc docs list                # api-notes, reference, workflows
asc docs show workflows      # task-based recipes for release/TestFlight/Xcode
asc docs show api-notes      # API quirks: date formats, sandbox testers, finance

asc docs init (or asc init) can also drop an ASC.md quick reference into your repo so your whole team — and your AI assistant — works from the same command map.

The command spine you'll use to ship

The release path is just a few commands, each detailed on its own page in this track:

  • Build locally: asc xcode version … (set the version/build number), asc xcode archive, asc xcode export → a deterministic .ipa.
  • Ship it: asc publish appstore --app … --ipa … --version … --submit --confirm.
  • Watch it: asc status --app … for the release dashboard, asc builds list for processing state.
  • Stage without submitting: asc release stage … and asc validate … for a readiness report.

Read Building & uploading with asc next, then Submitting & monitoring with asc.

Previous
Shipping to the App Store