← All documentationContents ↓

API Reference: App Store (Purchases & Entitlements)

Verify Apple App Store and Google Play purchases, list a constituent's entitlements, and issue secure download URLs for purchased content.

Overview

The App Store API supports mobile apps that sell content through Apple's App Store or Google Play. After a user completes an in-app purchase, your mobile client posts the platform-issued transaction to Simply360, which verifies it with Apple/Google and records the purchase as an entitlement. From then on, Simply360 is the source of truth for what the user owns — you query entitlements to gate access to content, and you generate signed download URLs for files the user has purchased.

Use this API alongside the Constituent Authentication flow — the access token from constituent auth is what authorizes the purchase and entitlement endpoints. A separate set of admin endpoints lets Team Admins manage the App Store app configuration and review purchases.

Endpoints

MethodPathPurpose
POST/v1/app-store/purchases/verifyVerify a platform transaction and record the purchase.
GET/v1/app-store/entitlementsList the current constituent's entitlements for a specific app.
GET/v1/app-store/entitlements/{contentSimplyId}Check a single piece of content for entitlement (boolean + purchase metadata).
GET/v1/app-store/content/{contentSimplyId}/downloadGet a time-limited signed download URL for purchased content.

Verify a Purchase

After your mobile client receives a purchase confirmation from Apple or Google, post the transaction details. appStoreAppSimplyId, platform (apple or google), and transactionId are always required; Google purchases additionally require purchaseToken and productId. Re-verifying an already-recorded transaction is idempotent and returns the existing purchase.

TypeScript SDK

const verification = await s360.appStore.verifyPurchase({
  appStoreAppSimplyId: 'APPS-1234-ABCD',
  platform: 'apple', // or 'google'
  transactionId: 'transaction-id-from-platform',
  purchaseToken: 'platform-purchase-token', // required for Google
  productId: 'product-id-as-configured-in-store', // required for Google
});

// verification.data.isEntitled tells you whether the user can access the content right now.
// verification.data.purchaseSimplyId, .productId, and .status describe the recorded purchase.

cURL

curl -s -X POST "https://api.simply360.app/v1/app-store/purchases/verify" \
  -H "Authorization: Bearer $CONSTITUENT_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "appStoreAppSimplyId": "APPS-1234-ABCD",
    "platform": "apple",
    "transactionId": "1000000123456789"
  }'

List Entitlements

Returns every active entitlement for the authenticated constituent on a given app (the appStoreAppSimplyId query parameter is required). Useful for hydrating a mobile app's "what do I own" screen. Each entitlement includes purchaseSimplyId, productId, contentDataRecordId, status, and expiresAt.

const result = await s360.appStore.getEntitlements({
  appStoreAppSimplyId: 'APPS-1234-ABCD',
});

for (const entitlement of result.data.entitlements) {
  console.log(`${entitlement.productId}: ${entitlement.status}, expires ${entitlement.expiresAt ?? 'never'}`);
}
curl -s "https://api.simply360.app/v1/app-store/entitlements?appStoreAppSimplyId=APPS-1234-ABCD" \
  -H "Authorization: Bearer $CONSTITUENT_ACCESS_TOKEN"

Check Entitlement for One Item

When you only need to gate a single piece of content, this returns a focused boolean plus the most recent qualifying purchase:

const check = await s360.appStore.checkEntitlement('CONT-1234-ABCD');
if (check.data.isEntitled) {
  // Render the content / show the download button
}
curl -s "https://api.simply360.app/v1/app-store/entitlements/CONT-1234-ABCD" \
  -H "Authorization: Bearer $CONSTITUENT_ACCESS_TOKEN"

Download Purchased Content

Generates a signed URL for the encrypted content file (the appStoreAppSimplyId query parameter is required). URLs expire after 15 minutes (expiresIn: 900); request a fresh one whenever the user starts a download. Requests for content the constituent does not own return 403 NOT_ENTITLED.

const download = await s360.appStore.downloadContent('CONT-1234-ABCD', {
  appStoreAppSimplyId: 'APPS-1234-ABCD',
});

// download.data.downloadUrl is the signed URL for the encrypted file.
// download.data.expiresIn is the URL lifetime in seconds (900).
// download.data.encryptedContentKey wraps the decryption key for the file.
curl -s "https://api.simply360.app/v1/app-store/content/CONT-1234-ABCD/download?appStoreAppSimplyId=APPS-1234-ABCD" \
  -H "Authorization: Bearer $CONSTITUENT_ACCESS_TOKEN"

Subscription Renewals

Simply360 receives the platforms' server-to-server notifications directly: configure Apple App Store Server Notifications and Google Play Real-Time Developer Notifications to point at the Simply360 webhook endpoints for your app (/v1/app-store/webhooks/apple/{appStoreAppSimplyId} and /v1/app-store/webhooks/google/{appStoreAppSimplyId}). Renewals, cancellations, expirations, and refunds then update isEntitled automatically — your client just re-reads entitlements.

Admin Endpoints

Team Admins manage the App Store integration itself through /v1/app-store/admin/apps. These endpoints require Cognito user authentication plus the APP_STORE_INTEGRATION feature permission; provider credentials are write-only and never returned.

MethodPathPurposeSDK
GET/v1/app-store/admin/appsList the team's App Store app integrations.s360.appStore.admin.listApps()
POST/v1/app-store/admin/appsCreate an app integration.s360.appStore.admin.createApp(input)
PUT/v1/app-store/admin/apps/{appSimplyId}Update an app integration (only supplied fields change).s360.appStore.admin.updateApp(id, input)
GET/v1/app-store/admin/apps/{appSimplyId}/purchasesList purchases (filters: status, platform; paginated).s360.appStore.admin.listPurchases(id, query)
GET/v1/app-store/admin/apps/{appSimplyId}/statsAggregate purchase stats for an app.s360.appStore.admin.getStats(id)

Usage Notes

  • Purchase, entitlement, and download calls require the constituent access token (not a server API key) so Simply360 can attribute purchases and entitlements to the correct DataRecord person.
  • productId is the SKU as configured in App Store Connect / Google Play Console — map your platform SKUs to Simply360 content via the App Store admin in the dashboard.
  • Entitlement state is webhook-driven with an expiry-window backstop. Don't cache isEntitled for long on the client without refreshing.
  • Content download URLs are not idempotent — each call generates a fresh URL. Don't email or share them; they're meant for the immediate download session only.