Reference docs for
asactl. If you are just getting set up, start with the README.
- CLI Reference
- YAML Schema
- Auth Configuration
- Recreate & Wipe Modes
- Multi-File Composition
- Generators
- Safety & Best Practices
- Troubleshooting
| Flag | Description | Default |
|---|---|---|
--json |
Machine-readable JSON output on stdout | off |
--verbose |
Structured debug logs on stderr | off |
--root |
Base directory for resolving relative includes (manifests, stdin) | working directory |
Check YAML syntax and required structure without calling Apple Ads.
asactl validate campaign.yamlMake sure your credentials work and that the configured campaign group, app, and product pages are visible.
asactl check-auth campaign.yamlCompare your YAML to live Apple Ads state and show the diff.
asactl plan campaign.yaml
asactl plan campaign.yaml --out saved-plan.json| Flag | Description |
|---|---|
--out <file> |
Save the plan to a file so you can apply it later |
--recreate |
Plan to delete and rebuild everything managed for this app scope |
--wipe-org |
Plan to delete every campaign in the campaign group, then rebuild from YAML |
Apply the planned changes to Apple Ads.
# apply directly from YAML
asactl apply campaign.yaml --yes
# or if you saved a plan from before
asactl apply saved-plan.json --yes| Flag | Description |
|---|---|
--yes |
Skip the confirmation prompt |
--dry-run |
Show what would happen without changing anything |
--recreate |
Delete and rebuild everything managed for this app scope |
--wipe-org |
Delete every campaign in the campaign group, then rebuild from YAML |
Saved plan behavior:
apply <saved-plan>uses the saved plan as-is. It does not re-fetch remote state or build a new plan.- You cannot combine
apply <saved-plan>with--profile,--recreate,--wipe-org, or--root.
Clone a config to another storefront with bid and budget scaling.
asactl clone campaign-us.yaml campaign-uk.yaml --storefront GB --bid-multiplier 0.8 --budget-multiplier 0.5- Accepts a standalone config or a manifest; always writes a standalone config.
- Both
--bid-multiplierand--budget-multipliermust be greater than zero.
Format YAML consistently.
asactl fmt campaign.yaml # Print formatted output
asactl fmt -w campaign.yaml # Write in placeManage the global user config at ~/.asactl/config.toml.
asactl config path # Print config file location
asactl config init # Create starter config
asactl config init --force # Reset a profile to starter placeholders
asactl config edit # Open in editor
asactl config show # Show current config (redacted)Use kind: Config when you want one file that contains the full desired state for one app scope.
| Field | Type | Required | Description |
|---|---|---|---|
version |
integer | yes | Schema version, currently 1 |
kind |
string | yes | Must be Config |
campaign_group |
object | yes | Contains id (Apple Ads orgId) |
auth |
object | yes | Contains profile referencing a global TOML profile |
app |
object | yes | Contains name and app_id |
defaults |
object | yes | Contains currency, devices, optionally storefronts |
product_pages |
map | no | Named product page entries with product_page_id |
generators |
list | no | Typed generators for negative keyword generation |
campaigns |
list | yes | Campaign definitions |
Key field notes:
campaign_group.idis required and identifies the Apple Ads campaign group to manage.app.app_idis the App Store app identifier. Apple often calls it the Adam ID.defaults.currencyis the Apple Ads account currency (e.g.EUR). Required when campaign creation is part of the plan.product_pages.<key>.product_page_idis the Apple product page UUID (can be retrieved from appstoreconnect).adgroups[].product_pagemust reference an existingproduct_pagesentry.adgroups[].targetingmust beKEYWORDSorSEARCH_MATCH.- Search Results is the only supported ad type in v1 (no
defaults.placementfield).
Use kind: Manifest when you want to split one desired state across multiple files.
This is especially useful if you have multiple campaigns per region and want to organize them
into separate files, while keeping shared config like campaign_group and app in a common base file.
| Field | Type | Required | Description |
|---|---|---|---|
version |
integer | yes | Schema version |
kind |
string | yes | Must be Manifest |
base |
string | yes | Path to base file |
campaigns |
list | yes | Paths to campaign files |
Shared config referenced by a manifest.
Contains: campaign_group, auth, app, defaults, product_pages.
Campaign definitions referenced by a manifest.
Contains: generators, campaigns.
Set up auth by running:
asactl config init
asactl config editconfig init creates a starter config with placeholders for the required Apple Ads API credentials. Fill those in with your actual credentials before trying to plan or apply.
The global config uses named profiles like this:
version = 1
default_profile = "default"
[profiles.default]
client_id = "YOUR_APPLE_ADS_CLIENT_ID"
team_id = "YOUR_APPLE_ADS_TEAM_ID"
key_id = "YOUR_APPLE_ADS_KEY_ID"
private_key_path = "/absolute/path/to/appleads-private-key.pem"Reference the profile in YAML:
auth:
profile: defaultThis keeps secrets out of version-controlled YAML.
NOTE: At the time of writing, Apple Ads requires a second user account invited to your Apple Ads org with the
API Account Managerrole associated with it, to manage API access. An API client can not be set-up from anAccount Adminuser.
Generate an Apple-compatible EC P-256 key pair:
openssl ecparam -genkey -name prime256v1 -noout -out appleads-private-key.pem
openssl ec -in appleads-private-key.pem -pubout -out appleads-public-key.pemUpload appleads-public-key.pem in Apple Ads, then point private_key_path of the config at the matching private key file.
Deletes all managed campaigns in the configured campaign_group.id + app.app_id scope, then recreates the YAML state. Use this when you want a clean rebuild of one managed app scope.
Deletes every remote campaign visible in the configured campaign_group.id, then recreates the YAML state. This is org-wide and much riskier.
--recreateand--wipe-orgare mutually exclusive.- Always use
planwith these flags first to review what will be destroyed. - Even without these flags,
asactldeletes managed resources that you removed from YAML. Use--recreateor--wipe-orgonly when you want a bigger reset.
If one YAML file gets too big, split it into a base file and separate campaign files with a manifest:
# manifest.yaml (Manifest)
version: 1
kind: Manifest
base: base.yaml
campaigns:
- campaigns/brand.yaml
- campaigns/discovery.yamlThe base file (kind: Base) holds shared config: campaign_group, auth, app, defaults, product_pages.
Each campaigns file (kind: Campaigns) holds: generators, campaigns.
One manifest still resolves to one effective desired state for a single campaign_group.id + app.app_id scope.
See examples/composed/manifest.yaml for a working example.
Use generators when part of the desired state should be derived from other campaigns.
Right now, v1 supports one generator kind: KeywordToNegative.
generators:
- name: discovery-block-exact
kind: KeywordToNegative
spec:
source_refs:
campaigns:
- Brand - Exact
- Category - Exact
target_ref:
campaign: Discovery
filters:
keyword_match_types:
- EXACT
generate:
campaign_negative_keywords:
match_type: EXACT
status: ACTIVEThis creates exact campaign negatives in the target campaign for every exact keyword found in the source campaigns, so discovery campaigns do not bid on terms you already target elsewhere.
For a small working example, see examples/starter_with_generators.yaml.
asactl check-auth campaign.yaml # Verify credentials
asactl plan campaign.yaml --out prod.plan --json # Save plan
asactl apply prod.plan --yes # Apply for real- Always
planbeforeapply.
File issues at GitHub Issues.