Skip to content

Search the documentation by title, section, or page text.

Validate Apple Store Purchase

POST https://api.botsi.com/v2/purchases/apple-store/validate

Call this after a purchase completes in your app. It confirms the transaction with Apple and records the revenue against the Profile and the Paywall that produced it.

Identify the user with profileId.

Request body#

ParameterTypeRequiredDescription
profileIdstringRequiredBotsi profile ID from Create a Profile
productIdstringRequiredProduct identifier from the payment object. Must match the Product configured in Botsi
transactionIdstringRequiredTransaction identifier from the StoreKit payment object
originalTransactionIdstringRequiredOriginal transaction identifier from the StoreKit payment object
placementIdstringOptionalThe Placement used to fetch the Paywall
paywallIdnumberOptionalThe internal Paywall ID, from data.id on the Fetch Paywall response
abTestIdnumberOptionalThe A/B test that produced the Paywall
isExperimentbooleanOptionalThe value Fetch Paywall returned
aiPricingModelIdnumberOptionalThe model ID Fetch Paywall returned
environmentstringOptionalproduction for live apps, sandbox for testing
isSubscriptionbooleanOptionalWhether the purchase is a subscription
sourcestringOptionalrestore, purchasing, or observing. Defaults to purchasing

isExperiment and aiPricingModelId are optional on the v2 API, though the SDK surface requires them. Omitting them still validates the purchase — see the attribution note below for why you should send them anyway.

source is load-bearing, even though it is optional

It defaults to purchasing when omitted. restore short-circuits validation entirely, and purchasing selects the deduplication key, so the value you send changes what the endpoint does rather than just labelling the call. Send it deliberately.

This endpoint does not accept paywallSessionId

The token shortcut applies to Send Paywall Shown Event only. Keep passing paywallId, isExperiment, and aiPricingModelId here, exactly as Fetch Paywall returned them.

Omit them and the purchase still validates, but it detaches from the experiment that produced it, so the model gets no credit for the revenue it earned.

curl -X POST "https://api.botsi.com/v2/purchases/apple-store/validate" \
  -H "Authorization: $BOTSI_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "placementId": "ai-placement-id",
    "paywallId": 42,
    "source": "purchasing",
    "productId": "premium_annual",
    "isSubscription": true,
    "originalTransactionId": "2000000261699248",
    "environment": "sandbox",
    "transactionId": "2000000871590381",
    "profileId": "fff3c120-0ca8-480f-9b19-3955a5428e39",
    "isExperiment": true,
    "aiPricingModelId": 32
  }'

Finding the identifiers#

StoreKit. transactionId and originalTransactionId come from the SKPaymentTransaction object. The Product identifier is SKPaymentTransaction.payment.productIdentifier, and the original price is on SKProduct.

StoreKit 2. All three come from the Transaction object, and the original price from Product.

Response#

A successful validation returns the updated Profile, including its Access Levels, subscriptions, and total revenue.

{
  "ok": true,
  "data": {
    "profileId": "fff3c120-0ca8-480f-9b19-3955a5428e39",
    "appUserId": "user-123",
    "state": "subscribed",
    "totalRevenueUsd": 49.99,
    "accessLevels": {},
    "subscriptions": {},
    "nonSubscriptions": {}
  }
}