Validate purchase in Paddle, provide access level, and import transaction history from Paddle with server-side API
Validates a purchase using the provided Paddle token using the credentials of Paddle in your App Settings inside Adapty Dashboard. If the purchase is valid, the transaction history is imported from Paddle 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.
Request header
This request requires a different set of headers than usual. Only the Content-Type header can be used.
Note that customer_user_id
should be included in the request body, not in a header, and profile_id
should not be provided at all.
Header | Description |
---|---|
Content-Type | Set to application/json for the API to process the request. |
Method and endpoint
POST https://api.adapty.io/api/v2/server-side-api/purchase/paddle/token/validate/
Example request
- cURL
- Python
- JavaScript
curl --location 'https://api.adapty.io/api/v2/server-side-api/purchase/paddle/token/validate/' \
--header 'Content-Type: application/json' \
--header 'Authorization: Api-Key <YOUR_SECRET_API_KEY>' \
--data '{
"customer_user_id": "<YOUR_CUSTOMER_USER_ID>",
"paddle_token": "live_7d279f61a3339fed520f7cd8c08"
}'
import requests
import json
url = "https://api.adapty.io/api/v2/server-side-api/purchase/paddle/token/validate/"
payload = json.dumps({
"customer_user_id": "<YOUR_CUSTOMER_USER_ID>",
"paddle_token": "live_7d279f61a3339fed520f7cd8c08"
})
headers =
"Content-Type": "application/json",
"Authorization": "Api-Key <YOUR_SECRET_API_KEY>"
}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)
const myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Authorization", "Api-Key <YOUR_SECRET_API_KEY>");
const raw = JSON.stringify({
"customer_user_id": "<YOUR_CUSTOMER_USER_ID>",
"paddle_token": "live_7d279f61a3339fed520f7cd8c08"
});
const requestOptions = {
method: "POST",
headers: myHeaders,
body: raw,
redirect: "follow"
};
fetch("https://api.adapty.io/api/v2/server-side-api/purchase/paddle/token/validate/", requestOptions)
.then((response) => response.text())
.then((result) => console.log(result))
.catch((error) => console.error(error));
Placeholders:
<YOUR_SECRET_API_KEY>
: Your secret API key for authorization.<YOUR_CUSTOMER_USER_ID>
: The unique ID of the customer in your system.
Parameters
:::
Parameters | Type | Required | Nullable | Description |
---|---|---|---|---|
customer_user_id | String | ➕ | ➖ | The 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. |
paddle_token | String | ➕ | ➖ | Token of a Paddle object that represents a unique purchase. Could be either a transaction id (txn_...) or a subscription id (sub_...), |
Successful response: 200: OK
{
"data": {
"app_id": "14c3d623-2f3a-455a-aa86-ef83dff6913b",
"profile_id": "3286abd3-48b0-4e9c-a5f6-ac0a006804a6",
"customer_user_id": "[email protected]",
"total_revenue_usd": 0.0,
"segment_hash": "8f45947bad31ab0c",
"timestamp": 1736436751469,
"custom_attributes": [
{
"key": "favourite_sport",
"value": "yoga"
}
],
"access_levels": [],
"subscriptions": [
{
"purchase_id": "5a7ab471-2299-45f7-ad69-1d395c1256e3",
"store": "app_store",
"store_product_id": "1year.premium",
"store_base_plan_id": null,
"store_transaction_id": "30002109551456",
"store_original_transaction_id": "30002109551456",
"purchased_at": "2022-10-12T09:42:50+00:00",
"environment": "Production",
"is_refund": false,
"is_consumable": false
}
],
"non_subscriptions": []
}
}
Errors
400: Bad request
no_products_found
The request failed because the products linked to the provided token were not found. Please ensure that all required products are added to the correct app in Adapty and that their Paddle Product ID and Paddle Price ID are correctly filled in.
Body
Parameter | Type | Description |
---|---|---|
errors | Object |
|
error_code | String | Short error name. Possible value: no_products_found . |
status_code | Integer | HTTP status. Always 400 . |
Response example
{
"errors": [
{
"source": "non_field_errors",
"errors": [
"No products found for purchase"
]
}
],
"error_code": "no_products_found",
"status_code": 400
}
paddle_api_key_not_found
The request failed because the Paddle API Key in App Settings is incorrect. Please verify that it is accurate and associated with the correct app.
Body
Parameter | Type | Description |
---|---|---|
errors | Object |
|
error_code | String | Short error name. Possible value: paddle_api_key_not_found . |
status_code | Integer | HTTP status. Always 400 . |
Response example
{
"errors": [
{
"source": "non_field_errors",
"errors": [
"Paddle API key not found"
]
}
],
"error_code": "paddle_api_key_not_found",
"status_code": 400
}
invalid_paddle_credentials_or_purchase_not_found
The request failed for one of two reasons: either the Paddle credentials in App Settings are incorrect, or the provided token is invalid. Please check the following:
- The Paddle API Key in App Settings is correct and belongs to the right app.
- The
paddle_token
you're using exists in the app and has no typos in your request.
Body
Parameter | Type | Description |
---|---|---|
errors | Object |
|
error_code | String | Short error name. Possible value: invalid_paddle_credentials_or_purchase_not_found . |
status_code | Integer | HTTP status. Always 400 . |
Response example
{
"errors": [
{
"source": "non_field_errors",
"errors": [
"Invalid Paddle credentials or purchase not found"
]
}
],
"error_code": "invalid_paddle_credentials_or_purchase_not_found",
"status_code": 400
}
401: Unauthorized
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
Parameter | Type | Description |
---|---|---|
errors | Object |
|
error_code | String | Short error name. Always not_authenticated . |
status_code | Integer | HTTP status. Always 401. |
Response example
{
"errors": [
{
"source": "non_field_errors",
"errors": [
"Authentication credentials were not provided."
]
}
],
"error_code": "not_authenticated",
"status_code": 401
}
See also: