Skip to main content

API specs

Adapty's API lets you access and modify your Adapty data programmatically. We also support webhooks, where we notify your server of events as they happen.

This API enables you to seamlessly integrate Adapty with your existing services.


Authorization

  • Base URL: https://api.adapty.io/api/v2/server-side-api/

  • Authorization header: API requests must be authenticated by including your API key:

    • For profile requests, use either the public or secret API key as the Authorization header with the value Api-Key {YOUR_SECRET_API_KEY} or Api-Key {YOUR_PUBLIC_API_KEY}, for example, Api-Key public_live_iNuUlSsN.83zcTTT8D5Y8FI9cGUI6. You can find your secret API key in the Adapty Dashboard -> App Settings -> General tab -> API keys section. Remember, this key is secret, so keep it private.

    • For access level and transaction requests, you can secret API key as the Authorization header with the value Api-Key {YOUR_SECRET_API_KEY}, like this: Api-Key secret_live_BEHrYLTr.ce5zuDEWz06lFRNiaJC8mrLtL8fUwswD. Find these keys in the Adapty Dashboard -> App Settings -> General tab -> API keys section.

  • Content-Type header: Set the Content-Type header to application/json for the API to process your request.

  • Header:

    • adapty-platform: Use this header to specify the app's platform. Possible options include: iOS, macOS, iPadOS, visionOS, Android.
    • Use one of the following to identify the user profile:
      • adapty-profile-id: The user’s Adapty profile ID, visible in the Adapty ID field in the Adapty Dashboard -> Profiles -> specific profile page.
      • adapty-customer-user-id: The user’s ID in your system, visible in the Customer user ID field in the Adapty Dashboard -> Profiles -> specific profile page. ⚠️ This works only if you identify users in your app code using the Adapty SDK.
  • Body: The API expects the request to use the body as JSON.


Profile

Info about your customer and their subscription.

Profile object

The object that contains details about your customer and their subscription.

ParameterTypeNullableDescription
app_idStringThe internal ID of your app. You can see in the the Adapty Dashboard: App Settings -> General tab.
profile_idUUIDAdapty profile ID. You can see it in the Adapty ID field on the Adapty Dashboard -> Profiles -> specific profile page.
customer_user_idStringThe ID of your user in your system. You can see it in the Customer user ID field on the Adapty Dashboard -> Profiles -> specific profile page. It will work only if you identify the users in your mobile app code via Adapty SDK.
total_revenue_usdFloatA float value representing the total revenue in USD earned in the profile.
segment_hashStringInternal parameter.
timestampIntegerResponse time in milliseconds, needs for resolve a race condition.
custom_attributesDictionary

A maximum of 30 custom attributes to the profile are allowed to be set. If you provide the custom_attributes dictionary, you must provide at least one attribute key.

Key: The key must be a string with no more than 30 characters. Only letters, numbers, dashes, points, and underscores allowed

Value: The attribute value must be no more than 30 characters. Only strings and floats are allowed as values, booleans will be converted to floats. Send an empty value or null to delete the attribute.

access_levelsDictionaryProfile Paid Access Level objects. Dictionary where the keys are paid access level identifiers configured by a developer in the Adapty Dashboard. Values are Access level objects. Can be null if the customer has no access levels.
subscriptionsDictionaryDictionary where the keys are vendor product IDs. Values are Subscription objects. Can be null if the customer has no subscriptions.
non_subscriptionsDictionaryDictionary where the keys are vendor product ids. Values are an array of Non-Subscription objects. Can be null if the customer has no purchases.

Retrieve profile

Retrieves the details of an existing end user of your app.

Endpoint

https://api.adapty.io/api/v2/server-side-api/profile/

Method

GET

Parameters

None in the JSON body. Profile_id or customer_user_id must be set up as a header as described in Authorization.

Successful response: 200 - Success

NameTypeDescription
Request-IdStringThe Request ID, which is included in all backend logs.
Example: 758f01dfd9e74ccfbabb4934241c4966.
Body:

Profile object

Successful response example
{
"data": {
"app_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"profile_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"customer_user_id": "8612ED7C-3477-466D-93AE-1854B8E5FDD5",
"total_revenue_usd": 109.88999999999999,
"segment_hash": "string",
"timestamp": 0,
"custom_attributes": [
{
"key": "string",
"value": "string"
}
],
"access_levels": [
{
"access_level_id": "premium",
"store": "app_store",
"store_product_id": "weekly_8.99",
"store_base_plan_id": "",
"store_transaction_id": "530001802720333",
"store_original_transaction_id": "530001724306018",
"offer": {
"category": "introductory",
"type": "free_trial",
"id": "offer12"
},
"environment": "Production",
"starts_at": "2022-10-12T09:42:50.000000+0000",
"purchased_at": "2022-10-12T09:42:50.000000+0000",
"originally_purchased_at": "2021-10-12T09:42:50.000000+0000",
"expires_at": "2022-10-12T09:42:50.000000+0000",
"renewal_cancelled_at": "2022-10-12T09:42:50.000000+0000",
"billing_issue_detected_at": "2022-10-12T09:42:50.000000+0000",
"is_in_grace_period": true,
"cancellation_reason": "voluntarily_cancelled"
}
],
"subscriptions": [
{
"store": "app_store",
"store_product_id": "weekly_8.99",
"store_base_plan_id": "",
"store_transaction_id": "530001802720333",
"store_original_transaction_id": "530001724306018",
"offer": {
"offer_category": "introductory",
"offer_type": "free_trial",
"offer_id": "offer12"
},
"environment": "Production",
"purchased_at": "2022-10-12T09:42:50.000000+0000",
"originally_purchased_at": "2021-10-12T09:42:50.000000+0000",
"expires_at": "2022-10-12T09:42:50.000000+0000",
"renewal_cancelled_at": "2022-10-12T09:42:50.000000+0000",
"billing_issue_detected_at": "2022-10-12T09:42:50.000000+0000",
"is_in_grace_period": true,
"cancellation_reason": "voluntarily_cancelled"
}
],
"non_subscriptions": [
{
"purchase_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"store": "app_store",
"store_product_id": "weekly_8.99",
"store_base_plan_id": "",
"store_transaction_id": "530001724306018",
"store_original_transaction_id": "530001724306018",
"purchased_at": "2022-10-12T09:42:50.000000+0000",
"environment": "Production",
"is_refund": true,
"is_consumable": true
}
]
}
}

Errors

401 - Unauthorized (click to expand)

The request failed due to missing or incorrect authorization. Check the Authorization page, paying close attention to the Authorization header.

The request also failed because the specified profile wasn’t found.

Body:
ParameterTypeDescription
errorsObject
  • source: (string) Always non_field_errors
  • errors: A description of the error.
error_codeStringShort error name. Always not_authenticated.
status_codeIntegerHTTP status. Always 401.
Response example:
{
"errors": [
{
"source": "non_field_errors",
"errors": [
"Authentication credentials were not provided."
]
}
],
"error_code": "not_authenticated",
"status_code": 401
}
404 - Not found (click to expand)

The request failed because the specified profile wasn’t found.

Body
ParameterTypeDescription
errorsObject
  • source: (string) Always non_field_errors
  • errors: A description of the error.
error_codeStringShort error name. Always not_found.
status_codeIntegerHTTP status. Always 404.
Response example
{
"errors": [
{
"source": "non_field_errors",
"errors": [
"Not found."
]
}
],
"error_code": "not_found",
"status_code": 404
}

Create profile

Creates a new end user of your app in Adapty.

Endpoint

https://api.adapty.io/api/v2/server-side-api/profile/

Method

POST

Parameters

Profile_id or customer_user_id must be set up as a header as described in Authorization.

ParameterTypeRequired in requestNullable in requestDescription
first_nameStringYour end user's first name.
last_nameStringYour end user's last name.
genderStringYour end user's gender.
emailStringYour end user's email.
phone_numberStringYour end user's phone number.
birthdayISO 8601 dateYour end user's birthday.
ip_countryStringCountry of the end user in ISO 3166-2 format. It needs to be passed if the request is made from the server and not from the client, in order to set the current country. Otherwise, we will determine the country by the IP address of the request.
store_countryStringCountry of the end user app store.
storeStringThe app store.
analytics_disabledBoolean

Option to opt out of external analytics. When analytics is disabled, events won’t be sent to integrations, and the fields idfa, idfv, and advertising_id will become nullable.

ON - External analytics is opted out for this user

OFF - Analytics is active by default.

custom_attributesDictionary

Allows setting up to 30 custom attributes for the profile. If you use the custom_attributes dictionary, at least one attribute key is required.

Key: Must be a string with no more than 30 characters, using only letters, numbers, dashes, periods, and underscores.

Value: Must be a string or float with no more than 30 characters. Booleans will be converted to floats. To delete an attribute, send an empty value or null.

installation_metaDictionaryContains information about the specific app on a specific device, structured as a dictionary of Installation Meta objects.

Example request

Example request (click to expand)
JSON
{
"first_name": "Jane",
"last_name": "Doe",
"gender": "f",
"email": "[email protected]",
"phone_number": "+1234567890",
"birthday": "2000-12-31",
"ip_country": "FR",
"store_country": "US",
"store": "app_store",
"analytics_disabled": true,
"custom_attributes": [
{
"key": "favourite_sport",
"value": "yoga"
}
],
"installation_meta": {
"device_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"device": "string",
"locale": "en",
"os": "string",
"platform": "iOS",
"timezone": "Europe/Rome",
"user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Mobile/15E148 Safari/604.1",
"idfa": "EA7583CD-A667-48BC-B806-42ECB2B48333",
"idfv": "E9D48DA5-3930-4B41-8521-D953AECD2F33",
"advertising_id": "",
"android_id": "",
"android_app_set_id": ""
}
}

Successful response

200 - Success

NameTypeDescription
Request-IdStringThe Request ID, which is included in all backend logs.
Example: 758f01dfd9e74ccfbabb4934241c4966.
Body:

Profile object

Successful response example
{
"data": {
"app_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"profile_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"customer_user_id": "8612ED7C-3477-466D-93AE-1854B8E5FDD5",
"total_revenue_usd": 109.88999999999999,
"segment_hash": "string",
"timestamp": 0,
"custom_attributes": [
{
"key": "string",
"value": "string"
}
],
"access_levels": [
{
"access_level_id": "premium",
"store": "app_store",
"store_product_id": "weekly_8.99",
"store_base_plan_id": "",
"store_transaction_id": "530001802720333",
"store_original_transaction_id": "530001724306018",
"offer": {
"category": "introductory",
"type": "free_trial",
"id": "offer12"
},
"environment": "Production",
"starts_at": "2022-10-12T09:42:50.000000+0000",
"purchased_at": "2022-10-12T09:42:50.000000+0000",
"originally_purchased_at": "2021-10-12T09:42:50.000000+0000",
"expires_at": "2022-10-12T09:42:50.000000+0000",
"renewal_cancelled_at": "2022-10-12T09:42:50.000000+0000",
"billing_issue_detected_at": "2022-10-12T09:42:50.000000+0000",
"is_in_grace_period": true,
"cancellation_reason": "voluntarily_cancelled"
}
],
"subscriptions": [
{
"store": "app_store",
"store_product_id": "weekly_8.99",
"store_base_plan_id": "",
"store_transaction_id": "530001802720333",
"store_original_transaction_id": "530001724306018",
"offer": {
"offer_category": "introductory",
"offer_type": "free_trial",
"offer_id": "offer12"
},
"environment": "Production",
"purchased_at": "2022-10-12T09:42:50.000000+0000",
"originally_purchased_at": "2021-10-12T09:42:50.000000+0000",
"expires_at": "2022-10-12T09:42:50.000000+0000",
"renewal_cancelled_at": "2022-10-12T09:42:50.000000+0000",
"billing_issue_detected_at": "2022-10-12T09:42:50.000000+0000",
"is_in_grace_period": true,
"cancellation_reason": "voluntarily_cancelled"
}
],
"non_subscriptions": [
{
"purchase_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"store": "app_store",
"store_product_id": "weekly_8.99",
"store_base_plan_id": "",
"store_transaction_id": "530001724306018",
"store_original_transaction_id": "530001724306018",
"purchased_at": "2022-10-12T09:42:50.000000+0000",
"environment": "Production",
"is_refund": true,
"is_consumable": true
}
]
}
}

Errors

401 - Unauthorized (click to expand)

The request failed due to missing or incorrect authorization. Check the Authorization page, paying close attention to the Authorization header.

The request also failed because the specified profile wasn’t found.

Body:
ParameterTypeDescription
errorsObject
  • source: (string) Always non_field_errors
  • errors: A description of the error.
error_codeStringShort error name. Always not_authenticated.
status_codeIntegerHTTP status. Always 401.
Response example:
{
"errors": [
{
"source": "non_field_errors",
"errors": [
"Authentication credentials were not provided."
]
}
],
"error_code": "not_authenticated",
"status_code": 401
}
404 - Not found (click to expand)

The request failed because the specified profile wasn’t found.

Body
ParameterTypeDescription
errorsObject
  • source: (string) Always non_field_errors
  • errors: A description of the error.
error_codeStringShort error name. Always not_found.
status_codeIntegerHTTP status. Always 404.
Response example
{
"errors": [
{
"source": "non_field_errors",
"errors": [
"Not found."
]
}
],
"error_code": "not_found",
"status_code": 404
}

Update profile

Changes your end user profile attributes.

Endpoint

https://api.adapty.io/api/v2/server-side-api/profile/

Method

PATCH

Parameters

Profile_id or customer_user_id must be set up as a header as described in Authorization.

ParameterTypeRequired in requestNullable in requestDescription
first_nameStringYour end user's first name.
last_nameStringYour end user's last name.
genderStringYour end user's gender.
emailStringYour end user's email.
phone_numberStringYour end user's phone number.
birthdayISO 8601 dateYour end user's birthday.
ip_countryStringCountry of the end user in ISO 3166-2 format. It needs to be passed if the request is made from the server and not from the client, in order to set the current country. Otherwise, we will determine the country by the IP address of the request.
store_countryStringCountry of the end user app store.
storeStringThe app store.
analytics_disabledBoolean

Option to opt out of external analytics. When analytics is disabled, events won’t be sent to integrations, and the fields idfa, idfv, and advertising_id will become nullable.

ON - External analytics is opted out for this user

OFF - Analytics is active by default.

custom_attributesDictionary

Allows setting up to 30 custom attributes for the profile. If you use the custom_attributes dictionary, at least one attribute key is required.

Key: Must be a string with no more than 30 characters, using only letters, numbers, dashes, periods, and underscores.

Value: Must be a string or float with no more than 30 characters. Booleans will be converted to floats. To delete an attribute, send an empty value or null.

installation_metaDictionaryContains information about the specific app on a specific device, structured as a dictionary of Installation Meta objects.

Example request

