Back to blog
Blog PostMar 18, 2026

Monitoring the Xcode Cloud Situation

Rudrank Riyam
@rudrank
Monitoring the Xcode Cloud Situation

A friend of mine recently started using ASC CLI at a big Australian company. He started small, just to monitor the Xcode Cloud situation: which workflows are going through compute minutes, why builds are failing, and monitor the builds as each one takes at least half an hour.

We had a conversation about his setup and what he was using, and I realised I should write a proper guide on the Xcode Cloud side of the CLI. I have written about subscriptions and the review cycle, and this one is about what developers interact with every single day: their builds.

Enjoy the full walkthrough of everything asc cli can do with Xcode Cloud, from triggering builds to monitoring usage to figuring out which workflow is eating 60% of your quota.

https://github.com/rudrankriyam/App-Store-Connect-CLI

Setting Up

If you do not have asc installed yet:

brew install asc

You need an App Store Connect API key. Generate one under Users and Access > Integrations > App Store Connect API in the App Store Connect dashboard. Apple gives you three things: a Key ID, an Issuer ID, and a .p8 private key file. Hand those to asc:

asc auth login \
  --name "MyApp" \
  --key-id "ABC123" \
  --issuer-id "DEF456" \
  --private-key /path/to/AuthKey.p8

This stores the credentials in Keychain so you do not have to pass them on every subsequent command. If you manage multiple apps or teams, you can save different named profiles and switch between them.

For the experimental usage commands (asc web xcode-cloud), you also need an Apple Account web session. These commands take --apple-id and handle the session caching.

The Two Command Families

ASC CLI has two separate command trees for Xcode Cloud:

asc xcode-cloud: Uses the public App Store Connect API. This is stable, documented, and covers the core CI lifecycle: triggering builds, checking status, listing workflows, inspecting build actions, downloading artifacts, reviewing test results and issues.

asc web xcode-cloud: Uses Apple's private web API (the same one the App Store Connect website calls). This is experimental and covers what the public API does not: compute usage, plan quota, monthly and daily breakdowns, per-workflow usage stats, environment variables, and workflow management operations that Apple never exposed publicly.

Starting with Products and Workflows

Everything in Xcode Cloud starts with a product (your app) and the workflows attached to it. A product is the Xcode Cloud representation of your app whereas a workflow is the flow that builds, tests, archives, uploads and notifies relevant groups.

List your Xcode Cloud products:

asc xcode-cloud products --app "com.example.myapp"

List the workflows for an app:

asc xcode-cloud workflows --app "com.example.myapp"

This gives you the workflow IDs, names, and whether they are enabled. In table mode (--output table), it is easy to scan. To get details on a specific workflow:

asc xcode-cloud workflows get --id "WORKFLOW_ID"

And to see which repository a workflow is connected to:

asc xcode-cloud workflows repository --id "WORKFLOW_ID"

This is usually where you will start. Before you trigger anything, you want to know which workflow IDs exist, what they are called, and which repo they are attached to.

Triggering a Build

The run command is where most of the daily usage lives. You have two modes.

Standard mode — specify the workflow and what to build:

asc xcode-cloud run \
  --app "com.example.myapp" \
  --workflow "CI" \
  --branch "main"

You can reference the workflow by name (which requires --app for lookup) or by ID directly:

asc xcode-cloud run \
  --workflow-id "WORKFLOW_ID" \
  --git-reference-id "REF_ID"

You can also trigger a build against a pull request:

asc xcode-cloud run \
  --workflow-id "WORKFLOW_ID" \
  --pull-request-id "PR_ID"

And if you want to rerun an existing build run, there is a dedicated mode for that, optionally with a clean build:

asc xcode-cloud run --source-run-id "BUILD_RUN_ID" --clean

Waiting for a Build

You do not need to trigger a build and then write your own shell loop to poll it. The --wait flag turns run into a blocking call that polls until the build finishes:

asc xcode-cloud run \
  --app "com.example.myapp" \
  --workflow "Deploy" \
  --branch "release/1.0" \
  --wait \
  --poll-interval 30s \
  --timeout 1h

This is when you want to use it for CI scripts. You trigger the build, wait for it, and check the exit code. This is helpful to run as a subagent that can message back the main agent with the response.

Checking Build Status

If you triggered a build without --wait, or you want to check on something that started from Xcode or a webhook:

asc xcode-cloud status --run-id "BUILD_RUN_ID"

Add --wait to block until it finishes:

asc xcode-cloud status --run-id "BUILD_RUN_ID" --wait

The output gives you execution progress, completion status, issue counts, commit info, and timing.

Digging Into Build Runs

To see the history of build runs for a workflow:

asc xcode-cloud build-runs --workflow-id "WORKFLOW_ID"

Sort by newest first:

asc xcode-cloud build-runs --workflow-id "WORKFLOW_ID" --sort "-number"

Paginate through everything:

asc xcode-cloud build-runs --workflow-id "WORKFLOW_ID" --paginate

