What can I do with REST API v2?
REST API v2 follows RevenueCat's v2 paths, objects, list envelope and error format, so existing scripts keep working when you change the base URL and key.
Authenticate with a secret key (Authorization: Bearer sk_...) or the dashboard session cookie. Lists return { "object": "list", "items": [...], "next_page": ..., "url": ... }; follow next_page to page. limit is 1 to 100 (default 20).
RevenueDot-only endpoints are on Extensions.
Base URL: your server, for example http://localhost:8787 or https://revenuedot.example.com. The examples read REVENUEDOT_URL, PUBLIC_KEY, SECRET_KEY and PROJECT_ID from your shell.
Operations on this page (181)#
- Projects: List projects, Create a project
- Apps: List apps, Create an app, Get an app, Update an app and its store credentials, Delete an app, Get an app's public SDK key, Get a StoreKit configuration file, Issue a subscriber access token, List the products in the app's store, Import products from the app's store
- Products: List products, Create a product, Get a product, Update a product, Delete a product, List a product's prices, List a product's prices (old path), Add Test Store prices, Change a Test Store price, Remove a Test Store price, Archive a product, Unarchive a product, Create the product in its store
- Entitlements: List entitlements, Create an entitlement, Get an entitlement, Rename an entitlement, Delete an entitlement, Archive an entitlement, Unarchive an entitlement, List an entitlement's products, Attach products to an entitlement, Detach products from an entitlement
- Offerings: List offerings, Create an offering, Get an offering, Update an offering or make it current, Delete an offering, Duplicate an offering, Archive an offering, Unarchive an offering
- Packages: List an offering's packages, Create a package, Get a package, Update a package, Delete a package, List a package's products, Attach products to a package, Detach products from a package
- Customers: List or search customers, Create a customer, Get a customer, Delete a customer, List a customer's app user ids, List a customer's attributes, Set a customer's attributes, List a customer's active entitlements, List a customer's subscriptions, List a customer's one-time purchases, List a customer's events, Grant an entitlement, Revoke a granted entitlement, Assign an offering to a customer, Transfer a customer's purchases to another customer, Get the Customer Center configuration, Restore a purchase by its store order id, List the win-back offers Apple lets a customer redeem
- Subscriptions: Find subscriptions by store id, Get a subscription, List the entitlements a subscription unlocks, List a subscription's payments, Cancel a subscription (Google Play), Refund and revoke a subscription (Google Play), Extend a subscription, Refund one payment of a subscription (Google Play), Get where the customer manages a subscription
- Purchases: Find one-time purchases by store id, Get a one-time purchase, List the entitlements a purchase unlocks, Refund a one-time purchase (Google Play)
- Metrics: Overview metrics, Revenue over a date range
- Charts: Get chart data, Get available options for a chart, List the customers behind a chart, List chart annotations, Create a chart annotation, Get a chart annotation, Update a chart annotation, Delete a chart annotation, List active chart share links, Create a public chart share link, Revoke a chart share link, List saved charts, Save a chart view, Get a saved chart, Rename or update a saved chart, Delete a saved chart
- In-app currencies: List in-app currencies, Create an in-app currency, Get an in-app currency, Update an in-app currency, Delete an in-app currency, Archive an in-app currency, Unarchive an in-app currency, List a customer's balances, Credit or spend in-app currency, Change a balance without a ledger entry
- Audit log: List audit log entries
- Targeting: List audiences, Create an audience, Preview who matches, Known values for attribution and custom-attribute fields, Get an audience, Update an audience, Delete an unused audience, List targeting rules in order, Create a targeting rule, Set the evaluation order, Get a targeting rule, Update a targeting rule, Delete a targeting rule
- Experiments: List experiments, Create an offering experiment, Set the enrollment order, Estimate how many customers would join, Get an experiment, Update an experiment, Delete an experiment, Start or resume, Pause: enrolled customers keep their variant, nobody new joins, Stop for good, Results per variant, Export results as CSV
- Paywalls: List paywalls, Create a paywall, Get a paywall, Update a paywall's draft, Delete a paywall, Publish a paywall, Unpublish a paywall, Attach an offering to a paywall, Detach the offering from a paywall, Duplicate a paywall, List saved snapshots, Save a named snapshot, Restore a snapshot into the draft, Get a snapshot, Get the template form of a paywall, Store the template form of a paywall, List the template gallery, Validate paywall components, Whether the AI generator is available, Generate a paywall with AI, List images, Upload an image, List fonts, Upload a font, Download a paywall image or font, Download a built-in paywall icon, Delete an uploaded font
- Webhook integrations: List webhooks, Create a webhook, Get a webhook, Update a webhook, Delete a webhook
- Discounts: List discounts, Create a discount, Get a discount, Update a discount, Delete a discount, Enable a discount, Disable a discount, List a discount's codes, Create discount codes, Delete a discount code
- Invoices: List a customer's invoices, Download an invoice
- Collaborators: List collaborators
Projects#
Projects hold apps, the catalog, customers and webhooks.
List projects#
GET /v2/projects · Auth: secret key or dashboard session · Permissions: project_configuration:projects:read
A secret key sees its own project. A dashboard session sees every project the user is a member of.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of results. Returns a list of Project.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
Create a project#
POST /v2/projects · Auth: dashboard session · Permissions: project_configuration:projects:read_write
Needs a dashboard session: a secret key belongs to one project and cannot create another. The caller becomes the project's admin.
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects" \
-H "Content-Type: application/json" -d '{"name":"Scanner"}'Responses
- 200: The project. Returns Project.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
Apps#
One app per store, each with its public SDK key and store credentials.
List apps#
GET /v2/projects/{project_id}/apps · Auth: secret key or dashboard session · Permissions: project_configuration:apps:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/apps" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of results. Returns a list of App.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Create an app#
POST /v2/projects/{project_id}/apps · Auth: secret key or dashboard session · Permissions: project_configuration:apps:read_write
One app per store. app_store and mac_app_store need bundle_id; play_store, amazon and galaxy need package_name. The app gets a public SDK key with the store's prefix (pdl_ for Paddle, roku_ for Roku, galx_ for the Galaxy Store).
Other fields in the store object are saved as store credentials (for example subscription_private_key, subscription_key_id, subscription_key_issuer, play_service_account_credentials_json, Amazon's shared_secret, Stripe's stripe_secret_key and stripe_webhook_secret, Paddle's paddle_api_key and paddle_webhook_secret, Roku's roku_api_key, the Galaxy Store's galaxy_service_account_id, galaxy_service_account_private_key and galaxy_iap_public_key). Secrets are sealed and never returned. A Stripe publishable key (pk_…), a Paddle client-side token or a malformed signing secret is refused with 400.
Store commission programs (RevenueDot extension, as RevenueCat's app settings): app_store.small_business_program and mac_app_store.small_business_program (Apple's Small Business Program, 15% instead of 30%) and amazon.small_business_accelerator (Amazon's Small Business Accelerator Program, 20% instead of 30%) take { "enrolled": true, "periods": [{ "entry_date": "2024-01-01", "exit_date": null }] }: up to 10 periods, YYYY-MM-DD, an exit date after its entry date, no overlaps; null removes the program. Proceeds in charts, metrics, exports and the REST API are recomputed for the dates; webhooks and integration events already sent keep their values.
galaxy is a RevenueDot extension: RevenueCat's v2 API has no Galaxy app object.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | |
type |
amazon, app_store, mac_app_store, play_store, stripe, rc_billing, roku, paddle, test_store, galaxy |
yes | |
app_store |
object | no | bundle_id plus optional credentials. |
mac_app_store |
object | no | |
play_store |
object | no | package_name plus optional credentials. |
amazon |
object | no | |
stripe |
object | no | |
rc_billing |
object or null | no | |
roku |
object or null | no | roku_api_key (sealed), roku_channel_id, roku_channel_name. |
paddle |
object or null | no | paddle_api_key (sealed; pdl_live_apikey_… or pdl_sdbx_apikey_…), paddle_is_sandbox (only for keys made before May 2025), paddle_webhook_secret (sealed). |
galaxy |
object | no | package_name plus galaxy_service_account_id, galaxy_service_account_private_key (sealed) and the optional galaxy_iap_public_key. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/apps" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"name":"Scanner (iOS)","type":"app_store","app_store":{"bundle_id":"com.example.scanner"}}'Responses
- 201: The app. Returns App.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 201 response:
{
"object": "app",
"id": "appugfw01uy",
"name": "Scanner (iOS)",
"created_at": 1790801342594,
"type": "app_store",
"project_id": "proj18pzzkao",
"custom_url_scheme": "rc-4d13549313",
"app_store": {
"bundle_id": "com.example.scanner",
"app_store_connect_api_key_configured": false,
"subscription_key_configured": false,
"app_store_connect_vendor_number": null
}
}Get an app#
GET /v2/projects/{project_id}/apps/{app_id} · Auth: secret key or dashboard session · Permissions: project_configuration:apps:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
app_id |
string | yes | App id (app...). |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/apps/$APP_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The app. Returns App.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Update an app and its store credentials#
POST /v2/projects/{project_id}/apps/{app_id} · Auth: secret key or dashboard session · Permissions: project_configuration:apps:read_write
Send only the store object of the app's own type. A field set to null removes that credential; other values replace it.
RevenueDot extensions in the store object: notification_forward_url (copy store notifications to another URL, for example RevenueCat during a dual run; null or "" turns it off), track_new_purchases, allow_unsigned_receipts, xcode_certificate, app_apple_id, pubsub_audience, pubsub_service_account; Amazon shared_secret, sns_topic_arn; Stripe stripe_secret_key, stripe_webhook_secret, stripe_account_id, app_user_id_source (metadata, customer_id, anonymous), app_user_id_metadata_key, register_on (invoice_paid, invoice_created); Paddle paddle_webhook_secret, app_user_id_source (custom_data, anonymous), app_user_id_custom_data_key; Galaxy Store galaxy_iap_public_key. See App Store setup, Google Play setup, Amazon Appstore setup, Stripe setup, Paddle setup, Roku setup and Galaxy Store setup.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
app_id |
string | yes | App id (app...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | no | |
app_store |
object | no | |
mac_app_store |
object | no | |
play_store |
object | no | |
amazon |
object | no | |
stripe |
object | no | |
rc_billing |
object | no | |
roku |
object | no | |
paddle |
object | no | |
galaxy |
object | no |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/apps/$APP_ID" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"app_store":{"subscription_private_key":"-----BEGIN PRIVATE KEY-----\n…\n-----END PRIVATE KEY-----","subscription_key_id":"ABC123DEFG","subscription_key_issuer":"57246542-96fe-1a63-e053-0824d011072a"}}'Responses
- 200: The app. Returns App.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Delete an app#
DELETE /v2/projects/{project_id}/apps/{app_id} · Auth: secret key or dashboard session · Permissions: project_configuration:apps:read_write
Deletes the app and its products. Purchase history stays.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
app_id |
string | yes | App id (app...). |
Example request
curl -s -X DELETE "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/apps/$APP_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: Deleted. Returns Deleted.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "app",
"id": "…",
"deleted_at": 1790801342625
}Get an app's public SDK key#
GET /v2/projects/{project_id}/apps/{app_id}/public_api_keys · Auth: secret key or dashboard session · Permissions: project_configuration:apps:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
app_id |
string | yes | App id (app...). |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/apps/$APP_ID/public_api_keys" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: One key. Returns a list of PublicApiKey.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "list",
"items": [
{
"object": "public_api_key",
"id": "pk_appvnrm0a5h",
"key": "test_ea5120a23e7b9626f8eff225a627762c",
"environment": "sandbox",
"app_id": "appvnrm0a5h",
"created_at": 1790800900758
}
],
"next_page": null,
"url": "/v2/projects/proj18pzzkao/apps/appvnrm0a5h/public_api_keys"
}Get a StoreKit configuration file#
GET /v2/projects/{project_id}/apps/{app_id}/store_kit_config · Auth: secret key or dashboard session · Permissions: project_configuration:apps:read
A .storekit file for the products of an App Store app, to test purchases locally in Xcode. Prices come from the Test Store price when set, else 0.99.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
app_id |
string | yes | App id (app...). |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/apps/$APP_ID/store_kit_config" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The file contents.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Issue a subscriber access token#
POST /v2/projects/{project_id}/apps/{app_id}/authenticate · Auth: secret key or dashboard session · Permissions: iam:authorization:issue_token
A short-lived access token (one hour) for one app user id of one app. Send it as Authorization: Bearer rdat_... to the subscriber token endpoints (/v1/customer, /v1/customer/offerings ...), which answer for that app user id, or to any SDK endpoint for that app user id. It cannot be refreshed: ask for a new one. Only a hash of the token is stored.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
app_id |
string | yes | App id (app...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
app_user_id |
string | yes | The app user id the token speaks for. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/apps/$APP_ID/authenticate" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"app_user_id":"user_1"}'Responses
- 200: The token.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "authentication",
"access_token": "rdat_3f0c1d0b9e8a7c6b5a4f3e2d1c0b9a8f7e6d5c4b3a2f1e0d9c8b7a6f5e4d3c2b",
"expires_at": 1790804514034
}List the products in the app's store#
GET /v2/projects/{project_id}/apps/{app_id}/store_products · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:products:read
What the app's store has, read with the credentials the app already has, every page of the store's lists, each item marked in_catalog. Nothing in the store changes. See Import products.
- App Store and Mac App Store: the App Store Connect API key (
app_store_connect_api_key,_id,_issuer), a team key with the App Manager role. Subscriptions by subscription group, then in-app purchases (consumable, non-consumable, non-renewing). - Google Play: the service account, with "View app information and download bulk reports (read-only)" in Play Console. One item per subscription base plan (
subscription_id:base_plan_id) with its billing period, then one-time products (monetization.onetimeproducts, or the legacyinappproductslist where that API is not available). - Stripe: the app's restricted key with "Products" read (it covers prices). One item per active price; recurring prices are subscriptions.
422 unprocessable_entity_error when the credential is missing or the store refuses it (the message names the role or permission), for Amazon (it has no API that lists in-app items; add products by SKU) and the Test Store. 422 store_error with retryable: true while the store cannot be reached.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
app_id |
string | yes | App id (app...). |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/apps/$APP_ID/store_products" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The store's products.
next_pageis always null: every store page has been read. - 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
Example 200 response:
{
"object": "list",
"next_page": null,
"url": "/v2/projects/proj18pzzkao/apps/app1a2b3c4d/store_products",
"app_id": "app1a2b3c4d",
"store": "app_store",
"warnings": [],
"items": [
{
"object": "store_product_listing",
"store_identifier": "pro_monthly",
"type": "subscription",
"display_name": "Pro Monthly",
"duration": "P1M",
"store_state": "APPROVED",
"group": {
"id": "21000001",
"name": "Pro"
},
"price": null,
"importable": true,
"note": null,
"in_catalog": true,
"product_id": "prod1a2b3c4d5e"
},
{
"object": "store_product_listing",
"store_identifier": "pro_annual",
"type": "subscription",
"display_name": "Pro Annual",
"duration": "P1Y",
"store_state": "APPROVED",
"group": {
"id": "21000001",
"name": "Pro"
},
"price": null,
"importable": true,
"note": null,
"in_catalog": false,
"product_id": null
},
{
"object": "store_product_listing",
"store_identifier": "coins_100",
"type": "consumable",
"display_name": "100 coins",
"duration": null,
"store_state": "APPROVED",
"group": null,
"price": null,
"importable": true,
"note": null,
"in_catalog": false,
"product_id": null
}
]
}Import products from the app's store#
POST /v2/projects/{project_id}/apps/{app_id}/store_products/actions/import · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:products:read_write
Creates catalog products for the chosen store identifiers. The store is read again, so the type, duration and display name always come from the store. Products already in the catalog are left as they are and reported in existing; identifiers the store does not have, or cannot be imported, are reported in failed. Every created and existing product is attached to the entitlement_ids. With entitlement_ids, an API key also needs project_configuration:entitlements:read_write. Running the same import twice changes nothing. Flat-rate Stripe prices also become web products, so the web checkout can sell them.
201 when at least one product was created, else 200. Errors as for listing; 400 for an entitlement_ids entry that is not an entitlement of the project.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
app_id |
string | yes | App id (app...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
store_identifiers |
array of string | yes | store_identifier values from the list. |
entitlement_ids |
array of string | no | Entitlements to attach the products to. Optional. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/apps/$APP_ID/store_products/actions/import" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"store_identifiers":["pro_annual","coins_100"],"entitlement_ids":["entl1a2b3c4d5e"]}'Responses
- 200: Nothing new was created. Returns StoreProductImport.
- 201: Products were created. Returns StoreProductImport.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
Products#
Store products.
List products#
GET /v2/projects/{project_id}/products · Auth: secret key or dashboard session · Permissions: project_configuration:products:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
app_id |
string | no | Only this app's products. |
expand |
array of items.app, items.indicative_price, items.store_details |
no | items.app embeds each product's app. items.indicative_price adds each product's indicative price: the Test Store price, else the store price from the last price read (United States first), else the Stripe web product's price. items.store_details (RevenueDot extension) adds each product's store status and price; see store prices. |
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/products" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of results. Returns a list of Product.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Create a product#
POST /v2/projects/{project_id}/products · Auth: secret key or dashboard session · Permissions: project_configuration:products:read_write
store_identifier is the store's product id. For Google Play subscriptions use subscriptionId:basePlanId; for an App Store monthly billing plan with a 12-month commitment (iOS 26.4) use productId:monthly. Set subscription.duration (ISO 8601, for example P1M): the Test Store uses it as the period, and MRR uses it for every store. test_store_price sets what the SDK shows for a Test Store product.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
expand |
array of indicative_price, store_details |
no | indicative_price adds RevenueCat's IndicativePrice: the Test Store price; else the App Store or Google Play price in the United States from the last store price read (or the in-app purchase's base territory, or the first territory with a price); else the Stripe web product's price; null when none is known. store_details (RevenueDot extension) adds the store's status, base price, number of priced territories and when they were read. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
store_identifier |
string | yes | |
app_id |
string | yes | |
type |
subscription, one_time, consumable, non_consumable, non_renewing_subscription |
yes | |
display_name |
string or null | no | |
title |
string or null | no | Alias of display_name. |
price_identifier |
string or null | no | Accepted and ignored. |
subscription |
object or null | no | |
subscription.duration |
string or null | no | ISO 8601 period such as P1W, P1M, P1Y or P3D. |
test_store_price |
object or null | no | RevenueDot extension. The default Test Store price (Test Store products only): it adds or updates the price in this currency and keeps the other currencies. Null clears every price. Read it back with expand=indicative_price; list every currency with GET …/prices. |
test_store_price.amount_micros |
integer | yes | Price in micros: 9.99 is 9990000. |
test_store_price.currency |
string | yes | ISO 4217 code such as USD or EUR. A code with no exchange rate to USD is refused (its purchases would record no revenue). |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/products" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"store_identifier":"pro_monthly","app_id":"appvnrm0a5h","type":"subscription","display_name":"Pro monthly","subscription":{"duration":"P1M"}}'Responses
- 201: The product. Returns Product.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 409: It already exists, or it conflicts with another object. Returns V2Error.
Example 201 response:
{
"object": "product",
"id": "prode0zhpfisko",
"store_identifier": "pro_monthly",
"type": "subscription",
"state": "active",
"subscription": {
"duration": "P1M",
"grace_period_duration": null,
"trial_duration": null
},
"created_at": 1790800900948,
"app_id": "appvnrm0a5h",
"display_name": "Pro monthly"
}Get a product#
GET /v2/projects/{project_id}/products/{product_id} · Auth: secret key or dashboard session · Permissions: project_configuration:products:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
product_id |
string | yes | Product id (prod...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
expand |
array of app, indicative_price, store_details |
no | app embeds the app. indicative_price adds RevenueCat's IndicativePrice: the Test Store price; else the App Store or Google Play price in the United States from the last store price read (or the in-app purchase's base territory, or the first territory with a price); else the Stripe web product's price; null when none is known. store_details (RevenueDot extension) adds the store's status, base price, number of priced territories and when they were read. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/products/$PRODUCT_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The product. Returns Product.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "product",
"id": "prode0zhpfisko",
"store_identifier": "pro_monthly",
"type": "subscription",
"state": "active",
"subscription": {
"duration": "P1M",
"grace_period_duration": null,
"trial_duration": null
},
"created_at": 1790800900948,
"app_id": "appvnrm0a5h",
"display_name": "Pro monthly"
}Update a product#
POST /v2/projects/{project_id}/products/{product_id} · Auth: secret key or dashboard session · Permissions: project_configuration:products:read_write
RevenueDot also lets you correct type and subscription.duration (null clears it), and set or clear test_store_price.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
product_id |
string | yes | Product id. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
expand |
array of app, indicative_price, store_details |
no | indicative_price adds RevenueCat's IndicativePrice: the Test Store price; else the App Store or Google Play price in the United States from the last store price read (or the in-app purchase's base territory, or the first territory with a price); else the Stripe web product's price; null when none is known. store_details (RevenueDot extension) adds the store's status, base price, number of priced territories and when they were read. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
display_name |
string | no | |
type |
subscription, one_time, consumable, non_consumable, non_renewing_subscription |
no | |
subscription |
object | no | |
subscription.duration |
string or null | no | |
test_store_price |
object or null | no | RevenueDot extension. The default Test Store price (Test Store products only): it adds or updates the price in this currency and keeps the other currencies. Null clears every price. Read it back with expand=indicative_price; list every currency with GET …/prices. |
test_store_price.amount_micros |
integer | yes | Price in micros: 9.99 is 9990000. |
test_store_price.currency |
string | yes | ISO 4217 code such as USD or EUR. A code with no exchange rate to USD is refused (its purchases would record no revenue). |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/products/$PRODUCT_ID" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"display_name":"Pro (monthly)","test_store_price":{"amount_micros":9990000,"currency":"USD"}}'Responses
- 200: The product. Returns Product.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Delete a product#
DELETE /v2/projects/{project_id}/products/{product_id} · Auth: secret key or dashboard session · Permissions: project_configuration:products:read_write
Detaches it from entitlements and packages. Purchase history keeps the store id.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
product_id |
string | yes | Product id. |
Example request
curl -s -X DELETE "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/products/$PRODUCT_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: Deleted. Returns Deleted.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "product",
"id": "…",
"deleted_at": 1790801342625
}List a product's prices#
GET /v2/projects/{project_id}/products/{product_id}/prices · Auth: secret key or dashboard session · Permissions: project_configuration:products:read
Every price of a Test Store product, one per currency, the default first (the default is what indicative_price shows and what a customer whose currency has no price sees). For a Stripe web product, its Stripe price (id is the Stripe price id). Other products answer 400. The answer is a JSON array, not a list object, as in RevenueCat.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
product_id |
string | yes | Product id. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/products/$PRODUCT_ID/prices" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The prices.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
[
{
"id": "prcx8k2m4q9w1z7d",
"currency": "USD",
"amount_micros": 9990000
},
{
"id": "prc3n6p0r8t2v4b5",
"currency": "EUR",
"amount_micros": 8990000
}
]List a product's prices (old path)#
GET /v2/projects/{project_id}/products/{product_id}/test_store_prices · Auth: secret key or dashboard session · Permissions: project_configuration:products:read
The same as GET …/prices. Deprecated in RevenueCat; use …/prices.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
product_id |
string | yes | Product id. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/products/$PRODUCT_ID/test_store_prices" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The prices.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Add Test Store prices#
POST /v2/projects/{project_id}/products/{product_id}/test_store_prices · Auth: secret key or dashboard session · Permissions: project_configuration:products:read_write
Adds a price in each currency (Test Store products only; other products answer 400). Each currency once per request. A currency the product already has gets the new amount. A product without a price gets USD as its default when USD is among them, else the first one. The SDK shows each customer the price in their storefront's currency, else the default. Answers the prices it wrote, as an array.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
product_id |
string | yes | Product id. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
prices |
array of object | yes | |
prices[].currency |
string | yes | ISO 4217 code such as EUR. A code with no exchange rate to USD is refused. |
prices[].amount_micros |
integer | yes | Price in micros: 8.99 is 8990000. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/products/$PRODUCT_ID/test_store_prices" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"prices":[{"currency":"EUR","amount_micros":8990000},{"currency":"GBP","amount_micros":7990000}]}'Responses
- 201: The prices written.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 201 response:
[
{
"id": "prc3n6p0r8t2v4b5",
"currency": "EUR",
"amount_micros": 8990000
},
{
"id": "prc9d1f3h5j7l2c4",
"currency": "GBP",
"amount_micros": 7990000
}
]Change a Test Store price#
PATCH /v2/projects/{project_id}/products/{product_id}/prices/{currency} · Auth: secret key or dashboard session · Permissions: project_configuration:products:read_write
Sets the amount of the product's price in this currency. 404 when the product has no price in it (add one with POST …/test_store_prices).
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
product_id |
string | yes | Product id. |
currency |
string | yes | ISO 4217 code of the price, such as EUR. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
amount_micros |
integer | yes | Price in micros. |
Example request
curl -s -X PATCH "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/products/$PRODUCT_ID/prices/$CURRENCY" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"amount_micros":9490000}'Responses
- 200: The price. Returns ProductPrice.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"id": "prc3n6p0r8t2v4b5",
"currency": "EUR",
"amount_micros": 9490000
}Remove a Test Store price#
DELETE /v2/projects/{project_id}/products/{product_id}/prices/{currency} · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:products:read_write
RevenueDot extension (RevenueCat has no way to remove a currency). Removing the default price makes USD, else the first remaining currency, the default; removing the last price leaves the product without one.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
product_id |
string | yes | Product id. |
currency |
string | yes | ISO 4217 code of the price. |
Example request
curl -s -X DELETE "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/products/$PRODUCT_ID/prices/$CURRENCY" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: Removed.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "product_price",
"id": "prcx8k2m4n7q1z",
"currency": "GBP",
"deleted_at": 1790801342625
}Archive a product#
POST /v2/projects/{project_id}/products/{product_id}/actions/archive · Auth: secret key or dashboard session · Permissions: project_configuration:products:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
product_id |
string | yes | Product id. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/products/$PRODUCT_ID/actions/archive" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The archived product. Returns Product.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Unarchive a product#
POST /v2/projects/{project_id}/products/{product_id}/actions/unarchive · Auth: secret key or dashboard session · Permissions: project_configuration:products:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
product_id |
string | yes | Product id. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/products/$PRODUCT_ID/actions/unarchive" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The product. Returns Product.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Create the product in its store#
POST /v2/projects/{project_id}/products/{product_id}/create_in_store · Auth: secret key or dashboard session · Permissions: project_configuration:products:read_write
Creates a catalog product in the store its app belongs to. The product's display name becomes the store name.
- App Store (needs the app's App Store Connect API key with the App Manager role:
app_store_connect_api_key,app_store_connect_api_key_id,app_store_connect_api_key_issuer). A subscription needsstore_informationwithdurationandsubscription_group_name(orsubscription_group_id); the group is reused by name or created. Consumables, non-consumables and non-renewing subscriptions need no body. Add prices, a review screenshot and localizations in App Store Connect before submitting. - Google Play (a RevenueDot addition; needs the service account with "Manage store presence"): a subscription with one listing in the app's default language. Add its base plans and prices in Play Console. One-time products answer 422, because Google needs a price to create them.
409 when the store already has the product id, 422 without the credentials or for another store, 422 store_error with retryable: true while the store is down.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
product_id |
string | yes | Product id (prod...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
store_information |
object or object | no | App Store subscriptions only. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/products/$PRODUCT_ID/create_in_store" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"store_information":{"duration":"ONE_MONTH","subscription_group_name":"Pro"}}'Responses
- 201: The product in the store.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 409: It already exists, or it conflicts with another object. Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
Example 201 response:
{
"created_product": {
"object": "store_product",
"id": "6743920115",
"name": "Pro Monthly",
"product_identifier": "pro_monthly"
}
}Entitlements#
The access your app checks, unlocked by products.
List entitlements#
GET /v2/projects/{project_id}/entitlements · Auth: secret key or dashboard session · Permissions: project_configuration:entitlements:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
expand |
array of items.product |
no | items.product embeds the attached products. |
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/entitlements" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of results. Returns a list of Entitlement.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Create an entitlement#
POST /v2/projects/{project_id}/entitlements · Auth: secret key or dashboard session · Permissions: project_configuration:entitlements:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
lookup_key |
string | yes | What apps check, for example pro. |
display_name |
string | yes |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/entitlements" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"lookup_key":"pro","display_name":"Pro access"}'Responses
- 201: The entitlement. Returns Entitlement.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 409: It already exists, or it conflicts with another object. Returns V2Error.
Example 201 response:
{
"object": "entitlement",
"id": "entl1v0bp6r0qs",
"project_id": "proj18pzzkao",
"lookup_key": "pro",
"display_name": "Pro access",
"created_at": 1790800901115,
"state": "active"
}Get an entitlement#
GET /v2/projects/{project_id}/entitlements/{entitlement_id} · Auth: secret key or dashboard session · Permissions: project_configuration:entitlements:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
entitlement_id |
string | yes | Entitlement id (entl...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
expand |
array of product |
no | product embeds the attached products. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/entitlements/$ENTITLEMENT_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The entitlement. Returns Entitlement.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "entitlement",
"id": "entl1v0bp6r0qs",
"project_id": "proj18pzzkao",
"lookup_key": "pro",
"display_name": "Pro access",
"created_at": 1790800901115,
"state": "active"
}Rename an entitlement#
POST /v2/projects/{project_id}/entitlements/{entitlement_id} · Auth: secret key or dashboard session · Permissions: project_configuration:entitlements:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
entitlement_id |
string | yes | Entitlement id. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
display_name |
string | yes |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/entitlements/$ENTITLEMENT_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The entitlement. Returns Entitlement.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Delete an entitlement#
DELETE /v2/projects/{project_id}/entitlements/{entitlement_id} · Auth: secret key or dashboard session · Permissions: project_configuration:entitlements:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
entitlement_id |
string | yes | Entitlement id. |
Example request
curl -s -X DELETE "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/entitlements/$ENTITLEMENT_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: Deleted. Returns Deleted.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "entitlement",
"id": "…",
"deleted_at": 1790801342625
}Archive an entitlement#
POST /v2/projects/{project_id}/entitlements/{entitlement_id}/actions/archive · Auth: secret key or dashboard session · Permissions: project_configuration:entitlements:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
entitlement_id |
string | yes | Entitlement id. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/entitlements/$ENTITLEMENT_ID/actions/archive" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The archived entitlement. Returns Entitlement.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Unarchive an entitlement#
POST /v2/projects/{project_id}/entitlements/{entitlement_id}/actions/unarchive · Auth: secret key or dashboard session · Permissions: project_configuration:entitlements:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
entitlement_id |
string | yes | Entitlement id. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/entitlements/$ENTITLEMENT_ID/actions/unarchive" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The entitlement. Returns Entitlement.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
List an entitlement's products#
GET /v2/projects/{project_id}/entitlements/{entitlement_id}/products · Auth: secret key or dashboard session · Permissions: project_configuration:entitlements:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
entitlement_id |
string | yes | Entitlement id. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/entitlements/$ENTITLEMENT_ID/products" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of results. Returns a list of Product.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Attach products to an entitlement#
POST /v2/projects/{project_id}/entitlements/{entitlement_id}/actions/attach_products · Auth: secret key or dashboard session · Permissions: project_configuration:entitlements:read_write
Any of these products unlocks the entitlement. Every id must belong to the project, or nothing changes.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
entitlement_id |
string | yes | Entitlement id. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
product_ids |
array of string | yes |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/entitlements/$ENTITLEMENT_ID/actions/attach_products" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"product_ids":["prode0zhpfisko","prodz2c0dt6z9x"]}'Responses
- 200: The entitlement with its products. Returns Entitlement.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Detach products from an entitlement#
POST /v2/projects/{project_id}/entitlements/{entitlement_id}/actions/detach_products · Auth: secret key or dashboard session · Permissions: project_configuration:entitlements:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
entitlement_id |
string | yes | Entitlement id. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
product_ids |
array of string | yes |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/entitlements/$ENTITLEMENT_ID/actions/detach_products" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The entitlement with its products. Returns Entitlement.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Offerings#
Groups of packages the paywall shows.
List offerings#
GET /v2/projects/{project_id}/offerings · Auth: secret key or dashboard session · Permissions: project_configuration:offerings:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
expand |
array of items.package, items.package.product |
no | Embed packages, and their products. |
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/offerings" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of results. Returns a list of Offering.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Create an offering#
POST /v2/projects/{project_id}/offerings · Auth: secret key or dashboard session · Permissions: project_configuration:offerings:read_write
The project's first offering becomes current.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
lookup_key |
string | yes | |
display_name |
string | yes | |
metadata |
object or null | no |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/offerings" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"lookup_key":"default","display_name":"Standard plans"}'Responses
- 201: The offering. Returns Offering.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 409: It already exists, or it conflicts with another object. Returns V2Error.
Get an offering#
GET /v2/projects/{project_id}/offerings/{offering_id} · Auth: secret key or dashboard session · Permissions: project_configuration:offerings:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
offering_id |
string | yes | Offering id (ofrng...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
expand |
array of package, package.product |
no | Embed packages, and their products. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/offerings/$OFFERING_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The offering. Returns Offering.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Update an offering or make it current#
POST /v2/projects/{project_id}/offerings/{offering_id} · Auth: secret key or dashboard session · Permissions: project_configuration:offerings:read_write
is_current: true makes it the only current offering. An archived offering cannot be made current (422).
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
offering_id |
string | yes | Offering id. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
display_name |
string | no | |
is_current |
boolean | no | |
metadata |
object or null | no |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/offerings/$OFFERING_ID" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"is_current":true}'Responses
- 200: The offering. Returns Offering.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
Delete an offering#
DELETE /v2/projects/{project_id}/offerings/{offering_id} · Auth: secret key or dashboard session · Permissions: project_configuration:offerings:read_write
Deletes its packages and clears customer overrides that point to it. An offering that a draft, running or paused experiment uses (as a variant's offering or a placement offering) answers 409 and names the experiment: stop the experiment or pick another offering in it first. A stopped experiment keeps its results and shows the deleted offering's id.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
offering_id |
string | yes | Offering id. |
Example request
curl -s -X DELETE "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/offerings/$OFFERING_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: Deleted. Returns Deleted.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 409: It already exists, or it conflicts with another object. Returns V2Error.
Example 200 response:
{
"object": "offering",
"id": "…",
"deleted_at": 1790801342625
}Duplicate an offering#
POST /v2/projects/{project_id}/offerings/{offering_id}/actions/duplicate · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read_write
Copies an offering with its packages, for an experiment's treatment. The copy is never current. Without packages the copy is exact: every package in order, with the same products. With packages, the list sets which packages are copied and in what order, and a package's products replaces its products (to test another price, period, trial or introductory offer). copy_paywall copies the offering's paywall, draft and published content, onto the copy.
Answers 400 when a source_package_id is not a package of this offering or repeats, a product is not in the project or repeats in a package, or copy_paywall is true and the offering has no paywall. Answers 409 when the lookup_key is taken, or two products of the same app in a package have overlapping eligibility_criteria.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
offering_id |
string | yes | The offering to copy. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
lookup_key |
string | yes | |
display_name |
string | yes | |
metadata |
object or null | no | Default: the source offering's metadata. |
packages |
array of object | no | Packages to copy, in the new order. Omitted: every package, as it is. |
packages[].source_package_id |
string | yes | A package of the source offering (pkge...). |
packages[].products |
array of object | no | Replaces the package's products. Omitted: the source package's products. |
packages[].products[].product_id |
string | yes | |
packages[].products[].eligibility_criteria |
all, google_sdk_lt_6, google_sdk_ge_6 |
yes | |
copy_paywall |
boolean | no | Also copy the offering's paywall onto the copy. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/offerings/$OFFERING_ID/actions/duplicate" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"lookup_key":"default_price","display_name":"Standard plans (price point)","packages":[{"source_package_id":"pkge1a2b3c4d5e","products":[{"product_id":"prod9k8j7h6g5f","eligibility_criteria":"all"}]},{"source_package_id":"pkge6f7g8h9i0j"}]}'Responses
- 201: The copy, with its packages and their products expanded. Returns Offering.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 409: It already exists, or it conflicts with another object. Returns V2Error.
Archive an offering#
POST /v2/projects/{project_id}/offerings/{offering_id}/actions/archive · Auth: secret key or dashboard session · Permissions: project_configuration:offerings:read_write
An offering that a draft, running or paused experiment uses cannot be archived (409); the current offering cannot be archived (422).
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
offering_id |
string | yes | Offering id. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/offerings/$OFFERING_ID/actions/archive" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The archived offering. Returns Offering.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 409: It already exists, or it conflicts with another object. Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
Unarchive an offering#
POST /v2/projects/{project_id}/offerings/{offering_id}/actions/unarchive · Auth: secret key or dashboard session · Permissions: project_configuration:offerings:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
offering_id |
string | yes | Offering id. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
unarchive_referenced_entities |
boolean | no | Also unarchive the products in its packages. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/offerings/$OFFERING_ID/actions/unarchive" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The offering. Returns Offering.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Packages#
One choice on the paywall, with one product per app.
List an offering's packages#
GET /v2/projects/{project_id}/offerings/{offering_id}/packages · Auth: secret key or dashboard session · Permissions: project_configuration:packages:read
Ordered by position, then creation: the order the SDK shows them in.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
offering_id |
string | yes | Offering id. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
expand |
array of items.product |
no | Embed products. |
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/offerings/$OFFERING_ID/packages" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of results. Returns a list of Package.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Create a package#
POST /v2/projects/{project_id}/offerings/{offering_id}/packages · Auth: secret key or dashboard session · Permissions: project_configuration:packages:read_write
Use the standard lookup keys ($rc_monthly, $rc_annual, $rc_weekly, $rc_lifetime ...) so the SDK's convenience accessors work. Without position, the package goes last.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
offering_id |
string | yes | Offering id. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
lookup_key |
string | yes | |
display_name |
string | yes | |
position |
integer | no |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/offerings/$OFFERING_ID/packages" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"lookup_key":"$rc_monthly","display_name":"Monthly","position":0}'Responses
- 201: The package. Returns Package.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 409: It already exists, or it conflicts with another object. Returns V2Error.
Get a package#
GET /v2/projects/{project_id}/packages/{package_id} · Auth: secret key or dashboard session · Permissions: project_configuration:packages:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
package_id |
string | yes | Package id (pkge...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
expand |
array of product |
no | Embed products. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/packages/$PACKAGE_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The package. Returns Package.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Update a package#
POST /v2/projects/{project_id}/packages/{package_id} · Auth: secret key or dashboard session · Permissions: project_configuration:packages:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
package_id |
string | yes | Package id. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
display_name |
string | no | |
position |
integer | no |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/packages/$PACKAGE_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The package. Returns Package.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Delete a package#
DELETE /v2/projects/{project_id}/packages/{package_id} · Auth: secret key or dashboard session · Permissions: project_configuration:packages:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
package_id |
string | yes | Package id. |
Example request
curl -s -X DELETE "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/packages/$PACKAGE_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: Deleted. Returns Deleted.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "package",
"id": "…",
"deleted_at": 1790801342625
}List a package's products#
GET /v2/projects/{project_id}/packages/{package_id}/products · Auth: secret key or dashboard session · Permissions: project_configuration:packages:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
package_id |
string | yes | Package id. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/packages/$PACKAGE_ID/products" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of results. Returns a list of PackageProduct.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Attach products to a package#
POST /v2/projects/{project_id}/packages/{package_id}/actions/attach_products · Auth: secret key or dashboard session · Permissions: project_configuration:packages:read_write
One product per app, so each app's SDK finds its product. Two products of the same app can share a package only with non-overlapping eligibility_criteria (409 otherwise).
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
package_id |
string | yes | Package id. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
products |
array of object | yes | |
products[].product_id |
string | yes | |
products[].eligibility_criteria |
all, google_sdk_lt_6, google_sdk_ge_6 |
yes |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/packages/$PACKAGE_ID/actions/attach_products" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"products":[{"product_id":"prode0zhpfisko","eligibility_criteria":"all"}]}'Responses
- 200: The package with its products. Returns Package.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 409: It already exists, or it conflicts with another object. Returns V2Error.
Detach products from a package#
POST /v2/projects/{project_id}/packages/{package_id}/actions/detach_products · Auth: secret key or dashboard session · Permissions: project_configuration:packages:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
package_id |
string | yes | Package id. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
product_ids |
array of string | yes |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/packages/$PACKAGE_ID/actions/detach_products" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The package with its products. Returns Package.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Customers#
Customers, their attributes, entitlements, subscriptions, purchases and events.
List or search customers#
GET /v2/projects/{project_id}/customers · Auth: secret key or dashboard session · Permissions: customer_information:customers:read
Newest first (by first seen).
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
search |
string | no | Exact match on an app user id, the $email attribute (any case) or a store transaction id. |
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/customers" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of results. Returns a list of Customer.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Create a customer#
POST /v2/projects/{project_id}/customers · Auth: secret key or dashboard session · Permissions: customer_information:customers:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | yes | App user id. |
attributes |
array of object | no | |
attributes[].name |
string | yes | |
attributes[].value |
string | yes |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/customers" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"id":"user_42","attributes":[{"name":"$email","value":"ana@example.com"}]}'Responses
- 201: The customer. Returns Customer.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 409: It already exists, or it conflicts with another object. Returns V2Error.
Get a customer#
GET /v2/projects/{project_id}/customers/{customer_id} · Auth: secret key or dashboard session · Permissions: customer_information:customers:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
customer_id |
string | yes | Any app user id of the customer. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
expand |
array of attributes |
no | attributes embeds the customer's attributes. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/customers/user_1" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The customer. Returns Customer.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "customer",
"id": "user_1",
"project_id": "proj18pzzkao",
"first_seen_at": 1790800914012,
"last_seen_at": 1790800914034,
"last_seen_app_version": null,
"last_seen_country": null,
"last_seen_platform": null,
"last_seen_platform_version": null,
"active_entitlements": {
"object": "list",
"items": [
{
"object": "customer.active_entitlement",
"entitlement_id": "entl1v0bp6r0qs",
"expires_at": 1793392914000
}
],
"next_page": null,
"url": "/v2/projects/proj18pzzkao/customers/user_1/active_entitlements"
},
"experiment": null
}Delete a customer#
DELETE /v2/projects/{project_id}/customers/{customer_id} · Auth: secret key or dashboard session · Permissions: customer_information:customers:read_write
Deletes aliases, attributes, subscriptions, purchases, transactions and events. Cannot be undone.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
customer_id |
string | yes | Any app user id of the customer. |
Example request
curl -s -X DELETE "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/customers/user_1" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: Deleted. Returns Deleted.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "customer",
"id": "…",
"deleted_at": 1790801342625
}List a customer's app user ids#
GET /v2/projects/{project_id}/customers/{customer_id}/aliases · Auth: secret key or dashboard session · Permissions: customer_information:customers:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
customer_id |
string | yes | Any app user id of the customer. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/customers/user_1/aliases" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of results. Returns a list of CustomerAlias.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
List a customer's attributes#
GET /v2/projects/{project_id}/customers/{customer_id}/attributes · Auth: secret key or dashboard session · Permissions: customer_information:customers:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
customer_id |
string | yes | Any app user id of the customer. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/customers/user_1/attributes" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of results. Returns a list of CustomerAttribute.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Set a customer's attributes#
POST /v2/projects/{project_id}/customers/{customer_id}/attributes · Auth: secret key or dashboard session · Permissions: customer_information:customers:read_write
API writes always win over older SDK writes. A null value deletes the attribute.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
customer_id |
string | yes | Any app user id of the customer. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
attributes |
array of object | yes | |
attributes[].name |
string | yes | |
attributes[].value |
string or null | yes |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/customers/user_1/attributes" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"attributes":[{"name":"$displayName","value":"Ana"}]}'Responses
- 200: Every attribute of the customer. Returns a list of CustomerAttribute.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
List a customer's active entitlements#
GET /v2/projects/{project_id}/customers/{customer_id}/active_entitlements · Auth: secret key or dashboard session · Permissions: customer_information:customers:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
customer_id |
string | yes | Any app user id of the customer. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/customers/user_1/active_entitlements" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of results. Returns a list of ActiveEntitlement.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
List a customer's subscriptions#
GET /v2/projects/{project_id}/customers/{customer_id}/subscriptions · Auth: secret key or dashboard session · Permissions: customer_information:subscriptions:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
customer_id |
string | yes | Any app user id of the customer. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
environment |
production, sandbox |
no | Only this environment. Default: both. |
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/customers/user_1/subscriptions" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of subscriptions. Returns a list of Subscription.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "list",
"items": [
{
"object": "subscription",
"id": "sub_k1u15wepvw0dfh25",
"customer_id": "user_1",
"original_customer_id": "user_1",
"product_id": "prode0zhpfisko",
"starts_at": 1790800914000,
"current_period_starts_at": 1790800914000,
"current_period_ends_at": 1793392914000,
"ends_at": 1793392914000,
"gives_access": true,
"pending_payment": false,
"auto_renewal_status": "will_renew",
"status": "active",
"total_revenue_in_usd": {
"currency": "USD",
"gross": 9.99,
"commission": 0,
"tax": 0,
"proceeds": 9.99
},
"presented_offering_id": null,
"entitlements": {
"object": "list",
"items": [
{
"object": "entitlement",
"id": "entl1v0bp6r0qs",
"project_id": "proj18pzzkao",
"lookup_key": "pro",
"display_name": "Pro access",
"created_at": 1790800901115,
"state": "active"
}
],
"next_page": null,
"url": "/v2/projects/proj18pzzkao/subscriptions/sub_k1u15wepvw0dfh25/entitlements"
},
"environment": "sandbox",
"store": "test_store",
"store_subscription_identifier": "test_1790800914000_quickstart",
"ownership": "purchased",
"management_url": null
}
],
"next_page": null,
"url": "/v2/projects/proj18pzzkao/customers/user_1/subscriptions"
}List a customer's one-time purchases#
GET /v2/projects/{project_id}/customers/{customer_id}/purchases · Auth: secret key or dashboard session · Permissions: customer_information:purchases:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
customer_id |
string | yes | Any app user id of the customer. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
environment |
production, sandbox |
no | Only this environment. Default: both. |
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/customers/user_1/purchases" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of results. Returns a list of Purchase.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
List a customer's events#
GET /v2/projects/{project_id}/customers/{customer_id}/events · Auth: secret key or dashboard session · Permissions: customer_information:customers:read
Newest first. body is the webhook event. Paywall events (PAYWALL_*) only with include_paywall_events=true or when type names them.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
customer_id |
string | yes | Any app user id of the customer. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
type |
array of string | no | Event types (any case); repeat or comma-separate. Naming a paywall type returns it. |
include_paywall_events |
boolean | no | Also return paywall events (PAYWALL_*). Default false: paywall events are left out unless type names them. |
environment |
production, sandbox |
no | Only this environment. Default: both. |
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/customers/user_1/events" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of results. Returns a list of CustomerEvent.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Grant an entitlement#
POST /v2/projects/{project_id}/customers/{customer_id}/actions/grant_entitlement · Auth: secret key or dashboard session · Permissions: customer_information:customers:read_write
Promotional access until expires_at. A grant ending within 2 hours of an existing grant for the same entitlement changes nothing.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
customer_id |
string | yes | Any app user id of the customer. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
entitlement_id |
string | yes | Entitlement id (entl...). |
expires_at |
integer | yes | Epoch milliseconds, in the future. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/customers/user_1/actions/grant_entitlement" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"entitlement_id":"entl1v0bp6r0qs","expires_at":1830000000000}'Responses
- 201: The customer. Returns Customer.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Revoke a granted entitlement#
POST /v2/projects/{project_id}/customers/{customer_id}/actions/revoke_granted_entitlement · Auth: secret key or dashboard session · Permissions: customer_information:customers:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
customer_id |
string | yes | Any app user id of the customer. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
entitlement_id |
string | yes |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/customers/user_1/actions/revoke_granted_entitlement" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The customer. Returns Customer.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Assign an offering to a customer#
POST /v2/projects/{project_id}/customers/{customer_id}/actions/assign_offering · Auth: secret key or dashboard session · Permissions: project_configuration:offerings:read, customer_information:customers:read_write
The customer sees this offering as current. null removes the override.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
customer_id |
string | yes | Any app user id of the customer. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
offering_id |
string or null | yes | Offering id (ofrng...) or null. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/customers/user_1/actions/assign_offering" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"offering_id":"ofrngjfr71v5awb"}'Responses
- 200: Done.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{}Transfer a customer's purchases to another customer#
POST /v2/projects/{project_id}/customers/{customer_id}/actions/transfer · Auth: secret key or dashboard session · Permissions: customer_information:customers:read_write, customer_information:subscriptions:read_write, customer_information:purchases:read_write
Moves subscriptions, one-time purchases and their transactions, optionally only those of the listed apps, and records a TRANSFER event. The target customer is created if it does not exist.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
customer_id |
string | yes | Any app user id of the customer. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
target_customer_id |
string | yes | |
app_ids |
array of string | no |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/customers/user_1/actions/transfer" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"target_customer_id":"user_2"}'Responses
- 200: Both customers after the transfer.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
Get the Customer Center configuration#
GET /v2/projects/{project_id}/customers/{customer_id}/customer_center · Auth: secret key or dashboard session · Permissions: customer_information:customers:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
customer_id |
string | yes | Any app user id of the customer. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
platform |
ios, android, macos, web, amazon |
no | |
locale |
string | no |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/customers/user_1/customer_center" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The configuration.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Restore a purchase by its store order id#
POST /v2/projects/{project_id}/customers/{customer_id}/actions/restore_purchase_by_order_id · Auth: secret key or dashboard session · Permissions: customer_information:customers:read_write
Finds the purchase an order id paid for and gives it to the customer, like a restore from the device: the project's transfer behaviour applies, and a purchase that moves records a TRANSFER event.
- Google Play order ids (
GPA.1234-5678-9012-34567, renewals with..0,..1):orders.batchGeton each Play app with a service account gives the purchase token, which is verified and acknowledged like a receipt. The service account needs the "View financial data" permission. - App Store order ids (the id on the customer's receipt email, such as
MK5TTTVWJH): Apple's Look Up Order ID on each App Store app with an In-App Purchase key, production first, then the sandbox. Only the order's own subscriptions and purchases are restored. RevenueCat's operation takes Google Play order ids only; App Store order ids are a RevenueDot addition.
404 when no store knows the order, 422 unprocessable_entity_error when no app has the credentials the lookup needs, 422 store_error with retryable: true while the store cannot be reached, 409 when the project's transfer behaviour is keep and another customer owns the purchase.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
customer_id |
string | yes | Any app user id of the customer. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
order_id |
string | yes | The store order id. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/customers/user_1/actions/restore_purchase_by_order_id" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"order_id":"GPA.3372-4157-7914-56870"}'Responses
- 200: The customer after the restore. Returns Customer.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 409: It already exists, or it conflicts with another object. Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
List the win-back offers Apple lets a customer redeem#
GET /v2/projects/{project_id}/customers/{customer_id}/win_back_offers · Auth: secret key or dashboard session · RevenueDot extension · Permissions: customer_information:subscriptions:read
One item per App Store subscription of the customer, with the win-back offer ids from Apple's renewal info, best first. RevenueDot reads the renewal info when the app posts a receipt, when Apple sends a notification, and after store actions; it needs the app's In-App Purchase key. See Win-back offers.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
customer_id |
string | yes | Any app user id of the customer. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/customers/user_1/win_back_offers" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The customer's App Store subscriptions.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "list",
"items": [
{
"object": "win_back_offer_eligibility",
"subscription_id": "sub_4kq0x2m9a7c1d8e3",
"product_id": "pro_monthly",
"store": "app_store",
"offer_ids": [
"comeback_50"
],
"updated_at": 1790800914034
}
],
"next_page": null,
"url": "/v2/projects/proj18pzzkao/customers/user_1/win_back_offers"
}Subscriptions#
Subscriptions across customers, and store actions on them.
Find subscriptions by store id#
GET /v2/projects/{project_id}/subscriptions · Auth: secret key or dashboard session · Permissions: customer_information:subscriptions:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
store_subscription_identifier |
string | yes | Store transaction id, original transaction id or purchase token. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/subscriptions" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of results. Returns a list of Subscription.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Get a subscription#
GET /v2/projects/{project_id}/subscriptions/{subscription_id} · Auth: secret key or dashboard session · Permissions: customer_information:subscriptions:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
subscription_id |
string | yes | Subscription id (sub_...). |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/subscriptions/$SUBSCRIPTION_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The subscription. Returns Subscription.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "subscription",
"id": "sub_k1u15wepvw0dfh25",
"customer_id": "user_1",
"original_customer_id": "user_1",
"product_id": "prode0zhpfisko",
"starts_at": 1790800914000,
"current_period_starts_at": 1790800914000,
"current_period_ends_at": 1793392914000,
"ends_at": 1793392914000,
"gives_access": true,
"pending_payment": false,
"auto_renewal_status": "will_renew",
"status": "active",
"total_revenue_in_usd": {
"currency": "USD",
"gross": 9.99,
"commission": 0,
"tax": 0,
"proceeds": 9.99
},
"presented_offering_id": null,
"entitlements": {
"object": "list",
"items": [
{
"object": "entitlement",
"id": "entl1v0bp6r0qs",
"project_id": "proj18pzzkao",
"lookup_key": "pro",
"display_name": "Pro access",
"created_at": 1790800901115,
"state": "active"
}
],
"next_page": null,
"url": "/v2/projects/proj18pzzkao/subscriptions/sub_k1u15wepvw0dfh25/entitlements"
},
"environment": "sandbox",
"store": "test_store",
"store_subscription_identifier": "test_1790800914000_quickstart",
"ownership": "purchased",
"management_url": null
}List the entitlements a subscription unlocks#
GET /v2/projects/{project_id}/subscriptions/{subscription_id}/entitlements · Auth: secret key or dashboard session · Permissions: customer_information:subscriptions:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
subscription_id |
string | yes | Subscription id. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/subscriptions/$SUBSCRIPTION_ID/entitlements" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of results. Returns a list of Entitlement.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
List a subscription's payments#
GET /v2/projects/{project_id}/subscriptions/{subscription_id}/transactions · Auth: secret key or dashboard session · Permissions: customer_information:subscriptions:read
One item per paid store transaction of the subscription (purchase, trial start, renewal). A refunded payment's effective_expiration_date is the refund time.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
subscription_id |
string | yes | Subscription id. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
sort |
id, purchased_at |
no | Default id. |
direction |
asc, desc |
no | Default asc. |
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/subscriptions/$SUBSCRIPTION_ID/transactions" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of results. Returns a list of SubscriptionTransaction.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Cancel a subscription (Google Play)#
POST /v2/projects/{project_id}/subscriptions/{subscription_id}/actions/cancel · Auth: secret key or dashboard session · Permissions: customer_information:subscriptions:read_write
Google Play only: turns auto-renew off. Other stores answer 422.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
subscription_id |
string | yes | Subscription id. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/subscriptions/$SUBSCRIPTION_ID/actions/cancel" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The subscription. Returns Subscription.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
- 503: The store could not be reached, or the feature is not configured on this server. Returns V2Error.
Refund and revoke a subscription (Google Play)#
POST /v2/projects/{project_id}/subscriptions/{subscription_id}/actions/refund · Auth: secret key or dashboard session · Permissions: customer_information:subscriptions:read_write
Google Play only: refunds the latest payment and ends access now. App Store refunds go through Apple. Other stores answer 422.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
subscription_id |
string | yes | Subscription id. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/subscriptions/$SUBSCRIPTION_ID/actions/refund" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The subscription. Returns Subscription.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
- 503: The store could not be reached, or the feature is not configured on this server. Returns V2Error.
Extend a subscription#
POST /v2/projects/{project_id}/subscriptions/{subscription_id}/actions/extend · Auth: secret key or dashboard session · Permissions: customer_information:subscriptions:read_write
App Store: Apple extends the renewal date (1 to 90 days, extend_reason_code required, needs the in-app purchase key). Google Play: the renewal is deferred (up to 365 days). Send extend_by_days or extend_until_ms, not both.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
subscription_id |
string | yes | Subscription id. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
extend_by_days |
integer | yes | |
extend_reason_code |
undeclared, customer_satisfaction, other, service_issue_or_outage |
no | Apple's reason for the extension. Required for App Store subscriptions. |
extend_until_ms |
integer | yes | New end, epoch milliseconds. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/subscriptions/$SUBSCRIPTION_ID/actions/extend" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"extend_by_days":7,"extend_reason_code":"customer_satisfaction"}'Responses
- 200: The subscription. Returns Subscription.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
- 503: The store could not be reached, or the feature is not configured on this server. Returns V2Error.
Refund one payment of a subscription (Google Play)#
POST /v2/projects/{project_id}/subscriptions/{subscription_id}/transactions/{transaction_id}/actions/refund · Auth: secret key or dashboard session · Permissions: customer_information:subscriptions:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
subscription_id |
string | yes | Subscription id. |
transaction_id |
string | yes | Google order id of the payment. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/subscriptions/$SUBSCRIPTION_ID/transactions/$TRANSACTION_ID/actions/refund" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The refunded payment. Returns SubscriptionTransaction.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
- 503: The store could not be reached, or the feature is not configured on this server. Returns V2Error.
Get where the customer manages a subscription#
GET /v2/projects/{project_id}/subscriptions/{subscription_id}/authenticated_management_url · Auth: secret key or dashboard session · Permissions: customer_information:subscriptions:read
The App Store subscriptions page or the Google Play page for the subscription. Null for stores without one.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
subscription_id |
string | yes |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/subscriptions/$SUBSCRIPTION_ID/authenticated_management_url" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The URL.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "authenticated_management_url",
"management_url": "https://apps.apple.com/account/subscriptions"
}Purchases#
One-time purchases across customers.
Find one-time purchases by store id#
GET /v2/projects/{project_id}/purchases · Auth: secret key or dashboard session · Permissions: customer_information:purchases:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
store_purchase_identifier |
string | yes | Store transaction id. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/purchases" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of results. Returns a list of Purchase.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Get a one-time purchase#
GET /v2/projects/{project_id}/purchases/{purchase_id} · Auth: secret key or dashboard session · Permissions: customer_information:purchases:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
purchase_id |
string | yes | Purchase id. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/purchases/$PURCHASE_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The purchase. Returns Purchase.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
List the entitlements a purchase unlocks#
GET /v2/projects/{project_id}/purchases/{purchase_id}/entitlements · Auth: secret key or dashboard session · Permissions: customer_information:purchases:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
purchase_id |
string | yes | Purchase id. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/purchases/$PURCHASE_ID/entitlements" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of results. Returns a list of Entitlement.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Refund a one-time purchase (Google Play)#
POST /v2/projects/{project_id}/purchases/{purchase_id}/actions/refund · Auth: secret key or dashboard session · Permissions: customer_information:purchases:read_write
Google Play refunds and revokes the order. Other stores answer 422.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
purchase_id |
string | yes | Purchase id. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/purchases/$PURCHASE_ID/actions/refund" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The purchase. Returns Purchase.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
- 503: The store could not be reached, or the feature is not configured on this server. Returns V2Error.
Metrics#
The dashboard overview numbers and revenue totals.
Overview metrics#
GET /v2/projects/{project_id}/metrics/overview · Auth: secret key or dashboard session · Permissions: charts_metrics:overview:read
Computed live: active trials, active paid subscriptions, MRR (USD price normalised to a month), revenue in the last 28 days, new and active customers in the last 28 days.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
currency |
"USD" |
no | Only USD is supported. |
environment |
production, sandbox |
no | RevenueDot extension. Default production. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/metrics/overview" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The metrics. Returns OverviewMetrics.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Revenue over a date range#
GET /v2/projects/{project_id}/metrics/revenue · Auth: secret key or dashboard session · Permissions: charts_metrics:overview:read
Production revenue from the transaction ledger, in USD at the purchase-date rate.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
start_date |
string | yes | |
end_date |
string | yes | Inclusive. |
currency |
string | no | Only USD for now. |
revenue_type |
revenue, revenue_net_of_taxes, proceeds |
no | Proceeds subtract the estimated store commission. We hold no tax data, so net of taxes equals revenue. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/metrics/revenue" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The total.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "revenue_metric",
"start_date": "2026-09-01",
"end_date": "2026-09-30",
"currency": "USD",
"value": 12345.67,
"revenue_type": "revenue"
}Charts#
Every built-in chart (revenue, MRR, subscriptions, trials, conversion, LTV, churn, refunds, paywalls, ads) with RevenueCat's definitions.
Get chart data#
GET /v2/projects/{project_id}/charts/{chart_name} · Auth: secret key or dashboard session · Permissions: charts_metrics:charts:read
Time series or cohort table for one chart, computed from the project's purchases when asked. Definitions, filters and SQL: https://revenuedot.app/docs/guides/charts.
Sandbox purchases, granted access and Family Sharing are excluded; money is USD at the purchase-date rate (or currency at the same date); periods are UTC.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
chart_name |
actives, actives_movement, actives_new, arr, churn, cohort_explorer, conversion_to_paying, customers_new, initial_conversion, ltv_per_customer, ltv_per_paying_customer, mrr, mrr_movement, prediction_explorer, refund_rate, refund_request, refunds, revenue, subscription_retention, subscription_status, trials, trials_movement, trials_new, customers_active, trial_conversion, trial_conversion_rate, trial_cancellation, non-subscription_purchases, ad_revenue, ad_impressions, ad_clicks, ad_monetized_customers, ad_arpdau, ad_rpm, ad_fill_rate, ad_ctr, paywall_encounter, paywall_conversion, paywall_ltv, paywall_abandonment, app_store_save_outcomes, play_store_cancel_reasons, customer_center_survey_responses |
yes | The chart. play_store_cancel_reasons and customer_center_survey_responses are RevenueDot names for charts RevenueCat shows only in its dashboard. Definitions: https://revenuedot.app/docs/guides/charts |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
resolution |
string | no | 0–4 or day, week, month, quarter, year. Default day (month for cohort tables). At most 1,000 periods. |
start_date |
string | no | First day (YYYY-MM-DD). Default: 30 days ago, or 12 months for cohort tables. |
end_date |
string | no | Last day, inclusive. Default: today. |
expand_periods |
boolean | no | Count the whole first period of flow charts. Default false. |
filters |
string | no | JSON array [{"name":"country","values":["US"]}]. Names come from /options. |
selectors |
string | no | JSON object, e.g. {"revenue_type":"proceeds"}. |
segment |
string | no | One dimension from /options. |
limit_num_segments |
integer | no | Top N segments by the first measure; the rest become "Other". |
aggregate |
string | no | average, total or both, comma separated: values is empty and summary holds only these. |
currency |
USD, EUR, GBP, AUD, CAD, JPY, BRL, KRW, CNY, MXN, SEK, PLN, NZD, CHF |
no | |
week_start |
string | no | RevenueDot extension: the first day of weekly buckets, 0 (Sunday) to 6 (Saturday) or a day's name. Default 1 (Monday). |
include_annotations |
boolean | no | |
realtime |
boolean | no | Accepted for compatibility; every chart uses the real-time (v3) definitions. |
environment |
production, sandbox |
no | RevenueDot extension. sandbox shows only sandbox and Test Store purchases. Default production. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/charts/$CHART_NAME" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The chart.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Get available options for a chart#
GET /v2/projects/{project_id}/charts/{chart_name}/options · Auth: secret key or dashboard session · Permissions: charts_metrics:charts:read
Resolutions, segments, filters with the values present in the project's data, and the chart's selectors.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
chart_name |
actives, actives_movement, actives_new, arr, churn, cohort_explorer, conversion_to_paying, customers_new, initial_conversion, ltv_per_customer, ltv_per_paying_customer, mrr, mrr_movement, prediction_explorer, refund_rate, refund_request, refunds, revenue, subscription_retention, subscription_status, trials, trials_movement, trials_new, customers_active, trial_conversion, trial_conversion_rate, trial_cancellation, non-subscription_purchases, ad_revenue, ad_impressions, ad_clicks, ad_monetized_customers, ad_arpdau, ad_rpm, ad_fill_rate, ad_ctr, paywall_encounter, paywall_conversion, paywall_ltv, paywall_abandonment, app_store_save_outcomes, play_store_cancel_reasons, customer_center_survey_responses |
yes | The chart. play_store_cancel_reasons and customer_center_survey_responses are RevenueDot names for charts RevenueCat shows only in its dashboard. Definitions: https://revenuedot.app/docs/guides/charts |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
environment |
production, sandbox |
no | RevenueDot extension. sandbox shows only sandbox and Test Store purchases. Default production. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/charts/$CHART_NAME/options" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The options.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
List the customers behind a chart#
GET /v2/projects/{project_id}/charts/{chart_name}/customers · Auth: secret key or dashboard session · RevenueDot extension · Permissions: charts_metrics:charts:read, customer_information:customers:read
The customers whose purchases, subscriptions, trials or activity make up the chart for these parameters (the chart's own), most recent contribution first, each with their part of one of the chart's measures. The values plus unattributed_value add up to the chart: over the range (sum: total) or at the last period (sum: last). Which customers each chart lists: https://revenuedot.app/docs/guides/charts#customers-behind-a-chart.
format=csv streams every contributor as CSV (at most 100,000 rows); the response has X-RevenueDot-Total-Count.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
chart_name |
actives, actives_movement, actives_new, arr, churn, cohort_explorer, conversion_to_paying, customers_new, initial_conversion, ltv_per_customer, ltv_per_paying_customer, mrr, mrr_movement, prediction_explorer, refund_rate, refund_request, refunds, revenue, subscription_retention, subscription_status, trials, trials_movement, trials_new, customers_active, trial_conversion, trial_conversion_rate, trial_cancellation, non-subscription_purchases, ad_revenue, ad_impressions, ad_clicks, ad_monetized_customers, ad_arpdau, ad_rpm, ad_fill_rate, ad_ctr, paywall_encounter, paywall_conversion, paywall_ltv, paywall_abandonment, app_store_save_outcomes, play_store_cancel_reasons, customer_center_survey_responses |
yes | The chart. play_store_cancel_reasons and customer_center_survey_responses are RevenueDot names for charts RevenueCat shows only in its dashboard. Definitions: https://revenuedot.app/docs/guides/charts |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
resolution |
string | no | As for chart data. |
start_date |
string | no | |
end_date |
string | no | |
expand_periods |
boolean | no | |
filters |
string | no | |
selectors |
string | no | |
segment |
string | no | |
limit_num_segments |
integer | no | |
currency |
USD, EUR, GBP, AUD, CAD, JPY, BRL, KRW, CNY, MXN, SEK, PLN, NZD, CHF |
no | |
environment |
production, sandbox |
no | RevenueDot extension. sandbox shows only sandbox and Test Store purchases. Default production. |
limit |
integer | no | Customers in items, 1–100. Default 100. |
format |
json, csv |
no | Default json. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/charts/$CHART_NAME/customers" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The customers, or the CSV export.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
List chart annotations#
GET /v2/projects/{project_id}/chart_annotations · Auth: secret key or dashboard session · RevenueDot extension · Permissions: charts_metrics:charts:read
The project's annotations, oldest first, at most 1,000. With dates, only those that overlap them.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
start_date |
string | no | |
end_date |
string | no |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/chart_annotations" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The annotations.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Create a chart annotation#
POST /v2/projects/{project_id}/chart_annotations · Auth: secret key or dashboard session · RevenueDot extension · Permissions: charts_metrics:charts:read_write
A note on a UTC day or date range that every chart of the project shows. Viewers cannot create one. Audited as chart_annotation_created.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
title |
string | yes | |
description |
string or null | no | |
start_date |
string | yes | |
end_date |
string or null | no | Default start_date; never before it. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/chart_annotations" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"title":"Launched the annual plan","description":"New paywall in 2.4","start_date":"2026-09-14"}'Responses
- 201: The annotation.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
Get a chart annotation#
GET /v2/projects/{project_id}/chart_annotations/{annotation_id} · Auth: secret key or dashboard session · RevenueDot extension · Permissions: charts_metrics:charts:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
annotation_id |
string | yes |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/chart_annotations/$ANNOTATION_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The annotation.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Update a chart annotation#
PATCH /v2/projects/{project_id}/chart_annotations/{annotation_id} · Auth: secret key or dashboard session · RevenueDot extension · Permissions: charts_metrics:charts:read_write
Changes any field. A single-day annotation moved with start_date stays a single day; end_date: null makes any annotation a single day. Audited as chart_annotation_updated.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
annotation_id |
string | yes |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
title |
string | no | |
description |
string or null | no | |
start_date |
string | no | |
end_date |
string or null | no |
Example request
curl -s -X PATCH "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/chart_annotations/$ANNOTATION_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The annotation.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Delete a chart annotation#
DELETE /v2/projects/{project_id}/chart_annotations/{annotation_id} · Auth: secret key or dashboard session · RevenueDot extension · Permissions: charts_metrics:charts:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
annotation_id |
string | yes |
Example request
curl -s -X DELETE "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/chart_annotations/$ANNOTATION_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: Deleted.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
List active chart share links#
GET /v2/projects/{project_id}/chart_shares · Auth: secret key or dashboard session · RevenueDot extension · Permissions: charts_metrics:charts:read
The project's links that are not revoked, newest first, at most 200.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
chart_name |
actives, actives_movement, actives_new, arr, churn, cohort_explorer, conversion_to_paying, customers_new, initial_conversion, ltv_per_customer, ltv_per_paying_customer, mrr, mrr_movement, prediction_explorer, refund_rate, refund_request, refunds, revenue, subscription_retention, subscription_status, trials, trials_movement, trials_new, customers_active, trial_conversion, trial_conversion_rate, trial_cancellation, non-subscription_purchases, ad_revenue, ad_impressions, ad_clicks, ad_monetized_customers, ad_arpdau, ad_rpm, ad_fill_rate, ad_ctr, paywall_encounter, paywall_conversion, paywall_ltv, paywall_abandonment, app_store_save_outcomes, play_store_cancel_reasons, customer_center_survey_responses |
no | Only this chart's links. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/chart_shares" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The links.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Create a public chart share link#
POST /v2/projects/{project_id}/chart_shares · Auth: secret key or dashboard session · RevenueDot extension · Permissions: charts_metrics:charts:read_write
Computes the chart for the view now and keeps that snapshot (series, labels, summary values; no customer data) behind an unguessable public link with a PNG preview. Owners, admins and developers can create one; at most 200 active per project. Audited as chart_share_created.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
chart_name |
actives, actives_movement, actives_new, arr, churn, cohort_explorer, conversion_to_paying, customers_new, initial_conversion, ltv_per_customer, ltv_per_paying_customer, mrr, mrr_movement, prediction_explorer, refund_rate, refund_request, refunds, revenue, subscription_retention, subscription_status, trials, trials_movement, trials_new, customers_active, trial_conversion, trial_conversion_rate, trial_cancellation, non-subscription_purchases, ad_revenue, ad_impressions, ad_clicks, ad_monetized_customers, ad_arpdau, ad_rpm, ad_fill_rate, ad_ctr, paywall_encounter, paywall_conversion, paywall_ltv, paywall_abandonment, app_store_save_outcomes, play_store_cancel_reasons, customer_center_survey_responses |
yes | |
view |
object | no | |
view.range |
string | no | 7d, 30d, 90d, 12m or custom. |
view.start |
string | no | Custom range start, YYYY-MM-DD. |
view.end |
string | no | Custom range end. |
view.res |
string | no | day, week, month, quarter or year. |
view.segment |
string | no | |
view.filters |
string | no | The filters parameter's JSON. |
view.sel |
string | no | The selectors parameter's JSON. |
view.env |
production, sandbox |
no | |
view.compare |
boolean | no | Compare to the previous period. |
view.type |
line, stacked_area, column, stacked_column, percent_column |
no | The chart type. Stacked types need two or more series; with one they draw as line or column. |
view.m |
string | no | The plotted measure group, 0 first. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/chart_shares" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"chart_name":"revenue","view":{"range":"90d","res":"week","segment":"product","type":"stacked_column"}}'Responses
- 201: The link.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
Revoke a chart share link#
DELETE /v2/projects/{project_id}/chart_shares/{share_id} · Auth: secret key or dashboard session · RevenueDot extension · Permissions: charts_metrics:charts:read_write
The public page and images answer 410 from now on. Audited as chart_share_deleted.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
share_id |
string | yes | The link's id (chartshare…), not its token. |
Example request
curl -s -X DELETE "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/chart_shares/$SHARE_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: Revoked.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
List saved charts#
GET /v2/projects/{project_id}/saved_charts · Auth: secret key or dashboard session · RevenueDot extension · Permissions: charts_metrics:charts:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/saved_charts" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of saved charts.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Save a chart view#
POST /v2/projects/{project_id}/saved_charts · Auth: secret key or dashboard session · RevenueDot extension · Permissions: charts_metrics:charts:read_write
A named chart with the dashboard view that produced it (range, dates, resolution, segment, filters, selectors, environment, compare, chart type, measure group). Up to 200 per project.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | |
chart_name |
actives, actives_movement, actives_new, arr, churn, cohort_explorer, conversion_to_paying, customers_new, initial_conversion, ltv_per_customer, ltv_per_paying_customer, mrr, mrr_movement, prediction_explorer, refund_rate, refund_request, refunds, revenue, subscription_retention, subscription_status, trials, trials_movement, trials_new, customers_active, trial_conversion, trial_conversion_rate, trial_cancellation, non-subscription_purchases, ad_revenue, ad_impressions, ad_clicks, ad_monetized_customers, ad_arpdau, ad_rpm, ad_fill_rate, ad_ctr, paywall_encounter, paywall_conversion, paywall_ltv, paywall_abandonment, app_store_save_outcomes, play_store_cancel_reasons, customer_center_survey_responses |
yes | |
view |
object | no | |
view.range |
string | no | 7d, 30d, 90d, 12m or custom. |
view.start |
string | no | Custom range start, YYYY-MM-DD. |
view.end |
string | no | Custom range end. |
view.res |
string | no | day, week, month, quarter or year. |
view.segment |
string | no | |
view.filters |
string | no | The filters parameter's JSON. |
view.sel |
string | no | The selectors parameter's JSON. |
view.env |
production, sandbox |
no | |
view.compare |
boolean | no | Compare to the previous period. |
view.type |
line, stacked_area, column, stacked_column, percent_column |
no | The chart type. Stacked types need two or more series; with one they draw as line or column. |
view.m |
string | no | The plotted measure group, 0 first. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/saved_charts" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"name":"MRR by country","chart_name":"mrr","view":{"range":"90d","res":"week","segment":"country","compare":true}}'Responses
- 201: The saved chart.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
Get a saved chart#
GET /v2/projects/{project_id}/saved_charts/{saved_chart_id} · Auth: secret key or dashboard session · RevenueDot extension · Permissions: charts_metrics:charts:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
saved_chart_id |
string | yes |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/saved_charts/$SAVED_CHART_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The saved chart.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Rename or update a saved chart#
PATCH /v2/projects/{project_id}/saved_charts/{saved_chart_id} · Auth: secret key or dashboard session · RevenueDot extension · Permissions: charts_metrics:charts:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
saved_chart_id |
string | yes |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | no | |
view |
object | no | |
view.range |
string | no | 7d, 30d, 90d, 12m or custom. |
view.start |
string | no | Custom range start, YYYY-MM-DD. |
view.end |
string | no | Custom range end. |
view.res |
string | no | day, week, month, quarter or year. |
view.segment |
string | no | |
view.filters |
string | no | The filters parameter's JSON. |
view.sel |
string | no | The selectors parameter's JSON. |
view.env |
production, sandbox |
no | |
view.compare |
boolean | no | Compare to the previous period. |
view.type |
line, stacked_area, column, stacked_column, percent_column |
no | The chart type. Stacked types need two or more series; with one they draw as line or column. |
view.m |
string | no | The plotted measure group, 0 first. |
Example request
curl -s -X PATCH "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/saved_charts/$SAVED_CHART_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The saved chart.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Delete a saved chart#
DELETE /v2/projects/{project_id}/saved_charts/{saved_chart_id} · Auth: secret key or dashboard session · RevenueDot extension · Permissions: charts_metrics:charts:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
saved_chart_id |
string | yes |
Example request
curl -s -X DELETE "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/saved_charts/$SAVED_CHART_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: Deleted.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
In-app currencies#
Currencies your app sells or rewards, their product grants and each customer's balance.
List in-app currencies#
GET /v2/projects/{project_id}/virtual_currencies · Auth: secret key or dashboard session · Permissions: project_configuration:virtual_currencies:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/virtual_currencies" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of currencies.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "list",
"items": [
{
"object": "virtual_currency",
"project_id": "proj18pzzkao",
"code": "GLD",
"name": "Gold",
"description": null,
"state": "active",
"created_at": 1790800901115,
"product_grants": [
{
"object": "virtual_currency.product_grant",
"product_ids": [
"prod6n3k1a8w2z"
],
"amount": 100,
"trial_amount": 0,
"expire_at_cycle_end": false
}
]
}
],
"next_page": null,
"url": "/v2/projects/{project_id}/virtual_currencies"
}Create an in-app currency#
POST /v2/projects/{project_id}/virtual_currencies · Auth: secret key or dashboard session · Permissions: project_configuration:virtual_currencies:read_write
A product grant credits the balance each time a purchase, renewal or trial start of one of its products is recorded, once per store transaction.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
code |
string | yes | Letters, digits and underscores, up to 10. |
name |
string | yes | |
description |
string or null | no | |
product_grants |
array of object | no | |
product_grants[].product_ids |
array of string | yes | |
product_grants[].amount |
integer | yes | Credited per purchase or renewal. |
product_grants[].trial_amount |
integer or null | no | Credited when a free trial starts. Default 0. |
product_grants[].expire_at_cycle_end |
boolean or null | no |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/virtual_currencies" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"code":"GLD","name":"Gold","product_grants":[{"product_ids":["prod6n3k1a8w2z"],"amount":100}]}'Responses
- 201: The currency.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 409: It already exists, or it conflicts with another object. Returns V2Error.
Example 201 response:
{
"object": "virtual_currency",
"project_id": "proj18pzzkao",
"code": "GLD",
"name": "Gold",
"description": null,
"state": "active",
"created_at": 1790800901115,
"product_grants": [
{
"object": "virtual_currency.product_grant",
"product_ids": [
"prod6n3k1a8w2z"
],
"amount": 100,
"trial_amount": 0,
"expire_at_cycle_end": false
}
]
}Get an in-app currency#
GET /v2/projects/{project_id}/virtual_currencies/{virtual_currency_code} · Auth: secret key or dashboard session · Permissions: project_configuration:virtual_currencies:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
virtual_currency_code |
string | yes | The currency code, such as GLD. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/virtual_currencies/$VIRTUAL_CURRENCY_CODE" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The currency.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "virtual_currency",
"project_id": "proj18pzzkao",
"code": "GLD",
"name": "Gold",
"description": null,
"state": "active",
"created_at": 1790800901115,
"product_grants": [
{
"object": "virtual_currency.product_grant",
"product_ids": [
"prod6n3k1a8w2z"
],
"amount": 100,
"trial_amount": 0,
"expire_at_cycle_end": false
}
]
}Update an in-app currency#
POST /v2/projects/{project_id}/virtual_currencies/{virtual_currency_code} · Auth: secret key or dashboard session · Permissions: project_configuration:virtual_currencies:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
virtual_currency_code |
string | yes | The currency code, such as GLD. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | no | |
description |
string or null | no | |
product_grants |
array of object | no | |
product_grants[].product_ids |
array of string | yes | |
product_grants[].amount |
integer | yes | Credited per purchase or renewal. |
product_grants[].trial_amount |
integer or null | no | Credited when a free trial starts. Default 0. |
product_grants[].expire_at_cycle_end |
boolean or null | no |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/virtual_currencies/$VIRTUAL_CURRENCY_CODE" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"name":"Gold coins"}'Responses
- 200: The currency.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "virtual_currency",
"project_id": "proj18pzzkao",
"code": "GLD",
"name": "Gold",
"description": null,
"state": "active",
"created_at": 1790800901115,
"product_grants": [
{
"object": "virtual_currency.product_grant",
"product_ids": [
"prod6n3k1a8w2z"
],
"amount": 100,
"trial_amount": 0,
"expire_at_cycle_end": false
}
]
}Delete an in-app currency#
DELETE /v2/projects/{project_id}/virtual_currencies/{virtual_currency_code} · Auth: secret key or dashboard session · Permissions: project_configuration:virtual_currencies:read_write
Also deletes every customer's balance of it and its ledger.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
virtual_currency_code |
string | yes | The currency code, such as GLD. |
Example request
curl -s -X DELETE "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/virtual_currencies/$VIRTUAL_CURRENCY_CODE" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: Deleted. Returns Deleted.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "virtual_currency",
"id": "GLD",
"deleted_at": 1790801342625
}Archive an in-app currency#
POST /v2/projects/{project_id}/virtual_currencies/{virtual_currency_code}/actions/archive · Auth: secret key or dashboard session · Permissions: project_configuration:virtual_currencies:read_write
An archived currency stops granting; customers keep a non-zero balance.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
virtual_currency_code |
string | yes | The currency code, such as GLD. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/virtual_currencies/$VIRTUAL_CURRENCY_CODE/actions/archive" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The currency.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "virtual_currency",
"project_id": "proj18pzzkao",
"code": "GLD",
"name": "Gold",
"description": null,
"state": "active",
"created_at": 1790800901115,
"product_grants": [
{
"object": "virtual_currency.product_grant",
"product_ids": [
"prod6n3k1a8w2z"
],
"amount": 100,
"trial_amount": 0,
"expire_at_cycle_end": false
}
]
}Unarchive an in-app currency#
POST /v2/projects/{project_id}/virtual_currencies/{virtual_currency_code}/actions/unarchive · Auth: secret key or dashboard session · Permissions: project_configuration:virtual_currencies:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
virtual_currency_code |
string | yes | The currency code, such as GLD. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/virtual_currencies/$VIRTUAL_CURRENCY_CODE/actions/unarchive" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The currency.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "virtual_currency",
"project_id": "proj18pzzkao",
"code": "GLD",
"name": "Gold",
"description": null,
"state": "active",
"created_at": 1790800901115,
"product_grants": [
{
"object": "virtual_currency.product_grant",
"product_ids": [
"prod6n3k1a8w2z"
],
"amount": 100,
"trial_amount": 0,
"expire_at_cycle_end": false
}
]
}List a customer's balances#
GET /v2/projects/{project_id}/customers/{customer_id}/virtual_currencies · Auth: secret key or dashboard session · Permissions: customer_information:purchases:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
customer_id |
string | yes | Any app user id of the customer. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
include_empty_balances |
boolean | no | Also list currencies with a zero balance. |
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/customers/user_1/virtual_currencies" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: Balances.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "list",
"items": [
{
"object": "virtual_currency_balance",
"currency_code": "GLD",
"balance": 100,
"name": "Gold"
}
],
"next_page": null,
"url": "/v2/projects/proj18pzzkao/customers/user_1/virtual_currencies"
}Credit or spend in-app currency#
POST /v2/projects/{project_id}/customers/{customer_id}/virtual_currencies/transactions · Auth: secret key or dashboard session · Permissions: customer_information:purchases:read_write
Adds a ledger entry per currency. Send an Idempotency-Key so a retry does not apply twice. Nothing is applied if any balance would go below zero.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
customer_id |
string | yes | Any app user id of the customer. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
include_empty_balances |
boolean | no | Also list currencies with a zero balance. |
Headers
| Name | Type | Required | Description |
|---|---|---|---|
Idempotency-Key |
string | no | Repeating a request with the same key changes nothing the second time. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
adjustments |
object | yes | Signed change per currency code. A balance cannot go below zero. |
reference |
string or null | no | Your own note, kept in the ledger. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/customers/user_1/virtual_currencies/transactions" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"adjustments":{"GLD":-20},"reference":"level 3 unlock"}'Responses
- 200: The balances after the change.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
Example 200 response:
{
"object": "list",
"items": [
{
"object": "virtual_currency_balance",
"currency_code": "GLD",
"balance": 100,
"name": "Gold"
}
],
"next_page": null,
"url": "/v2/projects/proj18pzzkao/customers/user_1/virtual_currencies"
}Change a balance without a ledger entry#
POST /v2/projects/{project_id}/customers/{customer_id}/virtual_currencies/update_balance · Auth: secret key or dashboard session · Permissions: customer_information:purchases:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
customer_id |
string | yes | Any app user id of the customer. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
include_empty_balances |
boolean | no | Also list currencies with a zero balance. |
Headers
| Name | Type | Required | Description |
|---|---|---|---|
Idempotency-Key |
string | no | Repeating a request with the same key changes nothing the second time. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
adjustments |
object | yes | Signed change per currency code. A balance cannot go below zero. |
reference |
string or null | no | Your own note, kept in the ledger. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/customers/user_1/virtual_currencies/update_balance" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"adjustments":{"GLD":-20},"reference":"level 3 unlock"}'Responses
- 200: The balances after the change.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
Example 200 response:
{
"object": "list",
"items": [
{
"object": "virtual_currency_balance",
"currency_code": "GLD",
"balance": 100,
"name": "Gold"
}
],
"next_page": null,
"url": "/v2/projects/proj18pzzkao/customers/user_1/virtual_currencies"
}Audit log#
Who changed what in a project.
List audit log entries#
GET /v2/projects/{project_id}/audit_logs · Auth: secret key or dashboard session · Permissions: project_configuration:audit_logs:read
Who changed what, newest first. Every successful write through API v2 is recorded with the actor (a user, a secret key, or an OAuth client). Reads and request bodies are never recorded.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
start_date |
string | no | |
end_date |
string | no | Inclusive. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/audit_logs" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of entries.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "list",
"items": [
{
"object": "audit_log",
"id": "log1ab2c3d4e5",
"project_id": "proj18pzzkao",
"action_type": "entitlement_created",
"target_type": "entitlement",
"target_identifier": "entl1v0bp6r0qs",
"actor_type": "api_key",
"actor_identifier": "key_08ec817fce",
"occurred_at": 1790801342634,
"additional_data": {
"method": "POST",
"status": 201
}
}
],
"next_page": null,
"url": "/v2/projects/{project_id}/audit_logs"
}Targeting#
Audiences, and rules that pick the offering and placement offerings for each customer.
List audiences#
GET /v2/projects/{project_id}/audiences · Auth: secret key or dashboard session · Permissions: audiences:audiences:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/audiences" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: All audiences.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Create an audience#
POST /v2/projects/{project_id}/audiences · Auth: secret key or dashboard session · Permissions: audiences:audiences:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | |
rules |
object | yes | |
rules.groups |
array of object | yes | Groups are OR-ed; conditions in a group are AND-ed. |
rules.groups[].conditions |
array of object | yes |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/audiences" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"name":"Gold plan","rules":{"groups":[{"conditions":[{"field":"customAttribute:plan","operator":"is","value":"gold"}]}]}}'Responses
- 201: The audience.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Preview who matches#
POST /v2/projects/{project_id}/audiences/actions/preview · Auth: secret key or dashboard session · Permissions: audiences:audiences:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Request body (application/json)
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/audiences/actions/preview" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"rules":{"groups":[{"conditions":[{"field":"country","operator":"isAnyOf","value":"US,CA"}]}]}}'Responses
- 200: Stats and a sample.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Known values for attribution and custom-attribute fields#
GET /v2/projects/{project_id}/audiences/filter_options · Auth: secret key or dashboard session · Permissions: audiences:audiences:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
fields |
string | no | Comma-separated: mediaSource, campaign, adGroup, ad, keyword, creative, latestProduct, customAttribute: |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/audiences/filter_options" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: Options per field.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Get an audience#
GET /v2/projects/{project_id}/audiences/{audience_id} · Auth: secret key or dashboard session · Permissions: audiences:audiences:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
audience_id |
string | yes |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
expand |
array of stats, customer_sample, used_by |
no |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/audiences/$AUDIENCE_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The audience.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Update an audience#
POST /v2/projects/{project_id}/audiences/{audience_id} · Auth: secret key or dashboard session · Permissions: audiences:audiences:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
audience_id |
string | yes |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | no | |
rules |
object | no | |
rules.groups |
array of object | yes | Groups are OR-ed; conditions in a group are AND-ed. |
rules.groups[].conditions |
array of object | yes |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/audiences/$AUDIENCE_ID" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"name":"Gold"}'Responses
- 200: The audience.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Delete an unused audience#
DELETE /v2/projects/{project_id}/audiences/{audience_id} · Auth: secret key or dashboard session · RevenueDot extension · Permissions: audiences:audiences:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
audience_id |
string | yes |
Example request
curl -s -X DELETE "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/audiences/$AUDIENCE_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: Deleted. Returns Deleted.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 409: It already exists, or it conflicts with another object. Returns V2Error.
List targeting rules in order#
GET /v2/projects/{project_id}/targeting_rules · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/targeting_rules" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: All rules.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Create a targeting rule#
POST /v2/projects/{project_id}/targeting_rules · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read_write
Added at the end of the order, inactive unless state is active. When the SDK fetches offerings, the first live rule whose audience matches decides the current offering and the offering per placement.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | |
audience_id |
string or null | no | |
offering_id |
string | yes | |
placements |
object | no | |
state |
active, inactive |
no | |
starts_at |
integer or null | no | |
ends_at |
integer or null | no |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/targeting_rules" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"name":"Gold gets promo","audience_id":"aud1a2b3c4d5e6f","offering_id":"ofrngm2u3h89blc","placements":{"onboarding_end":"ofrng9x8y7z6w5"},"state":"active"}'Responses
- 201: The rule.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Set the evaluation order#
POST /v2/projects/{project_id}/targeting_rules/actions/reorder · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
rule_ids |
array of string | yes | Every rule of the project, once, first evaluated first. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/targeting_rules/actions/reorder" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: All rules in the new order.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Get a targeting rule#
GET /v2/projects/{project_id}/targeting_rules/{rule_id} · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
rule_id |
string | yes |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/targeting_rules/$RULE_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The rule.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Update a targeting rule#
POST /v2/projects/{project_id}/targeting_rules/{rule_id} · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
rule_id |
string | yes |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | no | |
audience_id |
string or null | no | |
offering_id |
string | no | |
placements |
object | no | |
state |
active, inactive |
no | |
starts_at |
integer or null | no | |
ends_at |
integer or null | no |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/targeting_rules/$RULE_ID" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"state":"inactive"}'Responses
- 200: The rule;
revisiongoes up by one. - 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Delete a targeting rule#
DELETE /v2/projects/{project_id}/targeting_rules/{rule_id} · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
rule_id |
string | yes |
Example request
curl -s -X DELETE "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/targeting_rules/$RULE_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: Deleted. Returns Deleted.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Experiments#
Offering experiments: a control and up to three treatments, each with an offering and placement offerings; enrollment of new or new and existing customers, a saved or custom audience, a share, and a priority order between experiments; results with 95% intervals, lift and the chance to beat the control for 18 metrics, a daily series, CSV export and a 7-day audience estimate. See Experiments.
List experiments#
GET /v2/projects/{project_id}/experiments · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read
Oldest first. priority gives the enrollment order.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/experiments" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of experiments. Returns a list of Experiment.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Create an offering experiment#
POST /v2/projects/{project_id}/experiments · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read_write
Creates a draft at the bottom of the enrollment order (the highest priority). Send variants (a control and 1 to 3 treatments), or the older offering_a and offering_b for two variants. Defaults: type other, the type's metrics, enrollment new, everyone, 100%.
Once started, a customer who asks for offerings and is admitted (enrollment mode, then audience, then the share) joins one variant for good and gets its offering as current_offering_id and its placements. Enrolling records one EXPERIMENT_ENROLLMENT event, and the customer's lifecycle events carry experiments.
Answers 400 when: both variants and offering_a/offering_b are sent, or neither; audience_id and audience_rules are both sent; the audience, an offering or a placement offering is not in the project, or the offering is archived; a condition's field or operator is unknown; a placement id has other characters; a variant's id does not match its position; two variants serve the same offering and the same placement offerings; or enrollment is new_and_existing with track_paywall_views: false.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | 1 to 256 characters. |
type |
introductory_offer, free_trial_offer, paywall_design, price_point, subscription_duration, subscription_ordering, other |
no | What is tested. It sets the default metrics: see Experiments. |
primary_metric |
initial_conversion_rate, trial_conversion_rate, conversion_to_paying, refund_rate, realized_ltv_per_customer, realized_ltv_per_paying_customer, mrr_per_customer, mrr_per_paying_customer |
no | The metric that decides the winner: a rate or a per-customer mean, because those have an interval. |
secondary_metrics |
array of initial_conversion_rate, initial_conversions, trials_started, trials_completed, trials_converted, trial_conversion_rate, paid_customers, conversion_to_paying, active_subscribers, churned_subscribers, refunded_customers, refund_rate, realized_ltv, realized_ltv_per_customer, realized_ltv_per_paying_customer, mrr, mrr_per_customer, mrr_per_paying_customer |
no | Up to 12 more metrics shown first in the results. |
notes |
string | no | The hypothesis, in Markdown. |
enrollment |
new, new_and_existing |
no | new: only customers first seen at or after the experiment's first start. new_and_existing: anyone who asks for offerings while it runs. |
track_paywall_views |
boolean | no | Default false. Set to true with new_and_existing; false there answers 400. |
audience_id |
string or null | no | A saved audience. Send it or audience_rules, not both; setting one clears the other. |
audience_rules |
object or null | no | Conditions written for this experiment only, in the audience condition format. Null when it uses a saved audience or everyone. |
audience_rules.groups |
array of object | yes | Groups are OR-ed; conditions in a group are AND-ed. |
audience_rules.groups[].conditions |
array of object | yes | |
enrollment_percent |
integer | no | Share of matching customers to enroll. Default 100. |
variants |
array of object | no | The control first, then 1 to 3 treatments. |
variants[].id |
a, b, c, d |
no | Optional; must match the position (a, b, c, d). |
variants[].name |
string | no | Default: Control, Treatment B ... |
variants[].offering_id |
string | yes | Offering id in this project. |
variants[].placements |
object | no | Placement id (letters, digits, _, ., -, up to 100) → offering id, or null for no paywall. |
offering_a |
string | no | Older form: the control offering. Send it with offering_b instead of variants. |
offering_b |
string | no | Older form: the treatment offering. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/experiments" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"name":"Price point test","type":"price_point","notes":"## Hypothesis\n$14.99 a month earns more per customer than $9.99.","variants":[{"offering_id":"ofrngm2u3h89blc"},{"name":"Higher price","offering_id":"ofrng9x8y7z6w5a"}],"audience_rules":{"groups":[{"conditions":[{"field":"country","operator":"isAnyOf","value":"US"}]}]},"enrollment_percent":50}'Responses
- 201: The draft. Returns Experiment.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 201 response:
{
"object": "experiment",
"id": "prexp4k2m9q8z1x",
"project_id": "proj18pzzkao",
"name": "Price point test",
"type": "price_point",
"status": "draft",
"primary_metric": "realized_ltv_per_customer",
"secondary_metrics": [
"conversion_to_paying",
"initial_conversion_rate",
"refund_rate"
],
"notes": "## Hypothesis\n$14.99 a month earns more per customer than $9.99.",
"enrollment": "new",
"track_paywall_views": false,
"audience_id": null,
"audience_rules": {
"groups": [
{
"conditions": [
{
"field": "country",
"operator": "isAnyOf",
"value": "US"
}
]
}
]
},
"enrollment_percent": 50,
"priority": 3,
"variants": [
{
"id": "a",
"name": "Control",
"offering_id": "ofrngm2u3h89blc",
"placements": {}
},
{
"id": "b",
"name": "Higher price",
"offering_id": "ofrng9x8y7z6w5a",
"placements": {}
}
],
"started_at": null,
"paused_at": null,
"stopped_at": null,
"created_at": 1790800914012,
"updated_at": null,
"enrolled_customers": 0
}Set the enrollment order#
POST /v2/projects/{project_id}/experiments/actions/reorder · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read_write
When several running experiments could take the same customer, the one with the lowest priority enrolls them. Stopped experiments keep their relative order after these.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
experiment_ids |
array of string | yes | Every draft, running and paused experiment of the project, once, first enrolling first. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/experiments/actions/reorder" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"experiment_ids":["prexp7h3k1m0p2q","prexp4k2m9q8z1x"]}'Responses
- 200: Every experiment of the project in priority order, stopped ones last. Returns a list of Experiment.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Estimate how many customers would join#
POST /v2/projects/{project_id}/experiments/actions/estimate · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read, audiences:audiences:read
What the dashboard's create form shows: customers of the last 7 days who match the audience, times the percentage, split between the variants. Changes nothing.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
audience_id |
string or null | no | A saved audience. Send it or audience_rules, not both. |
audience_rules |
object or null | no | Conditions written for this experiment only, in the audience condition format. Null when it uses a saved audience or everyone. |
audience_rules.groups |
array of object | yes | Groups are OR-ed; conditions in a group are AND-ed. |
audience_rules.groups[].conditions |
array of object | yes | |
enrollment |
new, new_and_existing |
no | new (default) counts customers first seen in the last 7 days; new_and_existing counts customers seen in them. |
enrollment_percent |
integer | no | Default 100. |
variant_count |
integer | no | Default 2. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/experiments/actions/estimate" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"enrollment":"new","audience_rules":{"groups":[{"conditions":[{"field":"country","operator":"isAnyOf","value":"US"}]}]},"enrollment_percent":50,"variant_count":2}'Responses
- 200: The estimate. Returns ExperimentEstimate.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "experiment_estimate",
"period_days": 7,
"matching_customers": 1840,
"enrolled_customers": 920,
"customers_per_variant": 460,
"is_approximate": false
}Get an experiment#
GET /v2/projects/{project_id}/experiments/{experiment_id} · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
experiment_id |
string | yes | Experiment id (prexp...). |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/experiments/$EXPERIMENT_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The experiment. Returns Experiment.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Update an experiment#
POST /v2/projects/{project_id}/experiments/{experiment_id} · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read_write
A draft takes every field, with the same checks as create. offering_a and offering_b replace the first two variants' offerings and keep their names and placements. Once an experiment has started (running, paused or stopped), only name, type, primary_metric, secondary_metrics, notes and enrollment_percent can change; any other field answers 422 and names it.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
experiment_id |
string | yes | Experiment id (prexp...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | no | 1 to 256 characters. |
type |
introductory_offer, free_trial_offer, paywall_design, price_point, subscription_duration, subscription_ordering, other |
no | What is tested. It sets the default metrics: see Experiments. |
primary_metric |
initial_conversion_rate, trial_conversion_rate, conversion_to_paying, refund_rate, realized_ltv_per_customer, realized_ltv_per_paying_customer, mrr_per_customer, mrr_per_paying_customer |
no | The metric that decides the winner: a rate or a per-customer mean, because those have an interval. |
secondary_metrics |
array of initial_conversion_rate, initial_conversions, trials_started, trials_completed, trials_converted, trial_conversion_rate, paid_customers, conversion_to_paying, active_subscribers, churned_subscribers, refunded_customers, refund_rate, realized_ltv, realized_ltv_per_customer, realized_ltv_per_paying_customer, mrr, mrr_per_customer, mrr_per_paying_customer |
no | Up to 12 more metrics shown first in the results. |
notes |
string | no | The hypothesis, in Markdown. |
enrollment |
new, new_and_existing |
no | new: only customers first seen at or after the experiment's first start. new_and_existing: anyone who asks for offerings while it runs. |
track_paywall_views |
boolean | no | Default false. Set to true with new_and_existing; false there answers 400. |
audience_id |
string or null | no | A saved audience. Send it or audience_rules, not both; setting one clears the other. |
audience_rules |
object or null | no | Conditions written for this experiment only, in the audience condition format. Null when it uses a saved audience or everyone. |
audience_rules.groups |
array of object | yes | Groups are OR-ed; conditions in a group are AND-ed. |
audience_rules.groups[].conditions |
array of object | yes | |
enrollment_percent |
integer | no | Share of matching customers to enroll. Default 100. |
variants |
array of object | no | The control first, then 1 to 3 treatments. |
variants[].id |
a, b, c, d |
no | Optional; must match the position (a, b, c, d). |
variants[].name |
string | no | Default: Control, Treatment B ... |
variants[].offering_id |
string | yes | Offering id in this project. |
variants[].placements |
object | no | Placement id (letters, digits, _, ., -, up to 100) → offering id, or null for no paywall. |
offering_a |
string | no | Older form: the control offering. Send it with offering_b instead of variants. |
offering_b |
string | no | Older form: the treatment offering. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/experiments/$EXPERIMENT_ID" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"enrollment_percent":100,"notes":"Raised to 100% after a week."}'Responses
- 200: The experiment. Returns Experiment.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
Delete an experiment#
DELETE /v2/projects/{project_id}/experiments/{experiment_id} · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read_write
Deletes its enrollments and results. On their next request its customers get what targeting gives them, or join another running experiment that admits them. A running experiment answers 422: stop or pause it first.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
experiment_id |
string | yes | Experiment id (prexp...). |
Example request
curl -s -X DELETE "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/experiments/$EXPERIMENT_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: Deleted. Returns Deleted.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
Start or resume#
POST /v2/projects/{project_id}/experiments/{experiment_id}/actions/start · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read_write
A draft or paused experiment runs. The first start sets started_at; a resume clears paused_at and keeps started_at. Answers 422 for a running or stopped experiment, when one of its offerings was deleted or archived, or when it has fewer than two variants; 409 (resource_locked_error) when another request changed its status at the same time.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
experiment_id |
string | yes | Experiment id (prexp...). |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/experiments/$EXPERIMENT_ID/actions/start" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The experiment. Returns Experiment.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 409: It already exists, or it conflicts with another object. Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
Pause: enrolled customers keep their variant, nobody new joins#
POST /v2/projects/{project_id}/experiments/{experiment_id}/actions/pause · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read_write
Only a running experiment can pause (422 otherwise; 409 when another request changed its status at the same time). Results keep counting what enrolled customers do.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
experiment_id |
string | yes | Experiment id (prexp...). |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/experiments/$EXPERIMENT_ID/actions/pause" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The experiment. Returns Experiment.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 409: It already exists, or it conflicts with another object. Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
Stop for good#
POST /v2/projects/{project_id}/experiments/{experiment_id}/actions/stop · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read_write
A running or paused experiment stops (422 otherwise; 409 when another request changed its status at the same time). On their next request its customers get what targeting gives them, or join another running experiment that admits them. Results stay and keep updating as renewals and refunds arrive. A stopped experiment never enrolls anyone again; duplicate it instead.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
experiment_id |
string | yes | Experiment id (prexp...). |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/experiments/$EXPERIMENT_ID/actions/stop" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The experiment. Returns Experiment.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 409: It already exists, or it conflicts with another object. Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
Results per variant#
GET /v2/projects/{project_id}/experiments/{experiment_id}/results · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read
Every metric per variant, computed now from the enrolled customers' purchases made after they joined (and the renewals, refunds and trial conversions that follow), in USD. Production results leave out customers who joined from a test device (a Test Store app or an iOS sandbox build); environment=sandbox counts every enrolled customer with their test purchases. Above 25,000 enrolled customers the numbers come from a fixed random sample of 25,000, and sample says so. Rates have a Wilson interval; per-customer means a normal interval; treatments also have the lift over the control with its interval and the chance to beat the control. guidance says whether there is enough data and how many customers a 20% lift needs. series holds every metric by day.
The first release's fields stay: conversions, conversion_rate, trials, paying_customers, revenue and revenue_per_customer on each variant, and chance_b_beats_a and enough_data at the top. See Experiments for every definition and the statistics.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
experiment_id |
string | yes | Experiment id (prexp...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
environment |
production, sandbox |
no | Default production. sandbox counts test purchases instead. |
platform |
string | no | Only customers whose last request came from this platform (any case), such as ios or android. |
country |
string | no | Only customers whose last request came from this country (ISO 3166-1 alpha-2, any case). |
paywall |
all, viewed, not_viewed |
no | Customers with or without a paywall view after joining. Default viewed when the experiment tracks paywall views, else all. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/experiments/$EXPERIMENT_ID/results" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: Results. Returns ExperimentResults.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Export results as CSV#
GET /v2/projects/{project_id}/experiments/{experiment_id}/results/export · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read
The same numbers as the results, as an RFC 4180 CSV with CRLF line ends, named experiment-<id>-<kind>-<environment>-<YYYY-MM-DD>.csv. Summary columns: variant_id, variant_name, offering_id, customers, metric, metric_name, value, numerator, denominator, lower_95, upper_95, lift, lift_lower_95, lift_upper_95, chance_to_beat_control. Daily columns: date (UTC), variant_id, variant_name, then each metric id. Empty cells are values that do not apply; text that a spreadsheet would run as a formula starts with an apostrophe.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
experiment_id |
string | yes | Experiment id (prexp...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
kind |
summary, daily |
no | summary (default): one row per variant and metric. daily: one row per day and variant, one column per metric id. |
environment |
production, sandbox |
no | Default production. sandbox counts test purchases instead. |
platform |
string | no | Only customers whose last request came from this platform (any case), such as ios or android. |
country |
string | no | Only customers whose last request came from this country (ISO 3166-1 alpha-2, any case). |
paywall |
all, viewed, not_viewed |
no | Customers with or without a paywall view after joining. Default viewed when the experiment tracks paywall views, else all. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/experiments/$EXPERIMENT_ID/results/export" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The CSV file, as an attachment.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Paywalls#
Paywall components the SDK renders, their publishing and versions, and the images and fonts they use.
List paywalls#
GET /v2/projects/{project_id}/paywalls · Auth: secret key or dashboard session · Permissions: project_configuration:offerings:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
expand |
array of items.offering |
no |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/paywalls" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of paywalls.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Create a paywall#
POST /v2/projects/{project_id}/paywalls · Auth: secret key or dashboard session · Permissions: project_configuration:offerings:read_write
Either { offering_id } for an empty paywall on that offering, a full draft with components_config and components_localizations, or (RevenueDot extension) { template_id } for a gallery template built with the offering's packages (see GET /paywall_templates). An offering has at most one paywall. Nothing reaches the SDK until it is published.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Request body (application/json)
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/paywalls" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"offering_id":"ofrngm2u3h89blc","template_id":"trial_timeline","template_options":{"app_name":"Scanner","accent_color":"#2563eb","terms_url":"https://example.com/terms","privacy_url":"https://example.com/privacy"}}'Responses
- 201: The paywall.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 409: It already exists, or it conflicts with another object. Returns V2Error.
Example 201 response:
{
"object": "paywall",
"id": "pw1a2b3c4d5e6f7g8",
"name": "Go Pro",
"offering_id": "ofrngm2u3h89blc",
"created_at": 1790800901115,
"published_at": 1790801342625,
"automatically_scale_font_size": true,
"revision": 3
}Get a paywall#
GET /v2/projects/{project_id}/paywalls/{paywall_id} · Auth: secret key or dashboard session · Permissions: project_configuration:offerings:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
paywall_id |
string | yes | Paywall id (pw...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
expand |
array of offering, components |
no |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/paywalls/$PAYWALL_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The paywall.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "paywall",
"id": "pw1a2b3c4d5e6f7g8",
"name": "Go Pro",
"offering_id": "ofrngm2u3h89blc",
"created_at": 1790800901115,
"published_at": 1790801342625,
"automatically_scale_font_size": true,
"revision": 3
}Update a paywall's draft#
PATCH /v2/projects/{project_id}/paywalls/{paywall_id} · Auth: secret key or dashboard session · Permissions: project_configuration:offerings:read_write
Send the revision you read. A different revision answers 409 so two editors never overwrite each other. The draft starts from the published version when there are no unpublished changes.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
paywall_id |
string | yes | Paywall id (pw...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
revision |
integer | yes | |
components_config |
object | no | |
components_localizations |
object | no | |
default_locale |
string | no | |
offering_id |
string or null | no | |
name |
string or null | no | |
automatically_scale_font_size |
boolean | no | |
exit_offers |
object or null | no | |
state_declarations |
object or null | no | |
play_store_product_change_mode |
object or null | no |
Example request
curl -s -X PATCH "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/paywalls/$PAYWALL_ID" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"revision":3,"components_localizations":{"en_US":{"headline":"Go Pro"}}}'Responses
- 200: The paywall.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 409: It already exists, or it conflicts with another object. Returns V2Error.
Example 200 response:
{
"object": "paywall",
"id": "pw1a2b3c4d5e6f7g8",
"name": "Go Pro",
"offering_id": "ofrngm2u3h89blc",
"created_at": 1790800901115,
"published_at": 1790801342625,
"automatically_scale_font_size": true,
"revision": 3
}Delete a paywall#
DELETE /v2/projects/{project_id}/paywalls/{paywall_id} · Auth: secret key or dashboard session · Permissions: project_configuration:offerings:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
paywall_id |
string | yes | Paywall id (pw...). |
Example request
curl -s -X DELETE "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/paywalls/$PAYWALL_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: Deleted. Returns Deleted.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "paywall",
"id": "pw1a2b3c4d5e6f7g8",
"deleted_at": 1790801342625
}Publish a paywall#
POST /v2/projects/{project_id}/paywalls/{paywall_id}/actions/publish · Auth: secret key or dashboard session · Permissions: project_configuration:offerings:read_write
The draft becomes what the SDK receives in paywall_components for the paywall's offering (and as a workflow in remote config). Needs an offering and unpublished changes, and the components must decode in the SDKs: otherwise 422 names the first problem (POST /paywalls/validate lists all). Locales missing a string are served the default locale's.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
paywall_id |
string | yes | Paywall id (pw...). |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/paywalls/$PAYWALL_ID/actions/publish" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The paywall.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
Example 200 response:
{
"object": "paywall",
"id": "pw1a2b3c4d5e6f7g8",
"name": "Go Pro",
"offering_id": "ofrngm2u3h89blc",
"created_at": 1790800901115,
"published_at": 1790801342625,
"automatically_scale_font_size": true,
"revision": 3
}Unpublish a paywall#
POST /v2/projects/{project_id}/paywalls/{paywall_id}/actions/unpublish · Auth: secret key or dashboard session · Permissions: project_configuration:offerings:read_write
The SDK stops receiving it. The content is kept as the draft.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
paywall_id |
string | yes | Paywall id (pw...). |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/paywalls/$PAYWALL_ID/actions/unpublish" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The paywall.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
Example 200 response:
{
"object": "paywall",
"id": "pw1a2b3c4d5e6f7g8",
"name": "Go Pro",
"offering_id": "ofrngm2u3h89blc",
"created_at": 1790800901115,
"published_at": 1790801342625,
"automatically_scale_font_size": true,
"revision": 3
}Attach an offering to a paywall#
POST /v2/projects/{project_id}/paywalls/{paywall_id}/actions/attach_offering · Auth: secret key or dashboard session · Permissions: project_configuration:offerings:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
paywall_id |
string | yes | Paywall id (pw...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
offering_id |
string | yes |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/paywalls/$PAYWALL_ID/actions/attach_offering" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"offering_id":"ofrngm2u3h89blc"}'Responses
- 200: The paywall.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 409: It already exists, or it conflicts with another object. Returns V2Error.
Example 200 response:
{
"object": "paywall",
"id": "pw1a2b3c4d5e6f7g8",
"name": "Go Pro",
"offering_id": "ofrngm2u3h89blc",
"created_at": 1790800901115,
"published_at": 1790801342625,
"automatically_scale_font_size": true,
"revision": 3
}Detach the offering from a paywall#
POST /v2/projects/{project_id}/paywalls/{paywall_id}/actions/detach_offering · Auth: secret key or dashboard session · Permissions: project_configuration:offerings:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
paywall_id |
string | yes | Paywall id (pw...). |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/paywalls/$PAYWALL_ID/actions/detach_offering" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The paywall.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "paywall",
"id": "pw1a2b3c4d5e6f7g8",
"name": "Go Pro",
"offering_id": "ofrngm2u3h89blc",
"created_at": 1790800901115,
"published_at": 1790801342625,
"automatically_scale_font_size": true,
"revision": 3
}Duplicate a paywall#
POST /v2/projects/{project_id}/paywalls/{paywall_id}/actions/duplicate · Auth: secret key or dashboard session · Permissions: project_configuration:offerings:read_write
Copies the draft (default) or the published version into a new unpublished paywall, optionally on a new offering.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
paywall_id |
string | yes | Paywall id (pw...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | no | |
source_version |
draft, published |
no | |
offering |
object | no | |
offering.lookup_key |
string | yes | |
offering.display_name |
string | yes |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/paywalls/$PAYWALL_ID/actions/duplicate" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"source_version":"published","offering":{"lookup_key":"summer","display_name":"Summer sale"}}'Responses
- 201: The new paywall.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 409: It already exists, or it conflicts with another object. Returns V2Error.
List saved snapshots#
GET /v2/projects/{project_id}/paywalls/{paywall_id}/versions · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
paywall_id |
string | yes | Paywall id (pw...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/paywalls/$PAYWALL_ID/versions" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of snapshots (without their content).
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Save a named snapshot#
POST /v2/projects/{project_id}/paywalls/{paywall_id}/versions · Auth: secret key or dashboard session · Permissions: project_configuration:offerings:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
paywall_id |
string | yes | Paywall id (pw...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/paywalls/$PAYWALL_ID/versions" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"name":"Before the summer test"}'Responses
- 201: The snapshot.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
Restore a snapshot into the draft#
POST /v2/projects/{project_id}/paywalls/{paywall_id}/versions/{version_id}/actions/restore · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read_write
The snapshot becomes the draft (the revision bumps). Publish to send it to apps.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
paywall_id |
string | yes | Paywall id (pw...). |
version_id |
string | yes |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/paywalls/$PAYWALL_ID/versions/$VERSION_ID/actions/restore" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The paywall with
components. - 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Get a snapshot#
GET /v2/projects/{project_id}/paywalls/{paywall_id}/versions/{version_id} · Auth: secret key or dashboard session · Permissions: project_configuration:offerings:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
paywall_id |
string | yes | Paywall id (pw...). |
version_id |
string | yes |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/paywalls/$PAYWALL_ID/versions/$VERSION_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The snapshot.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Get the template form of a paywall#
GET /v2/projects/{project_id}/paywalls/{paywall_id}/template · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read
The dashboard's template form (headline, features, colours, package labels) that produced the draft, so the editor can reopen it. Null for paywalls made another way.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
paywall_id |
string | yes | Paywall id (pw...). |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/paywalls/$PAYWALL_ID/template" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The form.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Store the template form of a paywall#
PUT /v2/projects/{project_id}/paywalls/{paywall_id}/template · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read_write
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
paywall_id |
string | yes | Paywall id (pw...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
template |
object or null | yes |
Example request
curl -s -X PUT "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/paywalls/$PAYWALL_ID/template" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"template":{"template":"classic","headline":"Unlock everything"}}'Responses
- 200: The form.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
List the template gallery#
GET /v2/projects/{project_id}/paywall_templates · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read
The ten gallery layouts with the fields the dashboard filters on. Create a paywall from one with POST /paywalls and template_id. icon_base_url is where the built-in icons are served.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/paywall_templates" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The templates.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Validate paywall components#
POST /v2/projects/{project_id}/paywalls/validate · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read
Checks components JSON the way the SDKs decode it, without saving: errors stop publishing (the SDK would fail or render it wrong), warnings do not. With repair: true it first fills missing required fields and inline texts (the same repair as the AI generator) and returns the repaired JSON and what it fixed. With offering_id, packages are checked against the offering.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
components_config |
object | yes | |
components_localizations |
object | yes | |
default_locale |
string | no | |
offering_id |
string or null | no | |
repair |
boolean | no |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/paywalls/validate" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"components_config":{"base":{"stack":{"type":"stack","components":[{"type":"text","text":"Go Pro"}]}}},"components_localizations":{},"repair":true}'Responses
- 200: The result.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Whether the AI generator is available#
GET /v2/projects/{project_id}/paywalls/ai · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read
RevenueDot Cloud uses GPT-6 Luna through the Vercel AI Gateway. A self-hosted server needs AI_GATEWAY_API_KEY, OPENAI_API_KEY or ANTHROPIC_API_KEY; without one, available is false.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/paywalls/ai" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The generator.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Generate a paywall with AI#
POST /v2/projects/{project_id}/paywalls/generate · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read_write
Designs a paywall with the language model and returns it without saving. The model first reads the prompt into a brief (plans, trial, benefits, look), then designs the paywall; a checker holds the design to the brief and sends problems back for up to two fixes. The result always passes validation. Save it with POST /paywalls. One generation every 5 seconds and 60 a day per project.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
prompt |
string | yes | |
app_name |
string | no | |
brand_colors |
array of string | no | |
offering_id |
string or null | no | |
locale |
string | no |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/paywalls/generate" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"prompt":"A calm sleep app, explain the 7-day trial, yearly first","app_name":"Calm","brand_colors":["#0f766e"],"offering_id":"ofrngm2u3h89blc"}'Responses
- 200: A paywall draft.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 429: Too many requests. Retry later. Returns V2Error.
- 502: An upstream service (the store or the language model) gave no usable answer. Retry later. Returns V2Error.
- 503: The store could not be reached, or the feature is not configured on this server. Returns V2Error.
List images#
GET /v2/projects/{project_id}/media_assets · Auth: secret key or dashboard session · Permissions: project_configuration:offerings:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/media_assets" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of images.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Upload an image#
POST /v2/projects/{project_id}/media_assets · Auth: secret key or dashboard session · Permissions: project_configuration:offerings:read_write
Up to about 2 MB. Width and height are read from PNG, JPEG and WebP files. A project holds up to 200 assets.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
filename |
string | yes | |
content_type |
image/jpeg, image/png, image/avif, image/heic, image/heif, image/webp |
yes | |
file_data_base64 |
string | yes | Base64 file bytes. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/media_assets" -H "Authorization: Bearer $SECRET_KEY"Responses
- 201: The image.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
List fonts#
GET /v2/projects/{project_id}/fonts · Auth: secret key or dashboard session · Permissions: project_configuration:offerings:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/fonts" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of fonts.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Upload a font#
POST /v2/projects/{project_id}/fonts · Auth: secret key or dashboard session · Permissions: project_configuration:offerings:read_write
A .ttf or .otf file. Its PostScript name, family, style and weight are read from the file.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
filename |
string | yes | |
content_type |
font/ttf, font/otf |
yes | |
file_data_base64 |
string | yes | Base64 file bytes. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/fonts" -H "Authorization: Bearer $SECRET_KEY"Responses
- 201: The font.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Download a paywall image or font#
GET /assets/{project_id}/{object_name} · Auth: none
Public and cached for a year (Cache-Control: immutable, a strong ETag, 304 on If-None-Match): object names are random and never change content. The SDK reads asset_base_url + object name. On Cloud repeat downloads come from Cloudflare's edge cache.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
object_name |
string | yes |
Example request
curl -s "$REVENUEDOT_URL/assets/$PROJECT_ID/$OBJECT_NAME"Responses
- 200: The file bytes with their content type.
- 404: No such asset.
Download a built-in paywall icon#
GET /assets/icons/{file} · Auth: none · RevenueDot extension
The icons that icon and timeline components use (base_url + formats.heic). Cached for a year.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
file |
string | yes | {name}.png (96 × 96, white on transparent; the SDKs tint it), .heic and .webp names serve the same PNG, or {name}.svg. |
Example request
curl -s "$REVENUEDOT_URL/assets/icons/$FILE"Responses
- 200: The image.
- 404: No such icon.
Delete an uploaded font#
DELETE /v2/projects/{project_id}/fonts/{font_id} · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:offerings:read_write
Paywalls that still name the font show the system font in the SDKs.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
font_id |
string | yes | Starts with fnt. |
Example request
curl -s -X DELETE "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/fonts/$FONT_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: Deleted.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Webhook integrations#
Where events are sent.
List webhooks#
GET /v2/projects/{project_id}/integrations/webhooks · Auth: secret key or dashboard session · Permissions: project_configuration:integrations:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/integrations/webhooks" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of results. Returns a list of WebhookIntegration.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Create a webhook#
POST /v2/projects/{project_id}/integrations/webhooks · Auth: secret key or dashboard session · Permissions: project_configuration:integrations:read_write
The answer includes signing_secret (whsec_...) once. Store it: it verifies the X-RevenueCat-Webhook-Signature header. See Webhooks.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | |
url |
string | yes | http(s) URL. |
authorization_header |
string or null | no | Sent as the Authorization header. |
environment |
production, sandbox, null |
no | Null or absent: both. |
event_types |
array of initial_purchase, renewal, product_change, cancellation, billing_issue, non_renewing_purchase, uncancellation, transfer, subscription_paused, expiration, subscription_extended, invoice_issuance, temporary_entitlement_grant, refund_reversed, virtual_currency_transaction, test, experiment_enrollment, purchase_redeemed, subscriber_alias, price_increase_consent_required, price_increase_consent_approved, funnel_viewed, funnel_step_completed, funnel_purchase, paywall_impression, paywall_close, paywall_cancel, paywall_exit_offer, paywall_component_interacted, paywall_purchase_initiated, paywall_purchase_error |
no | Empty or absent: every type except the opt-in ones (subscriber_alias, the three RevenueDot funnel types and the seven paywall types), which are sent only when named here. |
app_id |
string or null | no | Only this app's events. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/integrations/webhooks" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"name":"Backend","url":"https://api.example.com/webhooks/revenuedot","authorization_header":"Bearer my-shared-token","environment":"production","event_types":["initial_purchase","renewal"]}'Responses
- 201: The webhook with its signing secret. Returns WebhookIntegration.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 201 response:
{
"object": "webhook_integration",
"id": "wh_ceps8nr7mczvhaqw",
"project_id": "proj18pzzkao",
"name": "Backend",
"url": "https://api.example.com/webhooks/revenuedot",
"environment": "production",
"event_types": [
"initial_purchase",
"renewal"
],
"app_id": null,
"created_at": 1790801342625,
"signing_secret": "whsec_3f5b7fb5a591c17aaa9108376df0bddbe1555f0a908bfdc0"
}Get a webhook#
GET /v2/projects/{project_id}/integrations/webhooks/{webhook_integration_id} · Auth: secret key or dashboard session · Permissions: project_configuration:integrations:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
webhook_integration_id |
string | yes | Webhook id (wh_...). |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/integrations/webhooks/$WEBHOOK_INTEGRATION_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The webhook. Returns WebhookIntegration.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Update a webhook#
POST /v2/projects/{project_id}/integrations/webhooks/{webhook_integration_id} · Auth: secret key or dashboard session · Permissions: project_configuration:integrations:read_write
enabled is a RevenueDot extension: false pauses deliveries without deleting the webhook. Events recorded while it is off are not sent; queued retries resume when it is turned on. Read it with GET /v2/projects/{project_id}/webhooks.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
webhook_integration_id |
string | yes | Webhook id. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | no | |
url |
string | no | |
authorization_header |
string or null | no | |
environment |
string or null | no | |
event_types |
array of string | no | |
app_id |
string or null | no | |
enabled |
boolean | no | RevenueDot extension. False pauses deliveries. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/integrations/webhooks/$WEBHOOK_INTEGRATION_ID" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"enabled":false}'Responses
- 200: The webhook. Returns WebhookIntegration.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Delete a webhook#
DELETE /v2/projects/{project_id}/integrations/webhooks/{webhook_integration_id} · Auth: secret key or dashboard session · Permissions: project_configuration:integrations:read_write
Pending deliveries are deleted with it.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
webhook_integration_id |
string | yes | Webhook id. |
Example request
curl -s -X DELETE "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/integrations/webhooks/$WEBHOOK_INTEGRATION_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: Deleted. Returns Deleted.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "webhook_integration",
"id": "…",
"deleted_at": 1790801342625
}Discounts#
Web discounts for RevenueDot's web checkout, with RevenueCat's v2 discount operations and shapes. Each discount is a Stripe coupon and each code a Stripe promotion code in your own Stripe account. See Web discounts.
List discounts#
GET /v2/projects/{project_id}/discounts · Auth: secret key or dashboard session · Permissions: project_configuration:discounts:read
The project's web discounts in RevenueCat's Discount shape. RevenueDot's extra settings (max_redemptions, expires_at), the codes and the Stripe ids are on GET /web_discounts.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/discounts" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of discounts. Returns a list of Discount.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "list",
"items": [
{
"object": "discount",
"id": "disc5k2m9q4x7a1b3c",
"identifier": "spring20",
"customer_facing_name": "Spring sale",
"duration_mode": "time_window",
"eligibility": "everyone",
"time_window": "P3M",
"disabled_at": null,
"type": "percentage",
"percentage": 20,
"created_at": 1790800901115,
"updated_at": 1790801342625
}
],
"next_page": null,
"url": "/v2/projects/proj18pzzkao/discounts"
}Create a discount#
POST /v2/projects/{project_id}/discounts · Auth: secret key or dashboard session · Permissions: project_configuration:discounts:read_write
Creates the discount and a Stripe coupon for it in every Stripe app of the project that has a key: percent_off, or amount_off and currency with currency_options for the other currencies; duration once, repeating with duration_in_months, or forever; applies_to.products from product_identifiers; max_redemptions; redeem_by from expires_at. Add codes with POST /discounts/{discount_id}/discount_codes, or set it as a purchase link's or paywall step's automatic discount.
max_redemptions and expires_at are RevenueDot additions to RevenueCat's request. 409 when the identifier exists. Stripe errors answer 422 store_error (retryable: true while Stripe is unavailable) and nothing is saved. See Web discounts.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
identifier |
string | yes | Letters, digits, _ . -. |
customer_facing_name |
string | yes | |
type |
percentage, fixed_amount |
yes | |
percentage |
integer | no | For percentage: 1 to 100. |
fixed_amounts |
object | no | For fixed_amount: amount off by currency, keyed by the currency code. |
duration_mode |
one_time, time_window, forever |
yes | |
eligibility |
everyone, never_purchased, never_subscribed, never_subscribed_to_the_same_product |
yes | |
time_window |
string | no | For time_window: whole months or years, 1 to 36 months (P3M, P1Y). Stripe repeats a coupon only by months. |
product_identifiers |
array of string | no | Store identifiers (the Stripe price id of a web product) or product ids. Empty or absent: every product. |
max_redemptions |
integer or null | no | RevenueDot addition: paid checkouts it may be used in. Sent to Stripe as the coupon's max_redemptions. |
expires_at |
integer or null | no | RevenueDot addition: when it stops working, epoch milliseconds. Sent to Stripe as redeem_by. |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/discounts" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"identifier":"spring20","customer_facing_name":"Spring sale","type":"percentage","percentage":20,"duration_mode":"time_window","time_window":"P3M","eligibility":"everyone","max_redemptions":100}'Responses
- 201: The discount. Returns Discount.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 409: It already exists, or it conflicts with another object. Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
Example 201 response:
{
"object": "discount",
"id": "disc5k2m9q4x7a1b3c",
"identifier": "spring20",
"customer_facing_name": "Spring sale",
"duration_mode": "time_window",
"eligibility": "everyone",
"time_window": "P3M",
"disabled_at": null,
"type": "percentage",
"percentage": 20,
"created_at": 1790800901115,
"updated_at": 1790801342625
}Get a discount#
GET /v2/projects/{project_id}/discounts/{discount_id} · Auth: secret key or dashboard session · Permissions: project_configuration:discounts:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
discount_id |
string | yes | Discount id (disc...). |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/discounts/$DISCOUNT_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The discount. Returns Discount.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "discount",
"id": "disc8h3n1v6w0z2y4d",
"identifier": "five_off",
"customer_facing_name": "$5 off",
"duration_mode": "one_time",
"eligibility": "never_purchased",
"time_window": null,
"disabled_at": null,
"type": "fixed_amount",
"fixed_amount": {
"USD": 5,
"EUR": 4.5
},
"created_at": 1790800901115,
"updated_at": 1790801342625
}Update a discount#
PATCH /v2/projects/{project_id}/discounts/{discount_id} · Auth: secret key or dashboard session · Permissions: project_configuration:discounts:read_write
Send only what changes. A Stripe coupon cannot change its amount or duration, so a change to type, percentage, fixed_amounts, duration_mode, time_window, product_identifiers, max_redemptions or expires_at creates a new coupon and new promotion codes, and turns the old promotion codes off. Subscriptions that already use the old coupon keep it. A new name or eligibility changes nothing in Stripe. Stripe errors answer 422 store_error (retryable: true while Stripe is unavailable) and nothing is saved.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
discount_id |
string | yes | Discount id (disc...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
customer_facing_name |
string | no | |
type |
percentage, fixed_amount |
no | |
percentage |
integer | no | For percentage: 1 to 100. |
fixed_amounts |
object | no | For fixed_amount: amount off by currency, keyed by the currency code. |
duration_mode |
one_time, time_window, forever |
no | |
eligibility |
everyone, never_purchased, never_subscribed, never_subscribed_to_the_same_product |
no | |
time_window |
string | no | For time_window: whole months or years, 1 to 36 months (P3M, P1Y). Stripe repeats a coupon only by months. |
product_identifiers |
array of string | no | Store identifiers (the Stripe price id of a web product) or product ids. Empty or absent: every product. |
max_redemptions |
integer or null | no | RevenueDot addition: paid checkouts it may be used in. Sent to Stripe as the coupon's max_redemptions. |
expires_at |
integer or null | no | RevenueDot addition: when it stops working, epoch milliseconds. Sent to Stripe as redeem_by. |
Example request
curl -s -X PATCH "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/discounts/$DISCOUNT_ID" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"percentage":30,"customer_facing_name":"Spring sale 30"}'Responses
- 200: The discount. Returns Discount.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
Example 200 response:
{
"object": "discount",
"id": "disc5k2m9q4x7a1b3c",
"identifier": "spring20",
"customer_facing_name": "Spring sale 30",
"duration_mode": "time_window",
"eligibility": "everyone",
"time_window": "P3M",
"disabled_at": null,
"type": "percentage",
"percentage": 30,
"created_at": 1790800901115,
"updated_at": 1790801342625
}Delete a discount#
DELETE /v2/projects/{project_id}/discounts/{discount_id} · Auth: secret key or dashboard session · Permissions: project_configuration:discounts:read_write
Turns its promotion codes off and deletes its Stripe coupons. Subscriptions that already use the coupon keep the discount, as in Stripe. Stripe errors answer 422 store_error (retryable: true while Stripe is unavailable) and nothing is saved.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
discount_id |
string | yes | Discount id (disc...). |
Example request
curl -s -X DELETE "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/discounts/$DISCOUNT_ID" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: Deleted. Returns Deleted.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
Example 200 response:
{
"object": "discount",
"id": "disc5k2m9q4x7a1b3c",
"deleted_at": 1790801342625
}Enable a discount#
POST /v2/projects/{project_id}/discounts/{discount_id}/actions/enable · Auth: secret key or dashboard session · Permissions: project_configuration:discounts:read_write
Checkout accepts it again and its Stripe promotion codes are turned back on. Stripe errors answer 422 store_error (retryable: true while Stripe is unavailable) and nothing is saved.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
discount_id |
string | yes | Discount id (disc...). |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/discounts/$DISCOUNT_ID/actions/enable" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The discount. Returns Discount.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
Example 200 response:
{
"object": "discount",
"id": "disc5k2m9q4x7a1b3c",
"identifier": "spring20",
"customer_facing_name": "Spring sale",
"duration_mode": "time_window",
"eligibility": "everyone",
"time_window": "P3M",
"disabled_at": null,
"type": "percentage",
"percentage": 20,
"created_at": 1790800901115,
"updated_at": 1790801342625
}Disable a discount#
POST /v2/projects/{project_id}/discounts/{discount_id}/actions/disable · Auth: secret key or dashboard session · Permissions: project_configuration:discounts:read_write
Checkout refuses it ("This code is no longer active.") and its Stripe promotion codes are turned off. Stripe errors answer 422 store_error (retryable: true while Stripe is unavailable) and nothing is saved.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
discount_id |
string | yes | Discount id (disc...). |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/discounts/$DISCOUNT_ID/actions/disable" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: The discount. Returns Discount.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
Example 200 response:
{
"object": "discount",
"id": "disc5k2m9q4x7a1b3c",
"identifier": "spring20",
"customer_facing_name": "Spring sale",
"duration_mode": "time_window",
"eligibility": "everyone",
"time_window": "P3M",
"disabled_at": 1790801342625,
"type": "percentage",
"percentage": 20,
"created_at": 1790800901115,
"updated_at": 1790801342625
}List a discount's codes#
GET /v2/projects/{project_id}/discounts/{discount_id}/discount_codes · Auth: secret key or dashboard session · Permissions: project_configuration:discounts:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
discount_id |
string | yes | Discount id (disc...). |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/discounts/$DISCOUNT_ID/discount_codes" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of codes. Returns a list of DiscountCode.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "list",
"items": [
{
"object": "discount_code",
"code": "SPRING20",
"created_at": 1790800901115
}
],
"next_page": null,
"url": "/v2/projects/proj18pzzkao/discounts/disc5k2m9q4x7a1b3c/discount_codes"
}Create discount codes#
POST /v2/projects/{project_id}/discounts/{discount_id}/discount_codes · Auth: secret key or dashboard session · Permissions: project_configuration:discounts:read_write
Each code becomes a Stripe promotion code on the discount's coupon, active unless the discount is disabled. Codes are unique in the project whatever their case (409 when one is taken). Letters, digits, _ and -. Stripe errors answer 422 store_error (retryable: true while Stripe is unavailable) and nothing is saved.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
discount_id |
string | yes | Discount id (disc...). |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
codes |
array of string | yes |
Example request
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/discounts/$DISCOUNT_ID/discount_codes" -H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" -d '{"codes":["SPRING20","friends"]}'Responses
- 201: The new codes.
- 400: The request is invalid. Returns V2Error.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 409: It already exists, or it conflicts with another object. Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
Example 201 response:
[
{
"object": "discount_code",
"code": "SPRING20",
"created_at": 1790800901115
},
{
"object": "discount_code",
"code": "friends",
"created_at": 1790800901115
}
]Delete a discount code#
DELETE /v2/projects/{project_id}/discounts/{discount_id}/discount_codes/{discount_code} · Auth: secret key or dashboard session · Permissions: project_configuration:discounts:read_write
Its Stripe promotion code is turned off. Stripe errors answer 422 store_error (retryable: true while Stripe is unavailable) and nothing is saved.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
discount_id |
string | yes | Discount id (disc...). |
discount_code |
string | yes | The code, URL-encoded. Any case. |
Example request
curl -s -X DELETE "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/discounts/$DISCOUNT_ID/discount_codes/$DISCOUNT_CODE" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: Deleted. Returns Deleted.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
- 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
Example 200 response:
{
"object": "discount_code",
"id": "friends",
"deleted_at": 1790801342625
}Invoices#
RevenueCat Billing invoices. Stripe issues the invoices for RevenueDot's web checkout, so these answer on purpose: the list is empty and a file is 404.
List a customer's invoices#
GET /v2/projects/{project_id}/customers/{customer_id}/invoices · Auth: secret key or dashboard session · Permissions: customer_information:invoices:read
Always an empty list for a known customer; 404 for an unknown one. Invoices belong to RevenueCat Billing, which issues its own. For web purchases through RevenueDot's checkout, Stripe issues the invoices in your Stripe account.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
customer_id |
string | yes | Any app user id of the customer. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit |
integer | no | Page size. Values outside 1-100 are clamped, not rejected. |
starting_after |
string | no | Id of the last item of the previous page. Use next_page instead of building it. |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/customers/user_1/invoices" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: Always an empty list.
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Example 200 response:
{
"object": "list",
"items": [],
"next_page": null,
"url": "/v2/projects/proj18pzzkao/customers/user_1/invoices"
}Download an invoice#
GET /v2/projects/{project_id}/customers/{customer_id}/invoices/{invoice_id}/file · Auth: secret key or dashboard session · Permissions: customer_information:invoices:read
Answers 404 resource_missing: there is no invoice. Invoices belong to RevenueCat Billing, which issues its own. For web purchases through RevenueDot's checkout, Stripe issues the invoices in your Stripe account.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
customer_id |
string | yes | Any app user id of the customer. |
invoice_id |
string | yes |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/customers/user_1/invoices/$INVOICE_ID/file" -H "Authorization: Bearer $SECRET_KEY"Responses
- 401: No API key, or an unknown one. Returns V2Error.
- 402: Live data is paused: the project's owner went live more than 14 days ago without starting Pro (RevenueDot Cloud only). Sandbox reads (
environment=sandbox) and secret-key reads of one customer, subscription or purchase are never paused. Returns V2Error. - 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Collaborators#
Dashboard users of the project.
List collaborators#
GET /v2/projects/{project_id}/collaborators · Auth: secret key or dashboard session · Permissions: project_configuration:collaborators:read
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
project_id |
string | yes | Project id (proj...). |
Example request
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/collaborators" -H "Authorization: Bearer $SECRET_KEY"Responses
- 200: A page of results. Returns a list of Collaborator.
- 401: No API key, or an unknown one. Returns V2Error.
- 403: The key lacks a permission, or a public key was used. Returns V2Error.
- 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
Objects#
The shapes the operations above send and return.
ActiveEntitlement#
| Field | Type | Required | Description |
|---|---|---|---|
object |
"customer.active_entitlement" |
yes | |
entitlement_id |
string | yes | Entitlement id (entl...), not the lookup key. |
expires_at |
integer or null | yes | When access ends. Epoch milliseconds, or null. |
App#
Only the object for the app's own type is present. Store secrets are never returned.
| Field | Type | Required | Description |
|---|---|---|---|
object |
"app" |
yes | |
id |
string | yes | App id (app...). |
name |
string | yes | |
created_at |
integer | yes | Creation time. Epoch milliseconds. |
type |
amazon, app_store, mac_app_store, play_store, stripe, rc_billing, roku, paddle, test_store, galaxy |
yes | |
project_id |
string | yes | |
custom_url_scheme |
string | no | Derived from the public key. |
app_store |
object | no | |
app_store.bundle_id |
string | no | |
app_store.app_store_connect_api_key_configured |
boolean | no | |
app_store.subscription_key_configured |
boolean | no | True when the in-app purchase key (.p8, key id, issuer id) is set. |
app_store.app_store_connect_vendor_number |
string or null | no | |
mac_app_store |
object | no | |
mac_app_store.bundle_id |
string | no | |
play_store |
object | no | |
play_store.package_name |
string | no | |
play_store.play_service_account_credentials_configured |
boolean | no | |
amazon |
object | no | |
amazon.package_name |
string | no | |
stripe |
object | no | |
stripe.stripe_account_id |
string or null | no | |
rc_billing |
object | no | |
rc_billing.stripe_account_id |
string or null | no | |
rc_billing.seller_company_name |
string | no | |
rc_billing.app_name |
string | no | |
rc_billing.support_email |
string or null | no | |
rc_billing.default_currency |
string | no | |
roku |
object | no | |
roku.roku_channel_id |
string or null | no | |
roku.roku_channel_name |
string or null | no | |
paddle |
object | no | |
paddle.paddle_is_sandbox |
boolean | no | True for a sandbox key (pdl_sdbx_apikey_…), or for an older key marked sandbox. |
paddle.paddle_api_key |
null | no | Always null: the key is never returned (RevenueCat's field). |
galaxy |
object | no | RevenueDot extension. |
galaxy.package_name |
string | no |
Collaborator#
| Field | Type | Required | Description |
|---|---|---|---|
object |
"collaborator" |
yes | |
id |
string | yes | |
name |
string or null | no | |
email |
string | yes | |
role |
admin, developer, read_only |
yes | RevenueCat's role names. read_only is the dashboard's Viewer role. |
accepted_at |
integer | no | When the user joined. Epoch milliseconds. |
has_mfa |
boolean | no | Always false. |
Customer#
active_entitlements and experiment are present on single-customer answers; attributes only with expand=attributes.
| Field | Type | Required | Description |
|---|---|---|---|
object |
"customer" |
yes | |
id |
string | yes | The customer's original app user id. |
project_id |
string | yes | |
first_seen_at |
integer | yes | First seen. Epoch milliseconds. |
last_seen_at |
integer or null | yes | Last seen. Epoch milliseconds, or null. |
last_seen_app_version |
string or null | no | |
last_seen_country |
string or null | no | |
last_seen_platform |
string or null | no | |
last_seen_platform_version |
null | no | |
active_entitlements |
object | no | |
active_entitlements.object |
"list" |
yes | |
active_entitlements.items |
array of ActiveEntitlement | yes | |
active_entitlements.next_page |
string or null | yes | Path of the next page, or null on the last page. |
active_entitlements.url |
string | yes | Path of this list. |
experiment |
ExperimentEnrollment or null | no | The experiment the customer is in (running or paused), else the last one they joined, or null. |
attributes |
object | no | |
attributes.object |
"list" |
yes | |
attributes.items |
array of CustomerAttribute | yes | |
attributes.next_page |
string or null | yes | Path of the next page, or null on the last page. |
attributes.url |
string | yes | Path of this list. |
CustomerAlias#
| Field | Type | Required | Description |
|---|---|---|---|
object |
"customer.alias" |
yes | |
id |
string | yes | An app user id of the customer. |
created_at |
integer | yes | When it was linked. Epoch milliseconds. |
CustomerAttribute#
| Field | Type | Required | Description |
|---|---|---|---|
object |
"customer.attribute" |
yes | |
name |
string | yes | |
value |
string | yes | |
updated_at |
integer | yes | Last update. Epoch milliseconds. |
CustomerEvent#
| Field | Type | Required | Description |
|---|---|---|---|
object |
"customer.event" |
yes | |
id |
string | yes | |
app_id |
string or null | no | |
type |
string | yes | Webhook event type, for example INITIAL_PURCHASE. |
body |
object | yes | The webhook event object. |
created_at |
integer | yes | Recorded. Epoch milliseconds. |
occurred_at |
integer | yes | When it happened. Epoch milliseconds. |
Deleted#
| Field | Type | Required | Description |
|---|---|---|---|
object |
string | yes | The deleted object's type. |
id |
string | yes | |
deleted_at |
integer | yes | When it was deleted. Epoch milliseconds. |
Discount#
A web discount, in RevenueCat's Discount shape. Each one is a Stripe coupon in every Stripe app of the project.
| Field | Type | Required | Description |
|---|---|---|---|
object |
discount |
yes | |
id |
string | yes | Discount id (disc...). |
identifier |
string | yes | Your identifier for the discount, unique in the project. |
customer_facing_name |
string | yes | The name buyers see at checkout and Stripe puts on the coupon (first 40 characters). |
duration_mode |
one_time, time_window, forever |
yes | one_time: the first payment. time_window: every payment for time_window. forever: every payment. |
eligibility |
everyone, never_purchased, never_subscribed, never_subscribed_to_the_same_product |
yes | Who may use it, checked against the buyer's purchase history when the checkout has an app user id. |
time_window |
string or null | no | ISO 8601 months or years (P3M, P1Y), 1 to 36 months, for time_window. Otherwise null. |
disabled_at |
integer or null | no | When it was disabled. Epoch milliseconds, or null. |
type |
percentage |
yes | |
percentage |
integer | yes | Percent off, 1 to 100. |
created_at |
integer | yes | When it was created. Epoch milliseconds. |
updated_at |
integer | yes | When it last changed. Epoch milliseconds. |
fixed_amount |
object | yes | Amount off by currency code, in major units: { "USD": 5, "EUR": 4.5 }. |
DiscountCode#
| Field | Type | Required | Description |
|---|---|---|---|
object |
discount_code |
yes | |
code |
string | yes | The code buyers type. Matched without regard to case. |
created_at |
integer | yes | When it was created. Epoch milliseconds. |
DiscountFixedAmountVariant#
A fixed amount discount (RevenueCat's shape).
| Field | Type | Required | Description |
|---|---|---|---|
object |
discount |
yes | |
id |
string | yes | Discount id (disc...). |
identifier |
string | yes | Your identifier for the discount, unique in the project. |
customer_facing_name |
string | yes | The name buyers see at checkout and Stripe puts on the coupon (first 40 characters). |
duration_mode |
one_time, time_window, forever |
yes | one_time: the first payment. time_window: every payment for time_window. forever: every payment. |
eligibility |
everyone, never_purchased, never_subscribed, never_subscribed_to_the_same_product |
yes | Who may use it, checked against the buyer's purchase history when the checkout has an app user id. |
time_window |
string or null | no | ISO 8601 months or years (P3M, P1Y), 1 to 36 months, for time_window. Otherwise null. |
disabled_at |
integer or null | no | When it was disabled. Epoch milliseconds, or null. |
type |
fixed_amount |
yes | |
fixed_amount |
object | yes | Amount off by currency code, in major units: { "USD": 5, "EUR": 4.5 }. |
created_at |
integer | yes | When it was created. Epoch milliseconds. |
updated_at |
integer | yes | When it last changed. Epoch milliseconds. |
DiscountPercentageVariant#
A percentage discount (RevenueCat's shape).
| Field | Type | Required | Description |
|---|---|---|---|
object |
discount |
yes | |
id |
string | yes | Discount id (disc...). |
identifier |
string | yes | Your identifier for the discount, unique in the project. |
customer_facing_name |
string | yes | The name buyers see at checkout and Stripe puts on the coupon (first 40 characters). |
duration_mode |
one_time, time_window, forever |
yes | one_time: the first payment. time_window: every payment for time_window. forever: every payment. |
eligibility |
everyone, never_purchased, never_subscribed, never_subscribed_to_the_same_product |
yes | Who may use it, checked against the buyer's purchase history when the checkout has an app user id. |
time_window |
string or null | no | ISO 8601 months or years (P3M, P1Y), 1 to 36 months, for time_window. Otherwise null. |
disabled_at |
integer or null | no | When it was disabled. Epoch milliseconds, or null. |
type |
percentage |
yes | |
percentage |
integer | yes | Percent off, 1 to 100. |
created_at |
integer | yes | When it was created. Epoch milliseconds. |
updated_at |
integer | yes | When it last changed. Epoch milliseconds. |
Entitlement#
| Field | Type | Required | Description |
|---|---|---|---|
object |
"entitlement" |
yes | |
id |
string | yes | Entitlement id (entl...). |
project_id |
string | yes | |
lookup_key |
string | yes | What apps check, for example pro. |
display_name |
string | yes | |
created_at |
integer | yes | Creation time. Epoch milliseconds. |
state |
active, inactive |
yes | |
products |
object | no | |
products.object |
"list" |
yes | |
products.items |
array of Product | yes | |
products.next_page |
string or null | yes | Path of the next page, or null on the last page. |
products.url |
string | yes | Path of this list. |
Experiment#
| Field | Type | Required | Description |
|---|---|---|---|
object |
"experiment" |
yes | |
id |
string | yes | Experiment id (prexp...). |
project_id |
string | yes | |
name |
string | yes | |
type |
introductory_offer, free_trial_offer, paywall_design, price_point, subscription_duration, subscription_ordering, other |
yes | What is tested. It sets the default metrics: see Experiments. |
status |
draft, running, paused, stopped |
yes | draft → running ⇄ paused → stopped. A stopped experiment cannot run again. |
primary_metric |
initial_conversion_rate, trial_conversion_rate, conversion_to_paying, refund_rate, realized_ltv_per_customer, realized_ltv_per_paying_customer, mrr_per_customer, mrr_per_paying_customer |
yes | The metric that decides the winner: a rate or a per-customer mean, because those have an interval. |
secondary_metrics |
array of initial_conversion_rate, initial_conversions, trials_started, trials_completed, trials_converted, trial_conversion_rate, paid_customers, conversion_to_paying, active_subscribers, churned_subscribers, refunded_customers, refund_rate, realized_ltv, realized_ltv_per_customer, realized_ltv_per_paying_customer, mrr, mrr_per_customer, mrr_per_paying_customer |
yes | Up to 12 more metrics shown first in the results. |
notes |
string | yes | The hypothesis, in Markdown (up to 20,000 characters). |
enrollment |
new, new_and_existing |
yes | new: only customers first seen at or after the experiment's first start. new_and_existing: anyone who asks for offerings while it runs. |
track_paywall_views |
boolean | yes | Results count paywall views (SDK paywall_impression and custom_paywall_impression events) and default to customers who saw a paywall. Turned on for every experiment made or changed to new_and_existing; A/B experiments converted from the first release keep false. |
audience_id |
string or null | yes | A saved audience (aud...). Null with audience_rules or for everyone. |
audience_rules |
object or null | yes | Conditions written for this experiment only, in the audience condition format. Null when it uses a saved audience or everyone. |
audience_rules.groups |
array of object | yes | Groups are OR-ed; conditions in a group are AND-ed. |
audience_rules.groups[].conditions |
array of object | yes | |
enrollment_percent |
integer | yes | Share of matching customers enrolled, 1 to 100. |
priority |
integer | yes | Enrollment order among the project's experiments, 1 first. New experiments go last; change it with the reorder action. |
variants |
array of ExperimentVariant | yes | The control first, then 1 to 3 treatments. Customers are split evenly. |
started_at |
integer or null | yes | First start. A resume keeps it, so new keeps counting from the first start. Epoch milliseconds, or null. |
paused_at |
integer or null | yes | Last pause; cleared on resume. Epoch milliseconds, or null. |
stopped_at |
integer or null | yes | Stopped. Epoch milliseconds, or null. |
created_at |
integer | yes | Created. Epoch milliseconds. |
updated_at |
integer or null | yes | Last change. Epoch milliseconds, or null. |
enrolled_customers |
integer | yes | Customers enrolled so far, sandbox included. |
ExperimentEnrollment#
| Field | Type | Required | Description |
|---|---|---|---|
object |
"experiment_enrollment" |
yes | |
id |
string | yes | Experiment id (prexp...). |
name |
string | yes | The experiment's name. |
variant |
a, b, c, d |
yes | The customer's variant: a is the control. |
ExperimentEstimate#
| Field | Type | Required | Description |
|---|---|---|---|
object |
"experiment_estimate" |
yes | |
period_days |
integer | yes | Always 7. |
matching_customers |
integer | yes | Customers first seen (new) or seen (new_and_existing) in the last 7 days who match the audience. |
enrolled_customers |
integer | yes | matching_customers × enrollment_percent, rounded. |
customers_per_variant |
integer | yes | enrolled_customers ÷ variant_count, rounded down. |
is_approximate |
boolean | yes | True when more than 5,000 customers were seen: the audience share of the 5,000 most recent is scaled to all of them. |
ExperimentMetric#
| Field | Type | Required | Description |
|---|---|---|---|
id |
initial_conversion_rate, initial_conversions, trials_started, trials_completed, trials_converted, trial_conversion_rate, paid_customers, conversion_to_paying, active_subscribers, churned_subscribers, refunded_customers, refund_rate, realized_ltv, realized_ltv_per_customer, realized_ltv_per_paying_customer, mrr, mrr_per_customer, mrr_per_paying_customer |
yes | |
display_name |
string | yes | |
kind |
rate, mean, count, total |
yes | rate: numerator ÷ denominator with a Wilson interval. mean: per-customer average with a normal interval. count and total: no interval. |
unit |
%, $, # |
yes | Rates are fractions (0.12 is 12%); money is USD. |
better |
higher, lower |
yes | Which direction wins. Churned subscribers, refunded customers and refund rate are better when lower. |
description |
string | yes |
ExperimentMetricValue#
Counts and totals carry only value. The control has no lift or chance_to_beat_control fields.
| Field | Type | Required | Description |
|---|---|---|---|
value |
number or null | yes | The metric now. Null for a rate or mean with no customers in its denominator. |
numerator |
integer | no | Rates: the customers or trials counted. |
denominator |
integer | no | Rates: the customers, trials or paid customers it divides by. Means: the customers averaged. |
lower |
number or null | no | Rates and means: the 95% interval's lower bound. |
upper |
number or null | no | Rates and means: the 95% interval's upper bound. |
lift |
number or null | no | Treatments, rates and means: value ÷ control value − 1 (0.12 is 12% above the control). Null when the control's value is 0. |
lift_lower |
number or null | no | The lift's 95% interval, by the delta method on the log of the ratio (Katz's interval for rates, with half counts added when a rate is 0% or 100%). Null for a mean with fewer than two customers on a side, or a mean of 0. |
lift_upper |
number or null | no | Upper bound of the lift's 95% interval. |
chance_to_beat_control |
number or null | no | Treatments, rates and means: the probability this variant beats the control in the metric's better direction. Null for a mean with fewer than two customers on a side. |
ExperimentResults#
| Field | Type | Required | Description |
|---|---|---|---|
object |
"experiment_results" |
yes | |
experiment_id |
string | yes | |
environment |
production, sandbox |
yes | |
currency |
"USD" |
yes | |
computed_at |
integer | yes | Computed. Epoch milliseconds. |
primary_metric |
initial_conversion_rate, trial_conversion_rate, conversion_to_paying, refund_rate, realized_ltv_per_customer, realized_ltv_per_paying_customer, mrr_per_customer, mrr_per_paying_customer |
yes | |
secondary_metrics |
array of initial_conversion_rate, initial_conversions, trials_started, trials_completed, trials_converted, trial_conversion_rate, paid_customers, conversion_to_paying, active_subscribers, churned_subscribers, refunded_customers, refund_rate, realized_ltv, realized_ltv_per_customer, realized_ltv_per_paying_customer, mrr, mrr_per_customer, mrr_per_paying_customer |
yes | |
control_variant_id |
string | yes | Always a. |
filters |
object | yes | |
filters.platform |
string or null | yes | The platform filter, or null. |
filters.country |
string or null | yes | The country filter, or null. |
filters.paywall |
all, viewed, not_viewed |
yes | The paywall filter in effect, after the default. |
filter_options |
object | yes | |
filter_options.platforms |
array of string | yes | Platforms of the enrolled customers' last requests. |
filter_options.countries |
array of string | yes | Countries of the enrolled customers' last requests. |
sample |
object or null | no | Null when every enrolled customer is counted. Above 25,000 enrolled customers the results come from a fixed random sample of 25,000 (the same customers on every request): rates, means, intervals and chances are estimates from it, and counts and totals are the sample's. |
sample.customers |
integer | yes | Customers in the sample: 25,000. |
sample.enrolled_customers |
integer | yes | Every enrolled customer in this environment. |
sample.enrolled_by_variant |
object | yes | Variant id → its enrolled customers. |
metrics |
array of ExperimentMetric | yes | Every metric's definition, in the order the dashboard lists them. |
variants |
object | yes | One entry per variant, the control first. |
variants.object |
"list" |
yes | |
variants.items |
array of ExperimentVariantResult | yes | |
variants.next_page |
string or null | yes | Path of the next page, or null on the last page. |
variants.url |
string | yes | Path of this list. |
guidance |
object | yes | |
guidance.enough_data |
boolean | yes | Every variant has at least min_customers customers and min_events events of the primary metric. |
guidance.min_customers |
integer | yes | 100. |
guidance.min_events |
integer | yes | 10 events per variant: conversions for initial conversion rate, paid customers for conversion to paying, completed trials for trial conversion rate, and paid customers for refund rate and the per-customer means. |
guidance.customers_needed_per_variant |
integer or null | yes | Customers per variant to detect a 20% relative lift on the primary metric at 95% confidence and 80% power, from the control's current value. Null until the control has data. |
guidance.leader |
object or null | yes | The treatment most likely to beat the control on the primary metric. |
guidance.leader.variant_id |
string | yes | |
guidance.leader.chance_to_beat_control |
number | yes | |
guidance.message |
string | yes | One sentence for people, such as "Too early to call ...". |
series |
object | yes | |
series.days |
array of integer | yes | Start of each UTC day from the first start to now (at most the last 400 days). Epoch milliseconds. |
series.values |
object | yes | values[metric_id][variant_id][i]: the metric as of the end of days[i], cumulative. |
chance_b_beats_a |
number | yes | Kept from the first release: variant b's chance to beat the control on initial_conversion_rate (0.5 when unknown). |
enough_data |
boolean | yes | Kept: guidance.enough_data. |
ExperimentVariant#
| Field | Type | Required | Description |
|---|---|---|---|
id |
a, b, c, d |
yes | Set by position: a is the control, then b, c, d. |
name |
string | yes | Defaults to Control, Treatment B, Treatment C, Treatment D. |
offering_id |
string | yes | The offering Offerings.current returns to customers in this variant (ofrng...). A deleted offering keeps its id here. |
placements |
object | yes | Placement id → offering id for customers in this variant; null shows no paywall there. It overlays the placements of the targeting rule that matches the customer. |
ExperimentVariantResult#
| Field | Type | Required | Description |
|---|---|---|---|
id |
a, b, c, d |
yes | |
name |
string | yes | |
offering_id |
string or null | yes | The variant's offering id. |
customers |
integer | yes | Enrolled customers who pass the filters (in the sample, when sample is set). Production results leave out customers who joined from a test device. |
paywall_viewers |
integer | yes | Of those, customers with a paywall view after joining. |
metrics |
object | yes | Every metric id → its value, interval, lift and chance to beat the control. |
conversions |
integer | yes | Kept from the first results release: initial_conversions. |
conversion_rate |
number | yes | Kept: initial_conversion_rate, 4 decimals (0 when there are no customers). |
trials |
integer | yes | Kept: trials_started. |
paying_customers |
integer | yes | Kept: paid_customers. |
revenue |
number | yes | Kept: realized_ltv, USD. |
revenue_per_customer |
number | yes | Kept: realized_ltv_per_customer, USD (0 when there are no customers). |
IndicativePrice#
| Field | Type | Required | Description |
|---|---|---|---|
object |
"indicative_price" |
yes | |
currency |
string | yes | ISO 4217 code. |
country |
string or null | yes | US for a United States store price, the region code for another Google Play region, else null (Test Store and Stripe prices, other App Store territories). |
amount_micros |
integer | yes | Price in micros: 9.99 is 9990000. |
MonetaryAmount#
| Field | Type | Required | Description |
|---|---|---|---|
currency |
string | yes | ISO 4217 currency code. |
gross |
number | yes | Gross amount. |
commission |
number | yes | Estimated store commission. |
tax |
number | yes | Tax. Always 0 today. |
proceeds |
number | yes | Gross minus commission. |
Offering#
| Field | Type | Required | Description |
|---|---|---|---|
object |
"offering" |
yes | |
id |
string | yes | Offering id (ofrng...). |
lookup_key |
string | yes | |
display_name |
string | yes | |
is_current |
boolean | yes | Exactly one offering per project is current. |
created_at |
integer | yes | Creation time. Epoch milliseconds. |
project_id |
string | yes | |
state |
active, inactive |
yes | |
paywall_id |
string or null | no | The paywall attached to this offering, or null. |
metadata |
object or null | yes | |
packages |
object | no | |
packages.object |
"list" |
yes | |
packages.items |
array of Package | yes | |
packages.next_page |
string or null | yes | Path of the next page, or null on the last page. |
packages.url |
string | yes | Path of this list. |
OverviewMetrics#
| Field | Type | Required | Description |
|---|---|---|---|
object |
"overview_metrics" |
yes | |
currency |
"USD" |
yes | |
metrics |
array of object | yes | |
metrics[].object |
"overview_metric" |
no | |
metrics[].id |
active_trials, active_subscriptions, mrr, revenue, new_customers, active_users |
no | |
metrics[].name |
string | no | |
metrics[].description |
string | no | |
metrics[].unit |
#, $ |
no | |
metrics[].period |
P0D, P28D |
no | |
metrics[].value |
number | no | |
metrics[].last_updated_at |
integer | no | Computed at. Epoch milliseconds. |
metrics[].last_updated_at_iso8601 |
string | no |
Package#
| Field | Type | Required | Description |
|---|---|---|---|
object |
"package" |
yes | |
id |
string | yes | Package id (pkge...). |
lookup_key |
string | yes | For example $rc_monthly. |
display_name |
string | yes | |
position |
integer | yes | Order in the offering, lowest first. |
created_at |
integer | yes | Creation time. Epoch milliseconds. |
products |
object | no | |
products.object |
"list" |
yes | |
products.items |
array of PackageProduct | yes | |
products.next_page |
string or null | yes | Path of the next page, or null on the last page. |
products.url |
string | yes | Path of this list. |
PackageProduct#
| Field | Type | Required | Description |
|---|---|---|---|
product |
Product | yes | |
eligibility_criteria |
all, google_sdk_lt_6, google_sdk_ge_6 |
yes |
Product#
| Field | Type | Required | Description |
|---|---|---|---|
object |
"product" |
yes | |
id |
string | yes | Product id (prod...). |
store_identifier |
string | yes | The store's product id. Google Play subscriptions use subscriptionId:basePlanId; an App Store monthly billing plan (iOS 26.4) uses productId:monthly. |
type |
subscription, one_time, consumable, non_consumable, non_renewing_subscription |
yes | |
state |
active, inactive |
yes | |
subscription |
object | no | |
subscription.duration |
string or null | no | ISO 8601 period such as P1M. |
subscription.grace_period_duration |
null | no | |
subscription.trial_duration |
null | no | |
one_time |
object | no | |
one_time.is_consumable |
boolean or null | no | |
created_at |
integer | yes | Creation time. Epoch milliseconds. |
app_id |
string | yes | |
display_name |
string or null | yes | |
app |
App | no | Only the object for the app's own type is present. Store secrets are never returned. |
indicative_price |
IndicativePrice or null | no | With expand=indicative_price: the Test Store price; else the App Store or Google Play price in the United States from the last store price read (or the in-app purchase's base territory, or the first territory with a price); else the Stripe web product's price. Null when none is known. |
store_details |
StoreDetails or null | no | RevenueDot extension, with expand=store_details: the store's status and price from the last store price read. Null when the product was never read from App Store Connect or Google Play (other stores, or no read yet). |
ProductPrice#
| Field | Type | Required | Description |
|---|---|---|---|
id |
string or null | yes | Price id (prc...); null for a price saved before prices by currency existed, until it is changed. The Stripe price id for a web product. |
currency |
string | yes | ISO 4217 code. |
amount_micros |
integer | yes | Price in micros: 9.99 is 9990000. |
Project#
| Field | Type | Required | Description |
|---|---|---|---|
object |
"project" |
yes | |
id |
string | yes | Project id (proj...). |
name |
string | yes | |
created_at |
integer | yes | Creation time. Epoch milliseconds. |
icon_url |
string or null | no | Always null. |
icon_url_large |
string or null | no | Always null. |
PublicApiKey#
| Field | Type | Required | Description |
|---|---|---|---|
object |
"public_api_key" |
yes | |
id |
string | yes | |
key |
string | yes | The key the SDK sends (appl_, goog_, test_ ...). |
environment |
production, sandbox |
yes | |
app_id |
string | yes | |
created_at |
integer | yes | Creation time. Epoch milliseconds. |
Purchase#
| Field | Type | Required | Description |
|---|---|---|---|
object |
"purchase" |
yes | |
id |
string | yes | |
customer_id |
string | yes | |
original_customer_id |
string | no | |
product_id |
string | yes | |
purchased_at |
integer | yes | Purchase time. Epoch milliseconds. |
revenue_in_usd |
MonetaryAmount | no | |
quantity |
integer | no | |
status |
owned, refunded |
yes | |
presented_offering_id |
string or null | no | Offering the purchase was made from (its id, or the identifier the SDK sent when no such offering exists). |
entitlements |
object | no | |
entitlements.object |
"list" |
yes | |
entitlements.items |
array of Entitlement | yes | |
entitlements.next_page |
string or null | yes | Path of the next page, or null on the last page. |
entitlements.url |
string | yes | Path of this list. |
environment |
production, sandbox |
yes | |
store |
string | yes | |
store_purchase_identifier |
string | no | |
ownership |
purchased |
no | |
country |
string | no |
StoreDetails#
| Field | Type | Required | Description |
|---|---|---|---|
object |
"store_details" |
yes | |
status |
string or null | yes | The store's state in lower case: approved, ready_to_submit, waiting_for_review, in_review, rejected, developer_action_needed, missing_metadata, removed_from_sale (App Store), active, draft, inactive (Google Play). |
store_state |
string or null | yes | The state as the store spells it, such as APPROVED or ACTIVE. |
price |
object or null | yes | The United States price when the product has one, else the in-app purchase's base territory or the first territory with a price. Null when the store has no price for it. |
territories |
integer | yes | How many territories have a price. |
duration |
string or null | yes | The store's period for a subscription (P1M ...). |
display_name |
string or null | yes | The name in the store. |
editable |
boolean | yes | Whether the product editor can change its prices. |
refreshed_at |
integer | yes | When the prices were read from the store. Epoch milliseconds. |
refresh_status |
ok, failing, null |
yes | The app's last read: failing means it failed and these values are from the read before. |
StoreProductImport#
| Field | Type | Required | Description |
|---|---|---|---|
object |
"store_product_import" |
yes | |
app_id |
string | yes | |
created |
array of Product | yes | Products created by this import. |
existing |
array of Product | yes | Requested products the catalog already had; left unchanged. |
failed |
array of object | yes | Requested identifiers the store does not have, or that cannot be imported. |
failed[].store_identifier |
string | yes | |
failed[].reason |
not_in_store, not_importable |
yes | |
failed[].message |
string | yes | |
entitlement_ids |
array of string | yes | The entitlements every created and existing product is attached to. |
Subscription#
| Field | Type | Required | Description |
|---|---|---|---|
object |
"subscription" |
yes | |
id |
string | yes | Subscription id (sub_...). |
customer_id |
string | yes | |
original_customer_id |
string | no | |
product_id |
string or null | no | Product id (prod...), null for promotional grants. |
starts_at |
integer | yes | Start of the subscription. Epoch milliseconds. |
current_period_starts_at |
integer | no | Start of the current period. Epoch milliseconds. |
current_period_ends_at |
integer or null | no | End of the current period. Epoch milliseconds, or null. |
ends_at |
integer or null | no | End of access. Epoch milliseconds, or null. |
gives_access |
boolean | yes | |
pending_payment |
boolean | no | |
auto_renewal_status |
will_renew, will_not_renew, will_change_product, will_pause |
yes | |
status |
trialing, active, in_grace_period, in_billing_retry, paused, expired |
yes | |
total_revenue_in_usd |
MonetaryAmount | no | |
presented_offering_id |
string or null | no | Offering the purchase was made from (its id, or the identifier the SDK sent when no such offering exists). |
entitlements |
object | no | |
entitlements.object |
"list" |
yes | |
entitlements.items |
array of Entitlement | yes | |
entitlements.next_page |
string or null | yes | Path of the next page, or null on the last page. |
entitlements.url |
string | yes | Path of this list. |
environment |
production, sandbox |
yes | |
store |
string | yes | |
store_subscription_identifier |
string | no | Latest store transaction id, order id or token. |
ownership |
purchased, family_shared |
no | |
country |
string | no | ISO 3166-1 alpha-2, when known. |
management_url |
null | no |
SubscriptionTransaction#
| Field | Type | Required | Description |
|---|---|---|---|
object |
"subscription_transaction" |
yes | |
id |
string | yes | |
purchased_at |
integer | yes | Purchase time. Epoch milliseconds. |
product_store_identifier |
string | no | |
revenue_in_local_currency |
MonetaryAmount or null | no | |
revenue_in_usd |
MonetaryAmount | no | |
expiration_date |
integer or null | no | End of the period. Epoch milliseconds, or null. |
effective_expiration_date |
integer or null | no | When access actually ended (the refund time for a refunded period). Epoch milliseconds, or null. |
V2Error#
| Field | Type | Required | Description |
|---|---|---|---|
object |
"error" |
yes | |
type |
parameter_error, resource_already_exists, resource_missing, idempotency_error, rate_limit_error, authentication_error, authorization_error, store_error, server_error, resource_locked_error, unprocessable_entity_error, invalid_request, entity_references_archived_entities, plan_required |
yes | |
message |
string | yes | What went wrong. |
param |
string | no | The request field at fault, when there is one. |
doc_url |
string | yes | Link to the error's section of the errors page. |
retryable |
boolean | yes | True when retrying the same request can succeed. |
upgrade_url |
string | no | Only on plan_required: the Billing page where the owner starts Pro. |
WebhookIntegration#
| Field | Type | Required | Description |
|---|---|---|---|
object |
"webhook_integration" |
yes | |
id |
string | yes | Webhook id (wh_...). |
project_id |
string | yes | |
name |
string | yes | |
url |
string | yes | |
environment |
production, sandbox, null |
yes | Null sends both. |
event_types |
array of string | yes | Lower-case event types. Empty sends every type. |
app_id |
string or null | yes | Only events of this app, or null for all. |
created_at |
integer | yes | Creation time. Epoch milliseconds. |
signing_secret |
string | no | whsec_... Only in the answer that creates the webhook. |