Example request (click to expand)
JSON
{
"first_name": "Jane",
"last_name": "Doe",
"gender": "f",
"email": "[email protected]",
"phone_number": "+1234567890",
"birthday": "2000-12-31",
"ip_country": "FR",
"store_country": "US",
"store": "app_store",
"analytics_disabled": true,
"custom_attributes": [
{
"key": "favourite_sport",
"value": "yoga"
}
],
"installation_meta": {
"device_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"device": "string",
"locale": "en",
"os": "string",
"platform": "iOS",
"timezone": "Europe/Rome",
"user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Mobile/15E148 Safari/604.1",
"idfa": "EA7583CD-A667-48BC-B806-42ECB2B48333",
"idfv": "E9D48DA5-3930-4B41-8521-D953AECD2F33",
"advertising_id": "",
"android_id": "",
"android_app_set_id": ""
}
}

Successful response

200 - Success

NameTypeDescription
Request-IdStringThe Request ID, which is included in all backend logs.
Example: 758f01dfd9e74ccfbabb4934241c4966.
Body:

Profile object

Successful response example
{
"data": {
"app_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"profile_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"customer_user_id": "8612ED7C-3477-466D-93AE-1854B8E5FDD5",
"total_revenue_usd": 109.88999999999999,
"segment_hash": "string",
"timestamp": 0,
"custom_attributes": [
{
"key": "string",
"value": "string"
}
],
"access_levels": [
{
"access_level_id": "premium",
"store": "app_store",
"store_product_id": "weekly_8.99",
"store_base_plan_id": "",
"store_transaction_id": "530001802720333",
"store_original_transaction_id": "530001724306018",
"offer": {
"category": "introductory",
"type": "free_trial",
"id": "offer12"
},
"environment": "Production",
"starts_at": "2022-10-12T09:42:50.000000+0000",
"purchased_at": "2022-10-12T09:42:50.000000+0000",
"originally_purchased_at": "2021-10-12T09:42:50.000000+0000",
"expires_at": "2022-10-12T09:42:50.000000+0000",
"renewal_cancelled_at": "2022-10-12T09:42:50.000000+0000",
"billing_issue_detected_at": "2022-10-12T09:42:50.000000+0000",
"is_in_grace_period": true,
"cancellation_reason": "voluntarily_cancelled"
}
],
"subscriptions": [
{
"store": "app_store",
"store_product_id": "weekly_8.99",
"store_base_plan_id": "",
"store_transaction_id": "530001802720333",
"store_original_transaction_id": "530001724306018",
"offer": {
"offer_category": "introductory",
"offer_type": "free_trial",
"offer_id": "offer12"
},
"environment": "Production",
"purchased_at": "2022-10-12T09:42:50.000000+0000",
"originally_purchased_at": "2021-10-12T09:42:50.000000+0000",
"expires_at": "2022-10-12T09:42:50.000000+0000",
"renewal_cancelled_at": "2022-10-12T09:42:50.000000+0000",
"billing_issue_detected_at": "2022-10-12T09:42:50.000000+0000",
"is_in_grace_period": true,
"cancellation_reason": "voluntarily_cancelled"
}
],
"non_subscriptions": [
{
"purchase_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"store": "app_store",
"store_product_id": "weekly_8.99",
"store_base_plan_id": "",
"store_transaction_id": "530001724306018",
"store_original_transaction_id": "530001724306018",
"purchased_at": "2022-10-12T09:42:50.000000+0000",
"environment": "Production",
"is_refund": true,
"is_consumable": true
}
]
}
}

Errors

400 - Bad request (click to expand)

The request failed due to some error in a field.

Body
ParameterTypeDescription
errorsObject
  • source: (string) Field that caused the error(s)
  • errors: (list) listed errors.
error_codeStringShort error name.
status_codeIntegerHTTP status, always 400.
Response example
{
"errors": [
{
"source": "string",
"errors": [
"string"
]
}
],
"error_code": "string",
"status_code": 0
}
401 - Unauthorized (click to expand)

The request failed due to missing or incorrect authorization. Check the Authorization page, paying close attention to the Authorization header.

The request also failed because the specified profile wasn’t found.

Body:
ParameterTypeDescription
errorsObject
  • source: (string) Always non_field_errors
  • errors: A description of the error.
error_codeStringShort error name. Always not_authenticated.
status_codeIntegerHTTP status. Always 401.
Response example:
{
"errors": [
{
"source": "non_field_errors",
"errors": [
"Authentication credentials were not provided."
]
}
],
"error_code": "not_authenticated",
"status_code": 401
}
404 - Not found (click to expand)

The request failed because the specified profile wasn’t found.

Body
ParameterTypeDescription
errorsObject
  • source: (string) Always non_field_errors
  • errors: A description of the error.
error_codeStringShort error name. Always not_found.
status_codeIntegerHTTP status. Always 404.
Response example
{
"errors": [
{
"source": "non_field_errors",
"errors": [
"Not found."
]
}
],
"error_code": "not_found",
"status_code": 404
}

Delete profile

Deletes an end user of your app in Adapty.

Calling this endpoint enables the deletion of a user's profile and all related data, rendering it inaccessible to the client. Any profile history linked to the deleted profile will be detached, and integration events previously sent to integrations will be deleted from the event feed.

Should another profile make a purchase from the device with the same Apple ID (or when the subscription is restored), the profile history will be reassigned to the new profile, and integration events will be reissued.

Please be aware that this endpoint does not support bulk deletion, therefore each request must be handled individually. For managing a substantial number of users, it is advisable to execute requests concurrently.

Endpoint

https://api.adapty.io/api/v2/server-side-api/profile/

Method

DELETE

Parameters

None in the JSON body. Profile_id or customer_user_id must be set up as a header as described in Authorization.

Successful response

204 - Success

NameTypeDescription
Request-IdStringRequest ID, all backend logs have this id Example: 758f01dfd9e74ccfbabb4934241c4966
401 - Unauthorized (click to expand)

The request failed due to missing or incorrect authorization. Check the Authorization page, paying close attention to the Authorization header.

The request also failed because the specified profile wasn’t found.

Body:
ParameterTypeDescription
errorsObject
  • source: (string) Always non_field_errors
  • errors: A description of the error.
error_codeStringShort error name. Always not_authenticated.
status_codeIntegerHTTP status. Always 401.
Response example:
{
"errors": [
{
"source": "non_field_errors",
"errors": [
"Authentication credentials were not provided."
]
}
],
"error_code": "not_authenticated",
"status_code": 401
}
404 - Not found (click to expand)

The request failed because the specified profile wasn’t found.

Body
ParameterTypeDescription
errorsObject
  • source: (string) Always non_field_errors
  • errors: A description of the error.
error_codeStringShort error name. Always not_found.
status_codeIntegerHTTP status. Always 404.
Response example
{
"errors": [
{
"source": "non_field_errors",
"errors": [
"Not found."
]
}
],
"error_code": "not_found",
"status_code": 404
}

Transactions

One-time purchase object

ParameterTypeRequired in requestNullable in requestDescription
purchase_typeStringThe type of product purchased. Possible value: one_time_purchase.
storeStringStore where the product was bought. Possible values: app_store, play_store, stripe, or the Store ID of your custom store.
environmentStringTransaction environment that provided the access level. Options: Sandbox, Production.
store_product_idStringThe product ID in the app store (App Store, Google Play, Stripe, etc.) that unlocked this access level.
store_transaction_idStringTransaction ID in the app store (App Store, Google Play, Stripe, etc.).
store_original_transaction_idString

For recurring subscriptions, this is the original transaction ID that links the chain of renewals. The original transaction is the first in the chain; later transactions are extensions of it.

If no extensions, store_original_transaction_id matches store_transaction_id.