Each build run is made up of actions: Resolve Dependencies, Build, Archive, Test, Upload. To see what actually happened inside a build:

asc xcode-cloud actions --run-id "BUILD_RUN_ID" --output table

This is where you figure out which step failed. Each action has its own status, timing, and action type.

You can also inspect a specific action:

asc xcode-cloud actions get --id "ACTION_ID"

Test Results

If a test action ran:

asc xcode-cloud test-results list --run-id "BUILD_RUN_ID"

Or for a specific action:

asc xcode-cloud test-results list --action-id "ACTION_ID"

When you use --run-id, the CLI aggregates test results across all TEST actions in the run. You do not have to find the right action ID first.

Build Issues

Same pattern for issues (compiler warnings, errors, analyzer findings):

asc xcode-cloud issues list --run-id "BUILD_RUN_ID"

Artifacts

List the artifacts from a build run:

asc xcode-cloud artifacts list --run-id "BUILD_RUN_ID"

When you use --run-id here, it resolves from the ARCHIVE actions automatically. Then download:

asc xcode-cloud artifacts download --id "ARTIFACT_ID" --path ./build.zip

Artifact types include ARCHIVE, ARCHIVE_EXPORT, LOG_BUNDLE, RESULT_BUNDLE, TEST_PRODUCTS, and STAPLED_NOTARIZED_ARCHIVE. The log bundle is what you want when a build fails and you need to read the full Xcode build log.

Source Control

Xcode Cloud is tightly coupled to your SCM setup. You can inspect all of it. List your connected SCM providers:

asc xcode-cloud scm providers list

List repositories:

asc xcode-cloud scm repositories list

List branches and tags for a repository:

asc xcode-cloud scm repositories git-references --repo-id "REPO_ID"

List pull requests:

asc xcode-cloud scm repositories pull-requests --repo-id "REPO_ID"

These are useful when you need to find the git reference ID to trigger a build against a specific branch or tag programmatically.

Build Environment Versions

Check what macOS and Xcode versions are available for Xcode Cloud:

asc xcode-cloud macos-versions --output table
asc xcode-cloud xcode-versions --output table

See which Xcode versions are available on a specific macOS:

asc xcode-cloud macos-versions xcode-versions --id "MACOS_VERSION_ID"

Or the reverse — which macOS versions support a specific Xcode:

asc xcode-cloud xcode-versions macos-versions --id "XCODE_VERSION_ID"

This helps when you are setting up a new workflow and need to pick a compatible macOS + Xcode pair, or when Apple drops support for an older version and you need to find a replacement.

The Monitoring Side

Now we get to the reason this article exists. The public App Store Connect API still does not give you the full Xcode Cloud usage picture that you actually want when you are trying to monitor the situation. So ASC CLI has an experimental command family that talks to Apple's private web API.

These commands are marked experimental, unofficial, and discouraged for production-critical automation. That warning is real. Apple can break them anytime.

But for monitoring your own team's Xcode Cloud usage, they are extremely useful.

Discovering Product IDs First

The private monitoring commands use Xcode Cloud product IDs, not the same --app resolution flow as the public commands. So the first thing to do is list them:

asc web xcode-cloud products \
  --apple-id "[email protected]" \
  --output table

This gives you the product IDs you will use with usage breakdowns, workflow usage, and product-level environment variables.

Plan Summary

If you only run one monitoring command, make it this one:

asc web xcode-cloud usage summary \
  --apple-id "[email protected]" \
  --output table

This shows the current plan name, used minutes, available minutes, total minutes, and reset date. It is the answer to "are we fine this month, or should I care right now?"

Monthly Usage

For trend spotting:

asc web xcode-cloud usage months \
  --apple-id "[email protected]" \
  --output table

You can narrow the range:

asc web xcode-cloud usage months \
  --start-month 1 \
  --start-year 2026 \
  --end-month 3 \
  --end-year 2026 \
  --apple-id "[email protected]" \
  --output table

And if you only care about certain products in the per-product breakdown:

asc web xcode-cloud usage months \
  --product-ids "PRODUCT_UUID" \
  --apple-id "[email protected]" \
  --output table

That last command is worth calling out carefully. It filters the product breakdown section, which is often what I want when I am comparing apps, but it is not the same thing as pretending the entire team only has one product.

Daily Usage

When monthly data tells you something is weird but not when it started, switch to days:

asc web xcode-cloud usage days \
  --product-ids "PRODUCT_UUID" \
  --apple-id "[email protected]" \
  --output table

With a date range:

asc web xcode-cloud usage days \
  --product-ids "PRODUCT_UUID" \
  --start 2026-03-01 \
  --end 2026-03-18 \
  --apple-id "[email protected]" \
  --output table

You can pass multiple product IDs too. When you do that, the first product ID drives the daily and per-workflow tables, and the rest show up in the scope comparison. That is useful when you want to compare one app against the rest of the team.

Private Workflow Inspection and Editing

These commands work with the private workflow payload used by the App Store Connect web UI, not the public API request shape.

Describe a workflow:

asc web xcode-cloud workflows describe \
  --product-id "PRODUCT_UUID" \
  --workflow-id "WORKFLOW_UUID" \
  --apple-id "[email protected]"

Create one from a full private payload:

asc web xcode-cloud workflows create \
  --product-id "PRODUCT_UUID" \
  --file ./workflow.json \
  --apple-id "[email protected]"

Apply a JSON merge patch:

asc web xcode-cloud workflows edit \
  --product-id "PRODUCT_UUID" \
  --workflow-id "WORKFLOW_UUID" \
  --patch-file ./workflow.patch.json \
  --apple-id "[email protected]"

Enable a workflow:

asc web xcode-cloud workflows enable \
  --product-id "PRODUCT_UUID" \
  --workflow-id "WORKFLOW_UUID" \
  --apple-id "[email protected]"

Disable one:

asc web xcode-cloud workflows disable \
  --product-id "PRODUCT_UUID" \
  --workflow-id "WORKFLOW_UUID" \
  --confirm \
  --apple-id "[email protected]"

There is also an options tree for inspecting private editor option payloads, which is useful when you are trying to understand what the web UI considers valid for product config, team config, schemes, test destinations, Slack channels, and so on.

Usage Alerts

This is my favorite command in the whole monitoring set.

asc web xcode-cloud usage alert \
  --warn-at 80 \
  --critical-at 95 \
  --fail-on warning \
  --apple-id "[email protected]" \
  --output table

You can send Slack alerts:

asc web xcode-cloud usage alert \
  --warn-at 75 \
  --critical-at 90 \
  --notify-on critical \
  --slack-webhook "https://hooks.slack.com/services/..." \
  --apple-id "[email protected]"

There is also --trend-months if you want monthly trend context built into the alert evaluation.

The nice part is that this command has sane exit behavior. It can return a non-zero exit when severity reaches your --fail-on level, which means you can run it from cron, a scheduled GitHub Action, or any other tiny monitoring job without writing glue code, although you still have to login with the session cookies which is a pain in itself. (in a good way from security measures)

Workflow-Scoped Environment Variables

The public API does not help much here, so this also lives under asc web xcode-cloud. List workflow env vars:

asc web xcode-cloud env-vars list \
  --product-id "PRODUCT_UUID" \
  --workflow-id "WORKFLOW_UUID" \
  --apple-id "[email protected]"

Set one:

asc web xcode-cloud env-vars set \
  --product-id "PRODUCT_UUID" \
  --workflow-id "WORKFLOW_UUID" \
  --name "API_KEY" \
  --value "abc123" \
  --apple-id "[email protected]"

Set a secret:

asc web xcode-cloud env-vars set \
  --product-id "PRODUCT_UUID" \
  --workflow-id "WORKFLOW_UUID" \
  --name "SIGNING_SECRET" \
  --value "s3cret" \
  --secret \
  --apple-id "[email protected]"

Delete one:

asc web xcode-cloud env-vars delete \
  --product-id "PRODUCT_UUID" \
  --workflow-id "WORKFLOW_UUID" \
  --name "API_KEY" \
  --confirm \
  --apple-id "[email protected]"

Plaintext values are shown directly when listed. Secret values stay redacted, which is exactly what you want.

Shared Product-Level Environment Variables

There is a second env-var layer: shared variables on the product itself.

These are product-scoped, and they can optionally be linked to specific workflows. That is an important distinction. They are not just blindly global in every case. List them:

asc web xcode-cloud env-vars shared list \
  --product-id "PRODUCT_UUID" \
  --apple-id "[email protected]"

Set one:

asc web xcode-cloud env-vars shared set \
  --product-id "PRODUCT_UUID" \
  --name "SHARED_KEY" \
  --value "hello" \
  --apple-id "[email protected]"

Set a locked secret and link it to specific workflows:

asc web xcode-cloud env-vars shared set \
  --product-id "PRODUCT_UUID" \
  --name "SHARED_SECRET" \
  --value "s3cret" \
  --secret \
  --locked \
  --workflow-ids "WF-UUID-1,WF-UUID-2" \
  --apple-id "[email protected]"

Delete one:

asc web xcode-cloud env-vars shared delete \
  --product-id "PRODUCT_UUID" \
  --name "SHARED_KEY" \
  --confirm \
  --apple-id "[email protected]"

This is one of those things that is painful on the website and surprisingly pleasant once it becomes a command.

I treat this whole section as power-user territory. Very useful, but still private API territory!

What's Next

I like this split is that it matches how I think about Xcode Cloud. The public API side is the operational side.

Trigger builds. Check status. Inspect runs. Download artifacts. Query workflows and versions.

The web side is the monitoring and ergonomics side. How much quota is left? Which workflow is expensive? What shared secret is linked to which workflow? Can I alert myself before this becomes annoying?

My friend started with build status and usage. That was it. One command to look, one command to notify. Now, he is slowly expanding into the rest once it was useful.

Happy monitoring!