# Apple Ads Operator Playbooks > Practical Apple Ads workflows for safe setup, read-only checks, reporting, and guarded mutations Use these playbooks when you are operating Apple Ads from `asc` in a terminal, CI job, or agent run. The goal is to start read-only, confirm the ad-account context, and only mutate resources after the target IDs and payload files are reviewed. The primary examples use the direct Platform API v1 resource commands under `asc ads` with `--ad-account`. The deprecated Campaign Management API v5 tree is isolated in the migration and legacy sections below. ## Setup and Credential Safety Apple Ads credentials are separate from App Store Connect API credentials. `asc auth login` does not configure `asc ads`. Create a named Apple Ads profile and store the default ad account when you know it: ```bash theme={null} asc ads auth login \ --name "Marketing" \ --client-id "SEARCHADS_CLIENT_ID" \ --team-id "SEARCHADS_TEAM_ID" \ --key-id "KEY_ID" \ --private-key "$HOME/.asc/apple-ads-private-key.pem" \ --ad-account "987654" \ --network ``` For CI, prefer secrets and environment variables over checked-in config: ```bash theme={null} export ASC_ADS_CLIENT_ID="SEARCHADS_CLIENT_ID" export ASC_ADS_TEAM_ID="SEARCHADS_TEAM_ID" export ASC_ADS_KEY_ID="KEY_ID" export ASC_ADS_PRIVATE_KEY_PATH="$HOME/.asc/apple-ads-private-key.pem" export ASC_ADS_AD_ACCOUNT_ID="987654" export ASC_ADS_BYPASS_KEYCHAIN=1 asc ads apps search --ad-account "$ASC_ADS_AD_ACCOUNT_ID" --query "Example" --output json ``` Use a read-only API command for CI validation so the run resolves the exported `ASC_ADS_*` credentials and org context instead of only listing stored profiles. Use `ASC_ADS_PRIVATE_KEY_B64` when your CI secret store handles single-line values more reliably than PEM blocks. Keep `ASC_ADS_ACCESS_TOKEN` for cases where another trusted system already minted a short-lived token. ## Platform API v1 migration For new automation, start with the Apple Ads Platform API v1 command tree: ```bash theme={null} asc ads me view --output json asc ads acls list --output json asc ads apps search --ad-account "987654" --query "Example" --output json ``` The legacy Campaign Management API v5 commands under `asc ads v5` still run in 4.4.0 and print a deprecation warning on stderr. Apple retires v5 on January 26, 2027. A v5 command keeps its existing `--org` context; a v1 command uses the separate `--ad-account` context. Do not pass an organization ID where a v1 ad account ID is required, or assume that the two IDs identify the same thing. V1 query, report, and mutation bodies use the Platform API schemas. Put the appropriate v1 object in `--file`; the CLI does not convert a v5 selector, reporting request, numeric ID, or response envelope. V1 report pagination stays in the request JSON, so do not add the legacy `--paginate` flag to a v1 report command: ```bash theme={null} asc ads reports apps campaigns \ --ad-account "987654" \ --file report.json \ --output json ``` Platform v1 exposes one `negative-keywords` resource for both campaign and ad group scope. Move the scope into the v1 query or mutation body rather than looking for separate campaign-negative-keywords and ad-group-negative-keywords commands. The v5 `asc ads v5 reports preset` helper remains a warning-producing compatibility path; replace it with an explicit v1 report leaf and reviewed request file. Seven v5 leaves have no one-command v1 replacement in 4.4.0: product-page countries, product-page devices, targeting-keyword bulk delete, campaign negative-keyword bulk delete, ad-group negative-keyword bulk delete, and impression-share report list and view. Related v1 commands are not drop-in replacements for those contracts. In particular, `geo search` is not the v5 country/device mapping, `insights impression-share` is not the custom report list/view API, and v1 has no bulk-delete operation. For raw calls, keep `asc ads v5 api request` for v5 paths and its `--org` context. It remains runnable with a warning and is not rewritten. Use `asc ads api request --path v1/... --ad-account ...` for a raw v1 call. ## Account Access Inspection Start outside an ad-account context. These commands do not require `--ad-account`: ```bash theme={null} asc ads acls list --output json ``` Use the ACL response to confirm the ad account ID, then pin it explicitly for the rest of the session: ```bash theme={null} export ASC_ADS_AD_ACCOUNT_ID="987654" asc ads apps search --ad-account "$ASC_ADS_AD_ACCOUNT_ID" --query "Example" --limit 1 --output json ``` When operating multiple Apple Ads accounts, avoid relying on the ambient default. Pass both profile and ad account: ```bash theme={null} asc ads apps search \ --ads-profile "Marketing" \ --ad-account "987654" \ --query "Example" \ --output json ``` ## Safe Read-Only Smoke Test Use this sequence before an automation run or after rotating credentials: ```bash theme={null} ASC_BYPASS_KEYCHAIN=1 asc ads acls list --output json ASC_BYPASS_KEYCHAIN=1 asc ads apps search --ad-account "987654" --query "Example" --limit 1 --output json ``` Expected result: every command exits successfully, and the app search returns either a `result` envelope or an empty result. Treat auth failures, missing account errors, or unexpected account names as stop conditions. When using stored Ads profiles, add profile validation before the API smoke test: ```bash theme={null} asc ads auth status --validate --output json ``` ## Campaign and Ad Group Inventory Query campaigns first, then inspect ad groups under one confirmed campaign ID: ```bash theme={null} asc ads campaigns find \ --ad-account "987654" \ --file campaign-query.json \ --output json asc ads ad-groups find \ --ad-account "987654" \ --file ad-group-query.json \ --output json ``` For a full v1 query, keep pagination in the request body: ```bash theme={null} asc ads campaigns find --ad-account "987654" --file campaign-query.json --output json asc ads ad-groups find --ad-account "987654" --file ad-group-query.json --output json ``` Do not use report payloads as inventory replacements. Reports answer performance questions; list and find endpoints answer "what exists right now?" questions. ## Report Workflow Use Platform API v1 for new report automation. Its body shape differs from v5: ```json platform-report.json { "pagination": {"offset": 0, "pageSize": 20}, "filters": [ {"field": "campaignId", "operator": "EQUALS", "value": ["444555666"]} ], "groupBy": ["countryOrRegion"], "timeRange": { "start": "2025-01-01", "end": "2025-01-31", "timeZone": "ORTZ", "granularity": "DAILY" } } ``` ```bash theme={null} asc ads reports apps campaigns \ --ad-account "987654" \ --file platform-report.json \ --output json ``` The v1 report response keeps Apple's raw `result` and `pagination` fields. Change `offset` and `pageSize` in the file to fetch another page. Run `asc ads reports apps campaigns --help` for body and option guidance; report entities do not all accept the same `groupBy` values. ### Legacy v5 report compatibility The request below is the deprecated v5 shape. Keep it only while existing automation migrates: ```json reporting-request.json { "startTime": "2026-05-01", "endTime": "2026-05-31", "returnRowTotals": true, "returnGrandTotals": true, "selector": { "pagination": { "offset": 0, "limit": 100 }, "orderBy": [ { "field": "impressions", "sortOrder": "DESCENDING" } ] } } ``` Run the campaign report with an explicit org and output format: ```bash theme={null} asc ads v5 reports campaigns \ --org "123456" \ --file reporting-request.json \ --output json ``` For ad-group, keyword, search-term, or ad-level reports, first verify the parent campaign ID with `asc ads v5 campaigns list`. Report pagination lives inside the Apple Ads request body, so update the file's selector pagination rather than adding `--paginate`. ## Raw API Safety Use `asc ads api request` for Platform API v1 debugging, newly added Apple fields, or support captures. Prefer first-class commands for routine work. Read-only raw request: ```bash theme={null} asc ads api request \ --method POST \ --path v1/campaigns/query \ --ad-account "987654" \ --file campaign-query.json \ --output json ``` Raw v1 requests only accept Platform API v1 paths or `https://api.ads.apple.com/v1/...` URLs. `DELETE` requests and risky mutation payloads require `--confirm`; multipart asset upload must use `asc ads assets upload`. For legacy troubleshooting only, keep `asc ads v5 api request` with its v5 path and `--org` context: ```bash theme={null} asc ads v5 api request \ --method POST \ --path v5/campaigns/find \ --org "123456" \ --file selector.json \ --output json ``` Raw requests only accept Apple Ads v5 paths or Apple Ads API URLs. `DELETE` requests require `--confirm`; do not pass it until the command line includes the exact target path you intend to delete. ## Mutation Safety Checklist Before running create, update, delete, or bulk commands: 1. Run the matching `--help` command and confirm required flags. 2. Resolve account access with `asc ads acls list --output json`. 3. Run a read-only resource check: `asc ads apps search --ad-account "987654" --query "Example" --limit 1 --output json`. 4. Pass `--ads-profile` and `--ad-account` explicitly when more than one account is possible. 5. Store request bodies in reviewed JSON files; avoid shell-escaped inline JSON. 6. Create paused or clearly named test resources when validating workflow shape. 7. Print or log the target IDs before using `--confirm`. 8. Prefer deleting temporary parent campaigns only after confirming Apple allows the cleanup. Example guarded delete: ```bash theme={null} asc ads campaigns view --ad-account "987654" --campaign "campaign-id" --output json asc ads campaigns delete --ad-account "987654" --campaign "campaign-id" --confirm ``` ## Troubleshooting Auth and Source Precedence Use the auth doctor first: ```bash theme={null} asc ads auth doctor --output json asc ads auth status --verbose --output json ``` Common fixes: * Missing ad account: pass `--ad-account`, export `ASC_ADS_AD_ACCOUNT_ID`, or store `--ad-account` during login. * Wrong profile: pass `--ads-profile "Marketing"` or export `ASC_ADS_PROFILE`. * Keychain prompts in CI: export `ASC_ADS_BYPASS_KEYCHAIN=1`. * Mixed sources: set `ASC_ADS_STRICT_AUTH=1` to fail when both profile and env token/key sources are present. * Token-only runs: `ASC_ADS_ACCESS_TOKEN` still needs `--ad-account`, `ASC_ADS_AD_ACCOUNT_ID`, or a stored profile account for scoped v1 commands. When debugging API responses, use an explicit output format and redact tokens, client IDs, team IDs, key IDs, org IDs, and account names before sharing logs.