offerObjectThe offer used for the purchase as an Offer object.
is_family_sharedBooleanA Boolean value indicating whether the product supports family sharing in App Store Connect. iOS only. Always false for iOS below 14.0 and macOS below 11.0.
priceObjectPrice of the subscription or purchase as a Price object. An initial subscription purchase with zero cost is a free trial; a renewal with zero cost is a free renewal.
purchased_atISO 8601 dateThe datetime when the access level was last purchased.
refunded_atISO 8601 dateIf refunded, shows the datetime of the refund.
cancellation_reasonStringPossible reasons for cancellation: voluntarily_cancelled, billing_error, price_increase, product_was_not_available, refund, cancelled_by_developer, new_subscription_replace, upgraded, unknown, adapty_revoked.
variation_idStringThe variation ID used to trace purchases to the specific paywall they were made from.

Subscription object

ParameterTypeRequired in requestNullable in requestDescription
purchase_typeStringThe type of product purchased. Possible value: subscription.
storeStringStore where the product was bought. Options include app_store, play_store, stripe, or the name of your custom store.
environmentStringEnvironment where the transaction took place. Options are Sandbox or Production.
store_product_idStringID of the product in the app store (like App Store, Google Play, Stripe) that unlocked this access level.
store_base_plan_idStringBase plan ID in Google Play or price ID in Stripe.
store_transaction_idStringTransaction ID in the app store (App Store, Google Play, Stripe, etc.).
store_original_transaction_idString

For subscriptions, this ID links to the first transaction in a renewal chain. Each renewal is connected to this original transaction.


If there’s no renewal, store_original_transaction_id matches store_transaction_id.

offerObjectThe offer used in the purchase, provided as an Offer object.
is_family_sharedBooleanA Boolean value indicating whether the product supports family sharing in App Store Connect. iOS only. Always false for iOS below 14.0 and macOS below 11.0.
priceObjectPrice of the subscription as a Price object. An initial subscription purchase with zero cost is a free trial; a renewal with zero cost is a free renewal.
purchased_atISO 8601 dateThe datetime of the most recent access level purchase.
refunded_atISO 8601 dateIf refunded, shows the datetime of the refund.
cancellation_reasonStringPossible reasons for cancellation include: voluntarily_cancelled, billing_error, price_increase, product_was_not_available, refund, upgraded, or unknown.
variation_idStringThe variation ID used to trace purchases to the specific paywall they were made from.
originally_purchased_atISO 8601 dateFor subscription chains, this is the purchase date of the original transaction, linked by store_original_transaction_id.
expires_atISO 8601 dateThe datetime when the access level expires. It may be in the past and may be null for lifetime access.
renew_status
renew_cancelled_atISO 8601 dateThe datetime when auto-renewal was canceled. The subscription can still be active; it just won’t renew automatically. Set to null if the user reactivates.
billing_issue_detected_atISO 8601 dateThe datetime when a billing issue was detected (e.g., a failed card charge). Subscription might still be active. This is cleared if the payment goes through.
is_in_grace_periodBooleanIndicates if the auto-renewable subscription is currently in a grace period.
grace_period_expires_atBooleanIndicates if the auto-renewable subscription is currently in a grace period.

Set transaction

Creates a new transaction for an end user of your app in Adapty and provides access level. The transaction created by this method will appear in your analytics and Event Feed and well as will be sent to all integrations.

This method is recommended over the Grant access level one.

warning

Before setting a transaction, make sure the product is created in Adapty. Without this step, the transaction will still be recorded in the Adapty database, meaning it will appear in analytics and be included in integration events. However, the user won’t get access in the mobile app since no access level will be assigned.

Endpoint

https://api.adapty.io/api/v2/server-side-api/purchase/set/transaction/

Method

POST

Varies based on whether the purchase is a subscription or a one-time purchase.

For subscription
ParameterTypeRequired in requestNullable in requestDescription
purchase_typeStringThe type of product purchased. Possible value: subscription.
storeStringStore where the product was bought. Options include app_store, play_store, stripe, or the name of your custom store.
environmentStringEnvironment where the transaction took place. Options are Sandbox or Production.
store_product_idStringID of the product in the app store (like App Store, Google Play, Stripe) that unlocked this access level.
store_transaction_idStringTransaction ID in the app store (App Store, Google Play, Stripe, etc.).
store_original_transaction_idString

For subscriptions, this ID links to the first transaction in a renewal chain. Each renewal is connected to this original transaction.


If there’s no renewal, store_original_transaction_id matches store_transaction_id.

offerObjectThe offer used in the purchase, provided as an Offer object.
is_family_sharedBooleanA Boolean value indicating whether the product supports family sharing in App Store Connect. iOS only. Always false for iOS below 14.0 and macOS below 11.0.
priceObjectPrice of the subscription or purchase as a Price object. An initial subscription purchase with zero cost is a free trial; a renewal with zero cost is a free renewal.
purchased_atISO 8601 dateThe datetime of the most recent access level purchase.
refunded_atISO 8601 dateThe datetime when the subscription was refunded, if applicable.
cancellation_reasonStringPossible reasons for cancellation include: voluntarily_cancelled, billing_error, price_increase, product_was_not_available, refund, upgraded, or unknown.
variation_idStringThe variation ID used to trace purchases to the specific paywall they were made from.
originally_purchased_atISO 8601 dateFor subscription chains, this is the purchase date of the original transaction, linked by store_original_transaction_id.
expires_atISO 8601 dateThe datetime when the access level expires. It may be in the past and may be null for lifetime access.
renew_statusBooleanIndicates if the subscription auto-renewal is enabled.
renew_status_changed_atISO 8601 dateThe datetime when auto-renewal when auto-renewal was either enabled or disabled.
billing_issue_detected_atISO 8601 dateThe datetime when a billing issue was detected (e.g., a failed card charge). Subscription might still be active. This is cleared if the payment goes through.
grace_period_expires_atISO 8601 dateThe datetime when the grace period will end, if the subscription is currently in one.
For one-time purchase
ParameterTypeRequired in requestNullable in requestDescription
purchase_typeStringThe type of product purchased. Possible value: one_time_purchase.
storeStringStore where the product was bought. Possible values: app_store, play_store, stripe, or the Store ID of your custom store.
environmentStringTransaction environment that provided the access level. Options: Sandbox, Production.
store_product_idStringThe product ID in the app store (App Store, Google Play, Stripe, etc.) that unlocked this access level.
store_transaction_idStringTransaction ID in the app store (App Store, Google Play, Stripe, etc.).
store_original_transaction_idString

For recurring subscriptions, this is the original transaction ID that links the chain of renewals. The original transaction is the first in the chain; later transactions are extensions of it.

If no extensions, store_original_transaction_id matches store_transaction_id.

offerObjectThe offer used for the purchase as an Offer object.
is_family_sharedBooleanA Boolean value indicating whether the product supports family sharing in App Store Connect. iOS only. Always false for iOS below 14.0 and macOS below 11.0.
priceObjectPrice of the subscription or purchase as a Price object. An initial subscription purchase with zero cost is a free trial; a renewal with zero cost is a free renewal.
purchased_atISO 8601 dateThe datetime when the access level was last purchased.
refunded_atISO 8601 dateIf refunded, shows the datetime of the refund.
cancellation_reasonStringPossible reasons for cancellation: voluntarily_cancelled, billing_error, price_increase, product_was_not_available, refund, cancelled_by_developer, new_subscription_replace, upgraded, unknown, adapty_revoked.
variation_idStringThe variation ID used to trace purchases to the specific paywall they were made from.

Request example

Request example (click to expand)
JSON
  "purchase_type": "one_time_purchase",
