Status: Implemented as stable
Research date: June 18, 2026
Target API: Retention Messaging API 1.0
Target command root: asc storekit
Retention Messaging is a StoreKit server API, not an App Store Connect API
resource. It has different production and sandbox hosts, a different error
envelope, and dedicated In-App Purchase API keys. It therefore lives in
internal/storekit and under the stable Monetization command root:
asc storekit auth ...
asc storekit retention-messaging ...
This keeps StoreKit credentials out of the existing asc auth profile pool and
prevents an In-App Purchase key from being selected for an App Store Connect
request. The App Store Connect OpenAPI snapshot under docs/openapi does not
contain these endpoints.
Alternatives considered:
- Putting the commands under
asc subscriptionswould imply App Store Connect authentication and API ownership, both of which are incorrect. - A raw HTTP command would expose the endpoints but would not safely validate image constraints, message limits, environment selection, or destructive operations.
| CLI command | Method and path |
|---|---|
images upload |
PUT /inApps/v1/messaging/image/{imageIdentifier} |
images list |
GET /inApps/v1/messaging/image/list |
images delete |
DELETE /inApps/v1/messaging/image/{imageIdentifier} |
messages upload |
PUT /inApps/v1/messaging/message/{messageIdentifier} |
messages list |
GET /inApps/v1/messaging/message/list |
messages delete |
DELETE /inApps/v1/messaging/message/{messageIdentifier} |
defaults set |
PUT /inApps/v1/messaging/default/{productId}/{locale} |
defaults view |
GET /inApps/v1/messaging/default/{productId}/{locale} |
defaults delete |
DELETE /inApps/v1/messaging/default/{productId}/{locale} |
endpoint set |
PUT /inApps/v1/messaging/realtime/url |
endpoint view |
GET /inApps/v1/messaging/realtime/url |
endpoint delete |
DELETE /inApps/v1/messaging/realtime/url |
performance start |
POST /inApps/v1/messaging/performanceTest |
performance view / wait |
GET /inApps/v1/messaging/performanceTest/result/{requestId} |
The base host is selected explicitly:
production:https://api.storekit.apple.comsandbox:https://api.storekit-sandbox.apple.com
Performance-test commands reject the production environment. performance wait polls no faster than every 10 seconds to remain within Apple's sandbox
rate limit.
Create an In-App Purchase API key in App Store Connect after Apple grants Retention Messaging access. A browser session logged into App Store Connect can be used to complete Apple's access-request form, but browser cookies are not API credentials and the CLI does not read them.
Store a named profile:
asc storekit auth login \
--name Production \
--key-id "$KEY_ID" \
--issuer-id "$ISSUER_ID" \
--private-key ./SubscriptionKey.p8 \
--bundle-id com.example.appThe keychain is preferred. Use --bypass-keychain for config-backed local
development or CI. Environment authentication supports:
ASC_STOREKIT_KEY_ID
ASC_STOREKIT_ISSUER_ID
ASC_STOREKIT_PRIVATE_KEY_PATH
ASC_STOREKIT_PRIVATE_KEY
ASC_STOREKIT_PRIVATE_KEY_B64
ASC_STOREKIT_BUNDLE_ID
ASC_STOREKIT_ENVIRONMENT
ASC_STOREKIT_PROFILE
ASC_STOREKIT_STRICT_AUTH
ASC_STOREKIT_BYPASS_KEYCHAIN
Every request gets a newly signed ES256 JWT with iss, iat, exp,
aud=appstoreconnect-v1, and bid=<bundle-id>. The private key is parsed once
per client but tokens are never cached.
- The environment is always explicit through
--environmentorASC_STOREKIT_ENVIRONMENT; there is no risky production default. - Image and message identifiers are caller-generated UUIDs.
- Images must be PNG without transparency.
FULL_SIZErequires width 3840 and height 160–2160;BULLET_POINTrequires 1024×1024. - Message payloads use
--fileand reject unknown JSON fields. The CLI validates Apple's text limits and requires alternative text for every image. - Deletes and credential logout require
--confirm. - TTY-aware
json,table, andmarkdownoutput is available on API commands. - Invalid flags and local payloads return usage exit code
2. API and transport failures return exit code1with Apple's error code and message. - Upload image and upload message are not idempotent. The client never retries an ambiguous upload automatically.
Example message file:
{
"header": "Keep everything you unlocked",
"body": "Continue your subscription and keep access to every feature.",
"image": {
"imageIdentifier": "22222222-2222-4222-8222-222222222222",
"altText": "The Example app on an iPhone"
},
"headerPosition": "ABOVE_IMAGE"
}Use a disposable app and resources. The full live verification sequence is:
asc storekit auth doctor --environment sandbox --network
asc storekit retention-messaging images upload \
--image-id 11111111-1111-4111-8111-111111111111 \
--image-size FULL_SIZE \
--file ./retention.png \
--environment sandbox
asc storekit retention-messaging messages upload \
--message-id 33333333-3333-4333-8333-333333333333 \
--file ./message.json \
--environment sandbox
asc storekit retention-messaging messages list --environment sandbox
asc storekit retention-messaging defaults set \
--product-id com.example.monthly \
--locale en-US \
--message-id 33333333-3333-4333-8333-333333333333 \
--environment sandbox
asc storekit retention-messaging endpoint set \
--url https://example.com/retention-messaging-api/ \
--environment sandbox
asc storekit retention-messaging performance start \
--original-transaction-id 2000000000000000 \
--environment sandbox \
--waitThe original transaction ID must come from an actual StoreKit sandbox
subscription purchase. The endpoint must be publicly reachable over HTTPS and
implement Apple's Get Retention Message request/response contract. After the
test reports PASS, configure the production URL with --environment production.
Clean up disposable resources with the matching delete --confirm commands.
Sandbox uploads are automatically approved, so list responses can be verified
without waiting for Apple review.
This adds a new root and a new optional storekit config object. Existing
commands, App Store Connect profiles, and output formats are unchanged. There
is no deprecation or migration.
The RED-to-GREEN test plan covers:
- all 14 documented HTTP operations, request methods, paths, query values, content types, and response decoding;
- ES256 header and StoreKit JWT claims;
- dedicated config credential lifecycle and strict environment resolution;
- command discovery, local payload validation, confirmation gates, and output;
- API error and millisecond
Retry-Afterdecoding; - built-binary usage exit code
2.
Live network verification is conditional on Apple granting access and valid
ASC_STOREKIT_* credentials being present. It is not replaced by an App Store
Connect browser login.