How do I connect the App Store to RevenueDot?
Three steps: create an App Store app in RevenueDot with your bundle ID, give it an In-App Purchase key so it can ask Apple about purchases, and paste its notification URL into App Store Connect as the Version 2 server notification URL. The dashboard's app page walks you through the same steps and checks each one.
1. Create the app#
In the dashboard: Apps → New app → App Store, with your bundle ID. Or with the API:
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/apps" \
-H "Authorization: Bearer $SECRET_KEY" -H "Content-Type: application/json" \
-d '{"name":"Scanner (iOS)","type":"app_store","app_store":{"bundle_id":"com.example.scanner"}}'The answer holds the app's id. Its public SDK key (appl_...) is on the app's page, or at GET /v2/projects/{project_id}/apps/{app_id}/public_api_keys. Use type mac_app_store (key mac_...) for a separate Mac App Store app.
2. Add the In-App Purchase key#
RevenueDot uses this key to call Apple's App Store Server API: to confirm each StoreKit 2 purchase, read a customer's full history and renewal state (auto-renew, billing retry, grace period), and extend subscriptions.
- Open App Store Connect → Users and Access → Integrations → In-App Purchase.
- Click +, name the key (for example RevenueDot) and click Generate.
- Download the
.p8file. Apple lets you download it only once. - Note the Key ID (10 characters) and the Issuer ID shown above the keys list.
- In the dashboard, open the app → In-app purchase key, drop the
.p8file, fill in the IDs and click Check credentials.
Or with the API. RevenueDot stores these under RevenueCat's field names and never returns them:
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/apps/$APP_ID" \
-H "Authorization: Bearer $SECRET_KEY" -H "Content-Type: application/json" \
-d "$(jq -n --rawfile key AuthKey_ABC123DEFG.p8 '{app_store: {subscription_private_key: $key, subscription_key_id: "ABC123DEFG", subscription_key_issuer: "57246542-96fe-1a63-e053-0824d011072a"}}')"
# Ask Apple whether the key works (one harmless API call).
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/apps/$APP_ID/actions/verify_credentials" -H "Authorization: Bearer $SECRET_KEY"{"object":"credentials_check","app_id":"appugfw01uy","store":"app_store","status":"valid","valid":true,"message":"Apple accepted the in-app purchase key.","checked_at":1790801342700,"key_id":"ABC123DEFG"}Without the key, RevenueDot still verifies StoreKit 2 signed transactions against Apple's root certificate, but it knows only what the device sent: no renewal state and no history. StoreKit 1 receipts need the key. Without it RevenueDot answers 500 with code 7234, so the SDK keeps the purchase and retries after you add the key. (For development only, the allow_unsigned_receipts setting accepts StoreKit 1 receipts without checking them. Anyone could forge such a receipt, so never turn it on in production.)
3. Send App Store Server Notifications to RevenueDot#
Notifications tell RevenueDot about renewals, cancellations, billing problems and refunds when they happen, not only when the app next opens.
- Copy the app's notification URL from the dashboard, or from the API. It looks like
https://revenuedot.example.com/v1/notifications/apple/{app_id}:Behind a reverse proxy, RevenueDot builds the URL fromShellcurl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/apps/$APP_ID/store_settings" -H "Authorization: Bearer $SECRET_KEY" | jq -r .notification_urlX-Forwarded-HostandX-Forwarded-Proto. Check that it shows your public HTTPS address. - In App Store Connect, open your app → App Information → App Store Server Notifications.
- Paste the URL as both the Production Server URL and the Sandbox Server URL, and choose Version 2.
- Make a sandbox purchase. The app's notification status turns Ready when the first notification about a known purchase is processed.
How RevenueDot answers Apple:
- 200 for every verified notification, including ones about purchases it has not seen. Those are stored, and applied only when Track new purchases from server-to-server notifications is on (
track_new_purchases). - 400 when the signature is invalid, or the notification is for another bundle ID (or another Apple app ID, when you set
app_apple_id). App Store Connect shows these as failed. - 500 when RevenueDot itself fails. Apple retries.
Check the status any time: GET /v2/projects/{project_id}/setup_health lists each app's notification_status (ready, failing, received or waiting) and the last error. See store notifications not arriving.
Optional settings#
Set these in the app's app_store object with POST /v2/projects/{project_id}/apps/{app_id}, or in the dashboard under More settings:
| Field | What it does |
|---|---|
notification_forward_url |
Copies each notification, byte for byte, to another URL, such as RevenueCat's during a dual run. null turns it off |
track_new_purchases |
true: apply notifications about purchases RevenueDot has never seen. Useful during a migration |
app_apple_id |
Your app's Apple ID (a number). Production notifications for another app ID are refused |
xcode_certificate |
The StoreKit test certificate exported from Xcode (PEM). Lets RevenueDot accept purchases made with a StoreKit configuration file in the simulator. See sandbox testing |
allow_unsigned_receipts |
Development only: accept StoreKit 1 receipts without the In-App Purchase key |
app_store_connect_api_key, _id, _issuer, app_store_connect_vendor_number |
A separate App Store Connect API key. Stored for a later product import; not used yet |
shared_secret |
The legacy app-specific shared secret. Stored but not used: RevenueDot does not call Apple's deprecated verifyReceipt endpoint |
What you can do from the server afterwards#
- Extend a subscription by 1 to 90 days:
POST /v2/projects/{project_id}/subscriptions/{subscription_id}/actions/extendwithextend_by_daysandextend_reason_code. - Extend every active subscriber of a product, for example after an outage:
POST /v2/projects/{project_id}/apps/{app_id}/actions/mass_extend. - Refunds are Apple's decision; customers ask Apple. RevenueDot records Apple's
REFUNDnotification as aCANCELLATIONwith a negative price.