"store": "app_store",
"environment": "Production",
"store_product_id": "1year.premium",
"store_transaction_id": "30002109551456",
"store_original_transaction_id": "30002109461269",
"offer": {
"category": "introductory",
"type": "free_trial",
"id": "annual_free_trial"
},
"is_family_shared": false,
"price": {
"country": "US",
"currency": "USD",
"value": 0
},
"purchased_at": "2022-10-12T09:42:50.000000+0000",
"refunded_at": "2022-10-12T09:42:50.000000+0000",
"cancellation_reason": "voluntarily_cancelled",
"variation_id": "81109d24-ea95-4806-9ec7-b482bbd1a33d"
}

Successful response: 200 - Success

NameTypeDescription
Request-IdStringThe Request ID, which is included in all backend logs.
Example: 758f01dfd9e74ccfbabb4934241c4966.
Body:

Profile object

Successful response example
{
"data": {
"app_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"profile_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"customer_user_id": "8612ED7C-3477-466D-93AE-1854B8E5FDD5",
"total_revenue_usd": 109.88999999999999,
"segment_hash": "string",
"timestamp": 0,
"custom_attributes": [
{
"key": "string",
"value": "string"
}
],
"access_levels": [
{
"access_level_id": "premium",
"store": "app_store",
"store_product_id": "weekly_8.99",
"store_base_plan_id": "",
"store_transaction_id": "530001802720333",
"store_original_transaction_id": "530001724306018",
"offer": {
"category": "introductory",
"type": "free_trial",
"id": "offer12"
},
"environment": "Production",
"starts_at": "2022-10-12T09:42:50.000000+0000",
"purchased_at": "2022-10-12T09:42:50.000000+0000",
"originally_purchased_at": "2021-10-12T09:42:50.000000+0000",
"expires_at": "2022-10-12T09:42:50.000000+0000",
"renewal_cancelled_at": "2022-10-12T09:42:50.000000+0000",
"billing_issue_detected_at": "2022-10-12T09:42:50.000000+0000",
"is_in_grace_period": true,
"cancellation_reason": "voluntarily_cancelled"
}
],
"subscriptions": [
{
"store": "app_store",
"store_product_id": "weekly_8.99",
"store_base_plan_id": "",
"store_transaction_id": "530001802720333",
"store_original_transaction_id": "530001724306018",
"offer": {
"offer_category": "introductory",
"offer_type": "free_trial",
"offer_id": "offer12"
},
"environment": "Production",
"purchased_at": "2022-10-12T09:42:50.000000+0000",
"originally_purchased_at": "2021-10-12T09:42:50.000000+0000",
"expires_at": "2022-10-12T09:42:50.000000+0000",
"renewal_cancelled_at": "2022-10-12T09:42:50.000000+0000",
"billing_issue_detected_at": "2022-10-12T09:42:50.000000+0000",
"is_in_grace_period": true,
"cancellation_reason": "voluntarily_cancelled"
}
],
"non_subscriptions": [
{
"purchase_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"store": "app_store",
"store_product_id": "weekly_8.99",
"store_base_plan_id": "",
"store_transaction_id": "530001724306018",
"store_original_transaction_id": "530001724306018",
"purchased_at": "2022-10-12T09:42:50.000000+0000",
"environment": "Production",
"is_refund": true,
"is_consumable": true
}
]
}
}

Errors

400 - Bad request

billing_issue_detected_at_date_comparison_error (click to expand)

A billing issue happens when there’s a problem during a subscription renewal attempt, so it always occurs after the transaction date (purchased_at).

To resolve this, make sure the billing issue date (billing_issue_detected_at) is later than the transaction date (purchased_at).

Body
ParameterTypeDescription
errorsObject
  • source: (string) Always billing_issue_detected_at
  • errors: A description of the error.
error_codeStringShort error name. Always billing_issue_detected_at_date_comparison_error.
status_codeIntegerHTTP status. Always 400.
Response example
{
"errors": [
{
"source": "billing_issue_detected_at",
"errors": [
"billing_issue_detected_at must be later than purchased_at."
]
}
],
"error_code": "billing_issue_detected_at_date_comparison_error",
"status_code": 400
}
expires_date_error (click to expand)

A user can’t buy a subscription that has already expired. So, the expires_at date (when the subscription expires) should always be later than the purchased_at date (when the transaction occurred).

To fix this, check these dates and ensure that expires_at is after purchased_at.

Body
ParameterTypeDescription
errorsObject
  • source: (string) Always expires_at
  • errors: A description of the error.
error_codeStringShort error name. Always expires_date_error.
status_codeIntegerHTTP status. Always 400.
Response example
{
"errors": [
{
"source": "expires_at",
"errors": [
"expires_at must be later than purchased_at."
]
}
],
"error_code": "expires_date_error",
"status_code": 400
}
family_share_price_error (click to expand)

The request failed because the is_family_shared parameter is set to true, meaning the access level is shared with a family member for free. However, the value parameter of the Price object isn’t set to zero.

If is_family_shared should be true, make sure to set the value parameter of the Price object to 0.

Parameters
ParameterTypeDescription
errorsObject
  • source: (string) Always is_family_shared
  • errors: A description of the error.
error_codeStringShort error name. Always: family_share_price_error.
status_codeIntegerHTTP status. Always 400.
Response example

The profile is not found

{
"errors": [
{
"source": "is_family_shared",
"errors": [
"If is_family_shared is true, price.value must be 0."
]
}
],
"error_code": "family_share_price_error",
"status_code": 400
}
free_trial_price_error (click to expand)

The request failed because the offer_type parameter is set to free_trial, but the value parameter of the Price object isn’t set to zero.

Another possible reason is that the offer_id parameter was included but left null, even though it can’t be null. In this case, either provide a value for offer_id or remove the parameter entirely.

Parameters
ParameterTypeDescription
errorsObject
  • source: (string) Always offer.type
  • errors: A description of the error.
error_codeStringShort error name. Always: free_trial_price_error.
status_codeIntegerHTTP status. Always 400.
Response example

The profile is not found

{
"errors": [
{
"source": "offer_type",
"errors": [
"If offer_type is 'free_trial', price.value must be 0."
]
}
],
"error_code": "free_trial_price_error",
"status_code": 400
}
grace_period_billing_error (click to expand)

The start of a grace period counts as a billing issue. So, if the grace period has started (indicated by the grace_period_expires_at parameter being filled in), its start date should be recorded in the billing_issue_detected_at parameter.

To fix this, either set the start of the grace period in billing_issue_detected_at or, if the grace period hasn’t started yet, remove the grace_period_expires_at parameter.

Body
ParameterTypeDescription
errorsObject
  • source: (string) Always grace_period_billing_error
  • errors: A description of the error.
error_codeStringShort error name. Always grace_period_billing_error.
status_codeIntegerHTTP status. Always 400.
Response example
{
"errors": [
{
"source": "grace_period_billing_error",
"errors": [
"If grace_period_expires_at is specified, billing_issue_detected_at must also be specified."
]
}
],
"error_code": "grace_period_billing_error",
"status_code": 400
}
grace_period_expires_date_error (click to expand)

The request failed because the offer_type parameter is set to free_trial, but the value parameter of the Price object isn’t set to zero.

Another possible reason is that the offer_id parameter was included but left null, even though it can’t be null. In this case, either provide a value for offer_id or remove the parameter entirely.

Parameters
ParameterTypeDescription
errorsObject
  • source: (string) Always offer.type
  • errors: A description of the error.
error_codeStringShort error name. Always: free_trial_price_error.
status_codeIntegerHTTP status. Always 400.
Response example

The profile is not found

{
"errors": [
{
"source": "offer_type",
"errors": [
"If offer_type is 'free_trial', price.value must be 0."
]
}
],
"error_code": "free_trial_price_error",
"status_code": 400
}
missing_offer_id (click to expand)

The request failed because the offer_category parameter has a value other than introductory or offer_type but doesn’t include an offer_id. In this case, either provide an offer_id or remove the offer_category or offer_type from the request.

Another possible reason is that the offer_id parameter was added but left as null, even though it can’t be null. If that’s the case, either add a value for offer_id or remove the parameter entirely.

Parameters
ParameterTypeDescription
errorsObject
  • source: (string) Always offer.category
  • errors: A description of the error.
error_codeStringShort error name. Possible value: missing_offer_id.
status_codeIntegerHTTP status. Always 400.
Response example

The profile is not found

{
"errors": [
{
"source": "offer_category",
"errors": [
"offer_id must be specified for all offer types except 'introductory'."
]
}
],
"error_code": "missing_offer_id",
"status_code": 400
}
originally_purchased_date_error (click to expand)

profile_does_not_exist (click to expand)

The request failed because the profile in the request header wasn’t found. Double-check that there are no typos in the profile_id or customer_user_id you entered in the request header, and make sure it’s for the correct app.

Parameters
ParameterTypeDescription
errorsObject
  • source: (string) Always non_field_errors
  • errors: A description of the error.
error_codeStringShort error name. Possible value: profile_does_not_exist.
status_codeIntegerHTTP status. Always 400.
Response example

The profile is not found

{
"errors": [
{
"source": "non_field_errors",
"errors": [
"Profile not found"
]
}
],
"error_code": "profile_does_not_exist",
"status_code": 400
}
refund_date_error (click to expand)

The request failed because the purchase date (purchased_at) is earlier than or equal to the refund date (refunded_at). A refund always happens after a purchase, as it reverses the transaction.

To fix this, check the purchased_at and refunded_at parameters and make sure the refund date is later than the purchase date.

Body
ParameterTypeDescription
errorsObject
  • source: (string) Always refunded_at
  • errors: A description of the error.
error_codeStringShort error name. Always refund_date_error.
status_codeIntegerHTTP status. Always 400.
Response example
{
"errors": [
{
"source": "refunded_at",
"errors": [
"refunded_at must be later than purchased_at."
]
}
],
"error_code": "refund_date_error",
"status_code": 400
}
refund_fields_error (click to expand)

The request failed because it either includes cancellation_reason without a refunded_at date, or it has refunded_at without a cancellation_reason.

When a refund is set, both the refund date and reason need to be specified.

Body
ParameterTypeDescription
errorsObject
  • source: (string) Always refunded_at
  • errors: A description of the error.
error_codeStringShort error name. Always refund_fields_error.
status_codeIntegerHTTP status. Always 400.
Response example
{
"errors": [
{
"source": "refunded_at",
"errors": [
"refunded_at and cancellation_reason=refund must be specified together."
]
}
],
"error_code": "refund_fields_error",
"status_code": 400
}
renew_status_changed_date_error (click to expand)

Renewal is a prolongation of a subscription. The user can cancel the subscription prolongation and then again prolong it. The time of both these actions is stored in the renew_status_changed_at parameter. And it can never happen earlier than the transaction itself.

To fix the issue, make sure the renew_status_changed_at is later than the time of the transaction (purchased_at).

Body
ParameterTypeDescription
errorsObject
  • source: (string) Always originally_purchased_at
  • errors: A description of the error.
error_codeStringShort error name. Always originally_purchased_date_error.
status_codeIntegerHTTP status. Always 400.
Response example
{
"errors": [
{
"source": "renew_status_changed_at",
"errors": [
"renew_status_changed_at must be later than purchased_at."
]
}
],
"error_code": "renew_status_changed_date_error",
"status_code": 400
}
store_transaction_id_error (click to expand)

In the case of prolonged subscriptions, a chain of subscriptions is generated. The original transaction is the very first transaction in this chain and the chain is linked by it. Other transactions in the chain are prolongations. If the transaction is the very first purchase in the subscription chain, it can be its own original transaction.

Another case is a one-time purchase. It never creates chains as it cannot have prolongations. For it, the store_transaction_id is always the same as the store_original_transaction_id.

Your request failed because the store_transaction_id value for the One-Time Purchase object differs from its store_original_transaction_id . To fix the issue, either make them the same, or change the object - use Subscription instead of the One-Time Purchase.

Body
ParameterTypeDescription
errorsObject
  • source: (string) Always store_transaction_id
  • errors: A description of the error.
error_codeStringShort error name. Always store_transaction_id_error.
status_codeIntegerHTTP status. Always 400.
Response example
{
"errors": [
{
"source": "store_transaction_id",
"errors": [
"store_transaction_id must be equal to store_original_transaction_id for purchase."
]
}
],
"error_code": "store_transaction_id_error",
"status_code": 400
}

401 - Unauthorized (click to expand)

The request failed due to missing or incorrect authorization. Check the Authorization page, paying close attention to the Authorization header.

The request also failed because the specified profile wasn’t found.

Body:
ParameterTypeDescription
errorsObject
  • source: (string) Always non_field_errors
  • errors: A description of the error.
error_codeStringShort error name. Always not_authenticated.
status_codeIntegerHTTP status. Always 401.
Response example:
{
"errors": [
{
"source": "non_field_errors",
"errors": [
"Authentication credentials were not provided."
]
}
],
"error_code": "not_authenticated",
"status_code": 401
}
404 - Not found (click to expand)

The request failed because the specified profile wasn’t found.

Body
ParameterTypeDescription
errorsObject
  • source: (string) Always non_field_errors
  • errors: A description of the error.
error_codeStringShort error name. Always not_found.
status_codeIntegerHTTP status. Always 404.
Response example
{
"errors": [
{
"source": "non_field_errors",
"errors": [
"Not found."
]
}
],
"error_code": "not_found",
"status_code": 404
}

Access levels

Access level object

Info about current customer’s access level.

ParameterTypeRequired in requestNullable in requestDescription
access_level_idStringID for the Paid Access Level set up in the Adapty Dashboard.
storeStringStore where the product was bought. Options: app_store, play_store, stripe, or the name of your custom store.
store_product_idStringID of the product in the app store (like App Store, Google Play, Stripe) that unlocked this access level.
store_base_plan_idStringBase plan ID in Google Play or price ID in Stripe.
store_transaction_idStringTransaction ID in the app store (App Store, Google Play, Stripe, etc.).
store_original_transaction_idString

For subscriptions, this ID links the original transaction in the chain of renewals. Later transactions are linked as renewals.

If there’s no renewal, store_original_transaction_id matches store_transaction_id.

offerDictionaryDictionary where keys are offer IDs set by the developer in Adapty Dashboard. Values are Offer objects. Can be null if the customer has no access levels.
environmentStringEnvironment for the transaction that granted access. Options: Sandbox, Production.
starts_atISO 8601 dateThe date time when the access level becomes active. Could be in the future.
purchased_atISO 8601 dateThe datetime of the most recent purchase for the access level.
originally_purchased_atISO 8601 dateFor subscriptions, this is the date and time of the very first (original) purchase in the chain, tied to store_original_transaction_id.
expires_atISO 8601 dateThe datetime when the access level expires. Might be in the past, or null for lifetime access.
renewal_cancelled_atISO 8601 dateThe datetime when auto-renewal was turned off for a subscription. The subscription can still be active; it just won’t auto-renew. Set to null if the user reactivates the subscription.
billing_issue_detected_atISO 8601 dateThe datetime when a billing issue was found (like a failed card charge). The subscription might still be active. This is cleared if the payment goes through later.
is_in_grace_periodBooleanShows whether the subscription is in a grace period (only for auto-renewable subscriptions).
cancellation_reasonStringReason for cancellation, with options like: voluntarily_cancelled, billing_error, price_increase, product_was_not_available, refund, upgraded, unknown.

Grant access level

Provides access level to your end-user without providing info on the transaction. This comes in handy if you have bonuses for referrals or other events related to your products.

The access level provided by this method will not be reflected in your analytics. It will be sent to only webhook integration, and only in this case will appear in the Event Feed. If webhook integration is not enabled, granting access level will not be shown in the Event Feed.

To grant access and simultaneously provide the transaction details, please use the Set Transaction request which is recommended.

Endpoint

https://api.adapty.io/api/v2/server-side-api/purchase/profile/grant/access-level/

Method

POST

Parameters

ParameterTypeRequired in requestNullable in requestDescription
access_level_idStringPaid access level ID configured by you in the Access Levels page of the Adapty Dashboard
starts_atISO 8601 dateThe datetime when the access level will be active. Maybe in the future
expires_atISO 8601 dateThe datetime when the access level will expire. It may be in the past and may be null for lifetime access

Example request

Example request (click to expand)
{
"access_level_id": "premium",
"starts_at": "2022-10-12T09:42:50.000000+0000",
"expires_at": "2024-10-12T09:42:50.000000+0000"
}

Successful response: 200 - Success

NameTypeDescription
Request-IdStringThe Request ID, which is included in all backend logs.
Example: 758f01dfd9e74ccfbabb4934241c4966.
Body:

Profile object

Successful response example
{
"data": {
"app_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"profile_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"customer_user_id": "8612ED7C-3477-466D-93AE-1854B8E5FDD5",
"total_revenue_usd": 109.88999999999999,
"segment_hash": "string",
"timestamp": 0,
"custom_attributes": [
{
"key": "string",
"value": "string"
}
],
"access_levels": [
{
"access_level_id": "premium",
"store": "app_store",
"store_product_id": "weekly_8.99",
"store_base_plan_id": "",
"store_transaction_id": "530001802720333",
"store_original_transaction_id": "530001724306018",
"offer": {
"category": "introductory",
"type": "free_trial",
"id": "offer12"
},
"environment": "Production",
"starts_at": "2022-10-12T09:42:50.000000+0000",
"purchased_at": "2022-10-12T09:42:50.000000+0000",
"originally_purchased_at": "2021-10-12T09:42:50.000000+0000",
"expires_at": "2022-10-12T09:42:50.000000+0000",
"renewal_cancelled_at": "2022-10-12T09:42:50.000000+0000",
"billing_issue_detected_at": "2022-10-12T09:42:50.000000+0000",
"is_in_grace_period": true,
"cancellation_reason": "voluntarily_cancelled"
}
],
"subscriptions": [
{
"store": "app_store",
"store_product_id": "weekly_8.99",
"store_base_plan_id": "",
"store_transaction_id": "530001802720333",
"store_original_transaction_id": "530001724306018",
"offer": {
"offer_category": "introductory",
"offer_type": "free_trial",
"offer_id": "offer12"
},
"environment": "Production",
"purchased_at": "2022-10-12T09:42:50.000000+0000",
"originally_purchased_at": "2021-10-12T09:42:50.000000+0000",
"expires_at": "2022-10-12T09:42:50.000000+0000",
"renewal_cancelled_at": "2022-10-12T09:42:50.000000+0000",
"billing_issue_detected_at": "2022-10-12T09:42:50.000000+0000",
"is_in_grace_period": true,
"cancellation_reason": "voluntarily_cancelled"
}
],
"non_subscriptions": [
{
"purchase_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"store": "app_store",
"store_product_id": "weekly_8.99",
"store_base_plan_id": "",
"store_transaction_id": "530001724306018",
"store_original_transaction_id": "530001724306018",
"purchased_at": "2022-10-12T09:42:50.000000+0000",
"environment": "Production",
"is_refund": true,
"is_consumable": true
}
]
}
}

Errors

400 - Bad request

paid_access_level_does_not_exist (click to expand)

The request failed because the access level in the request couldn’t be found. Double-check that there are no typos in the access_level_id and that it matches the correct app.

Parameters
ParameterTypeDescription
errorsObject
  • source: (string) Always non_field_errors
  • errors: A description of the error.
error_codeStringShort error name. Possible value: paid_access_level_does_not_exist.
status_codeIntegerHTTP status. Always 404.
Response example

The access level was not found.

{
"errors": [
{
"source": "non_field_errors",
"errors": [
"Paid access level `premium` does not exist"
]
}
],
"error_code": "paid_access_level_does_not_exist",
"status_code": 400
}
profile_does_not_exist (click to expand)

The request failed because the profile in the request header wasn’t found. Double-check that there are no typos in the profile_id or customer_user_id you entered in the request header, and make sure it’s for the correct app.

Parameters
ParameterTypeDescription
errorsObject
  • source: (string) Always non_field_errors
  • errors: A description of the error.
error_codeStringShort error name. Possible value: profile_does_not_exist.
status_codeIntegerHTTP status. Always 400.
Response example

The profile is not found

{
"errors": [
{
"source": "non_field_errors",
"errors": [
"Profile not found"
]
}
],
"error_code": "profile_does_not_exist",
"status_code": 400
}

401 - Unauthorized (click to expand)

The request failed due to missing or incorrect authorization. Check the Authorization page, paying close attention to the Authorization header.

The request also failed because the specified profile wasn’t found.

Body:
ParameterTypeDescription
errorsObject
  • source: (string) Always non_field_errors
  • errors: A description of the error.
error_codeStringShort error name. Always not_authenticated.
status_codeIntegerHTTP status. Always 401.
Response example:
{
"errors": [
{
"source": "non_field_errors",
"errors": [
"Authentication credentials were not provided."
]
}
],
"error_code": "not_authenticated",
"status_code": 401
}
404 - Not found (click to expand)

The request failed because the specified profile wasn’t found.

Body
ParameterTypeDescription
errorsObject
  • source: (string) Always non_field_errors
  • errors: A description of the error.
error_codeStringShort error name. Always not_found.
status_codeIntegerHTTP status. Always 404.
Response example
{
"errors": [
{
"source": "non_field_errors",
"errors": [
"Not found."
]
}
],
"error_code": "not_found",
"status_code": 404
}

Revoke access level

Removes an access level from an end user of your app in Adapty.

Endpoint

https://api.adapty.io/api/v2/server-side-api/purchase/profile/revoke/access-level/

Method

POST

Parameters

ParameterTypeRequired in requestNullable in requestDescription
access_level_idStringPaid access level ID configured by you in the Access Levels page of the Adapty Dashboard
revoke_atISO 8601 dateThe datetime when the access level will expire. It may be in the past and may be null for lifetime access

Request example

Example request (click to expand)
{
"access_level_id": "premium",
"revoke_at": "2024-10-12T09:42:50.000000+0000"
}

Successful response: 200 - Success

NameTypeDescription
Request-IdStringThe Request ID, which is included in all backend logs.
Example: 758f01dfd9e74ccfbabb4934241c4966.
Body:

Profile object

