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
| Method | Path | Purpose |
|---|---|---|
POST | /v1/app-store/purchases/verify | Verify a platform transaction and record the purchase. |
GET | /v1/app-store/entitlements | List 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}/download | Get 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.
| Method | Path | Purpose | SDK |
|---|---|---|---|
GET | /v1/app-store/admin/apps | List the team's App Store app integrations. | s360.appStore.admin.listApps() |
POST | /v1/app-store/admin/apps | Create 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}/purchases | List purchases (filters: status, platform; paginated). | s360.appStore.admin.listPurchases(id, query) |
GET | /v1/app-store/admin/apps/{appSimplyId}/stats | Aggregate 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.
productIdis 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
isEntitledfor 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.