---
title: "How do I connect the App Store to RevenueDot?"
description: "Create an App Store app with your bundle ID, add an In-App Purchase key (.p8, key ID, issuer ID), then set RevenueDot's notification URL as the Server Notifications v2 URL in App Store Connect."
url: https://revenuedot.app/docs/guides/app-store
---

# 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:

```bash
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.

1. Open [App Store Connect → Users and Access → Integrations → In-App Purchase](https://appstoreconnect.apple.com/access/integrations/api/subs).
2. Click **+**, name the key (for example RevenueDot) and click **Generate**.
3. Download the `.p8` file. Apple lets you download it only once.
4. Note the **Key ID** (10 characters) and the **Issuer ID** shown above the keys list.
5. In the dashboard, open the app → **In-app purchase key**, drop the `.p8` file, fill in the IDs and click **Check credentials**.

Or with the API. RevenueDot stores these under RevenueCat's field names and never returns them:

```bash
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"
```
```json
{"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.

1. 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}`:
   ```bash
   curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/apps/$APP_ID/store_settings" -H "Authorization: Bearer $SECRET_KEY" | jq -r .notification_url
   ```
   Behind a reverse proxy, RevenueDot builds the URL from `X-Forwarded-Host` and `X-Forwarded-Proto`. Check that it shows your public HTTPS address.
2. In App Store Connect, open your app → **App Information** → **App Store Server Notifications**.
3. Paste the URL as both the **Production Server URL** and the **Sandbox Server URL**, and choose **Version 2**.
4. 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](https://revenuedot.app/docs/help/store-notifications-not-arriving.md).

## 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](https://revenuedot.app/docs/migrate/dual-run.md). `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](https://revenuedot.app/docs/guides/sandbox-testing.md) |
| `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/extend` with `extend_by_days` and `extend_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 `REFUND` notification as a `CANCELLATION` with a negative price.

## Related
- [iOS SDK guide](https://revenuedot.app/docs/sdks/ios.md)
- [Test with sandbox accounts and Xcode](https://revenuedot.app/docs/guides/sandbox-testing.md)
- [Webhooks](https://revenuedot.app/docs/guides/webhooks.md)
- [REST API v2: apps](https://revenuedot.app/docs/api/rest-v2.md)