Successful response example
{
"data": {
"app_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"profile_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"customer_user_id": "8612ED7C-3477-466D-93AE-1854B8E5FDD5",
"total_revenue_usd": 109.88999999999999,
"segment_hash": "string",
"timestamp": 0,
"custom_attributes": [
{
"key": "string",
"value": "string"
}
],
"access_levels": [
{
"access_level_id": "premium",
"store": "app_store",
"store_product_id": "weekly_8.99",
"store_base_plan_id": "",
"store_transaction_id": "530001802720333",
"store_original_transaction_id": "530001724306018",
"offer": {
"category": "introductory",
"type": "free_trial",
"id": "offer12"
},
"environment": "Production",
"starts_at": "2022-10-12T09:42:50.000000+0000",
"purchased_at": "2022-10-12T09:42:50.000000+0000",
"originally_purchased_at": "2021-10-12T09:42:50.000000+0000",
"expires_at": "2022-10-12T09:42:50.000000+0000",
"renewal_cancelled_at": "2022-10-12T09:42:50.000000+0000",
"billing_issue_detected_at": "2022-10-12T09:42:50.000000+0000",
"is_in_grace_period": true,
"cancellation_reason": "voluntarily_cancelled"
}
],
"subscriptions": [
{
"store": "app_store",
"store_product_id": "weekly_8.99",
"store_base_plan_id": "",
"store_transaction_id": "530001802720333",
"store_original_transaction_id": "530001724306018",
"offer": {
"offer_category": "introductory",
"offer_type": "free_trial",
"offer_id": "offer12"
},
"environment": "Production",
"purchased_at": "2022-10-12T09:42:50.000000+0000",
"originally_purchased_at": "2021-10-12T09:42:50.000000+0000",
"expires_at": "2022-10-12T09:42:50.000000+0000",
"renewal_cancelled_at": "2022-10-12T09:42:50.000000+0000",
"billing_issue_detected_at": "2022-10-12T09:42:50.000000+0000",
"is_in_grace_period": true,
"cancellation_reason": "voluntarily_cancelled"
}
],
"non_subscriptions": [
{
"purchase_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"store": "app_store",
"store_product_id": "weekly_8.99",
"store_base_plan_id": "",
"store_transaction_id": "530001724306018",
"store_original_transaction_id": "530001724306018",
"purchased_at": "2022-10-12T09:42:50.000000+0000",
"environment": "Production",
"is_refund": true,
"is_consumable": true
}
]
}
}

Errors

400 - Bad request

paid_access_level_does_not_exist (click to expand)

The request failed because the access level in the request couldn’t be found. Double-check that there are no typos in the access_level_id and that it matches the correct app.

Parameters
ParameterTypeDescription
errorsObject
  • source: (string) Always non_field_errors
  • errors: A description of the error.
error_codeStringShort error name. Possible value: paid_access_level_does_not_exist.
status_codeIntegerHTTP status. Always 404.
Response example

The access level was not found.

{
"errors": [
{
"source": "non_field_errors",
"errors": [
"Paid access level `premium` does not exist"
]
}
],
"error_code": "paid_access_level_does_not_exist",
"status_code": 400
}
profile_does_not_exist (click to expand)

The request failed because the profile in the request header wasn’t found. Double-check that there are no typos in the profile_id or customer_user_id you entered in the request header, and make sure it’s for the correct app.

Parameters
ParameterTypeDescription
errorsObject
  • source: (string) Always non_field_errors
  • errors: A description of the error.
error_codeStringShort error name. Possible value: profile_does_not_exist.
status_codeIntegerHTTP status. Always 400.
Response example

The profile is not found

{
"errors": [
{
"source": "non_field_errors",
"errors": [
"Profile not found"
]
}
],
"error_code": "profile_does_not_exist",
"status_code": 400
}
profile_paid_access_level_does_not_exist (click to expand)

The request failed because the profile in the request doesn’t match the specified access level. Double-check that the profile ID in the header and the access level ID in the body are correct, and make sure there are no typos.

Body
ParameterTypeDescription
errorsObject
  • source: (string) Always non_field_errors
  • errors: A description of the error.
error_codeStringShort error name. Always profile_paid_access_level_does_not_exist.
status_codeIntegerHTTP status. Always 400.
Response example
{
"errors": [
{
"source": "non_field_errors",
"errors": [
"Profile `478b2e7f-d557-4b8b-9c5f-cbd46fc2dee2` has no `premium` access level"
]
}
],
"error_code": "profile_paid_access_level_does_not_exist",
"status_code": 400
}
revocation_date_more_than_expiration_date (click to expand)

The request failed because the revoke_at you defined in the request is later than the current access level expires_at parameter. If you want to prolong the access level, use the Grant access level request.

Body
ParameterTypeDescription
errorsObject
  • source: (string) Always non_field_errors
  • errors: A description of the error.
error_codeStringShort error name. Always revocation_date_more_than_expiration_date.
status_codeIntegerHTTP status. Always 400.
Response example
  {
"errors": [
{
"source": "revoke_at",
"errors": [
"Revocation date (2029-08-29 09:33:42+00:00) is more than current expiration date (2028-08-29 09:33:42+00:00)"
]
}
],
"error_code": "revocation_date_more_than_expiration_date",
"status_code": 400
}

401 - Unauthorized (click to expand)

The request failed due to missing or incorrect authorization. Check the Authorization page, paying close attention to the Authorization header.

The request also failed because the specified profile wasn’t found.

Body:
ParameterTypeDescription
errorsObject
  • source: (string) Always non_field_errors
  • errors: A description of the error.
error_codeStringShort error name. Always not_authenticated.
status_codeIntegerHTTP status. Always 401.
Response example:
{
"errors": [
{
"source": "non_field_errors",
"errors": [
"Authentication credentials were not provided."
]
}
],
"error_code": "not_authenticated",
"status_code": 401
}
404 - Not found (click to expand)

The request failed because the specified profile wasn’t found.

Body
ParameterTypeDescription
errorsObject
  • source: (string) Always non_field_errors
  • errors: A description of the error.
error_codeStringShort error name. Always not_found.
status_codeIntegerHTTP status. Always 404.
Response example
{
"errors": [
{
"source": "non_field_errors",
"errors": [
"Not found."
]
}
],
"error_code": "not_found",
"status_code": 404
}

Purchase in Stripe

Validate purchase, provide access level to customer, and import their transaction history

Validates a purchase using the provided Stripe token using the credentials of Stripe in your App Settings inside Adapty Dashboard. If the purchase is valid, the transaction history is imported from Stripe to the profile in Adapty with the specified customer_user_id. If there was no profile with this customer_user_id before — it will be created.

Profile events are generated along the way and imported transactions are counted towards MTR.

Endpoint

https://api.adapty.io/api/v1/sdk/purchase/stripe/token/validate/

Method

POST

Parameters

warning

This request requires different authorization parameters:

ParameterTypeRequiredNullableDescription
customer_user_idStringThe ID of your user in your system. You can see it in the Customer user ID field on the Adapty Dashboard -> Profiles -> specific profile page. For it to work, you must identify the users in your mobile app code via Adapty SDK
stripe_tokenStringToken of a Stripe object that represents a unique purchase. Could either be a token of Stripe's Subscription (sub_XXX) or Payment Intent (pi_XXX).

Example request

CURL
--location 'https://api.adapty.io/api/v1/sdk/purchase/stripe/token/validate/' \
--header 'Content-Type: application/vnd.api+json' \
--header 'Authorization: Api-Key <PUBLIC_OR_PRIVATE_KEY' \
--data-raw '{
"data": {
"type": "stripe_receipt_validation_result",
"attributes": {
"customer_user_id": "<CUSTOMER_USER_ID>",
"stripe_token": "sub_1OM8brJTlbIG45BdDRFOHWAU"
}
}
}'

Successful response: 200 - Success

{
"data": null
}

Errors

400 Bad request

Contain a list of errors with parameters.

Parameters

ParameterTypeDescription
detailStringDescriptive information about the error.
sourceStringAn object containing a "pointer" that references the exact location in the request document causing the issue
StatusIntegerHTTP status. Always 400

Response example

{
"errors": [
{
"detail": "none is not an allowed value",
"source": {
"pointer": "/data/attributes/stripe_token"
},
"status": "400"
}
]
}