# Calculate price
Source: https://docs.corrily.com/api-reference/calculate-price
POST https://client.corrily.com/v1/prices
Calculate a localized or optimized price for your service or product based on the customer's location and other characteristics.
## Usage
The first time that you call this endpoint for a given IP address and user ID, Corrily calculates the price. On subsequent calls (with the same IP address and user ID), Corrily returns the original calculated price.
See [Calculate price API examples](../pricing/calculate-price-api-examples) for examples of how this endpoint is commonly used.
### Price localization
If you're only using Corrily for price localization (i.e. you are not doing price optimization), this is the endpoint that you call in order to get localized prices.
### Price optimization
If you're also doing price optimization, this is the endpoint you call to get optimized prices.
As explained in [Price optimization overview](../pricing/price-optimization-overview), Corrily either returns an experimental price or a control price which will be used to benchmark the experimental prices.
Experimental prices are affected by the following factors:
* The (potential) customer's location, which is inferred from their IP address
* Any [user features](../users/02_user-features) that you have provided
* The outcomes of previous price experiments
* The upper and lower bounds that you defined in the Corrily Dashboard
## Body Params
A [User ID](../users/01_users). Only required if no `ip` is provided and
`get_ip_from_request` is `false`.
The product IDs that you want to calculate prices for. In general, you
probably want to include the product IDs for all the product offerings that
you intend to show your visitor. The response from Corrily will contain the
display price for each product. For example, if you provide `["monthly",
"annual"]`, Corrily returns price data for your `monthly` and `annual`
products. Product IDs are created by Corrily when you set up your Corrily
account. Email [support@corrily.com](mailto:support@corrily.com) if you don't know yours.
If packaging segmentation is enabled, package id can be passed to get the products for a package. That way
the products shown themselves will be based on the package segmentation.
When package segmentation is enabled, the products parameter is optional.
If there is no pacakge segmentation defined for given country, audience, and no package\_id, products
are specified in request, an error will be returned.
An IPv4 or IPv6 address. Corrily uses this as the user ID if no user\_id is
provided. ip is only required if no user\_id is provided and
`get_ip_from_request` is false. Examples: `187.106.3.73` (IPv4),
`2804:14d:ac91:8286:efcb:851f:b59f:fffc` (IPv6).
If `true`, Corrily sources the user's IP from this request.
`get_ip_from_request` therefore only works correctly when the Corrily API
request is made from a client device.
A two-letter (Alpha-2) [ISO 3166 country
code](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes#Current_ISO_3166_country_codes).
Example: `US`
An object containing key-value pairs of arbitrary strings that represent features of this user which may affect their optimal price. See [User features](../users/02_user-features). Example: `{"platform": "ios"}`
The payment gateways to return integration objects for. Accepted values:
`stripe`, `paypal`, `chargebee`
A currency you wish the price to be tied to. Currencies do not need to match
the passed country. For example, it is possible to return a price in EUR for
someone based in the Unites Sates. Note that the country-adjusted price will
be returned, regardless of the currency set.
With this flag you can run a complete recomputation of what should be shown for a user: country, currency, experiment and arm.
For example, if a user was attached to country A and now you want to attach him to country B, and recompute products and prices for him,
then you can use this flag. After recomputation, all previously saved for this user data will be overrided.
Will override the currency of any previously saved price and instead return
the price in the currency provided. If set to `true`, a `currency` param must
be passed. Unlike `override` the price amount will *not* be recomputed.
If set to false, will make sure no experiment is run.
Optional product id to coupon id mapping to explicitly specify the coupon to be applied in the pricing. Coupons feature needs to be enabled for this. example `{"product_id_1": "coupon_id_1"}`. Multiple coupons can be applied by a comma-delimited string, for example: `{"product_id_1": "coupon_id_1,coupon_id2"}`
## Response
Whether or not the request completed successfully.
The user ID that you provided when you called calculate price, or the user's
IP address (if you did not provide a user ID).
The country that the price data is associated to. If you did not specify a
country when calling calculate price, the `country` value provided here in the
response is inferred from the user's IP address.
The currency that the price data is associated to. Tip: check if `products. {id}.display.price` meets your needs before manually constructing a display price
with this field.
The currency symbol that the price data is associated to. Tip: check if
`products.{id}.display.price` meets your needs before manually constructing a
display price with this field.
The package ID that was used to get the products.
The price data for each of the products that you specified when you called calculate price. The placeholder `{id}` below represents your product ID(s). See [Setup](../basics/setup) to learn how to create product IDs.
A float version of your price, in case you want to do some custom formatting of the price. Tip: check if `products.{id}.display.price` meets your needs before manually constructing a display price with this field.
The current value of `products.{id}.price` in United States dollars.
The billing interval of this product. Possible values: `day`, `week`, `month`, `year`, `one_time`
The price data, formatted in various ways for display to your user.
The price data, formatted in a way that your user will quickly understand. This is usually a combination of `currency`, `currency_symbol`, and `products.{id}.price`. `products.{id}.price` will use commas or periods as separators if that is the local custom. Example: `JP¥2,200`
The default price for the product. You can set this default price in your Corrily Dashboard.
If `products.{id}.price` is an experimental price, this field contains the baseline price striked out, followed by the experimental price. You may want to present this to your user to help them see that they would be paying less than normal customers. Example: $̶3̶4̶ $19
If `products.{id}.price` is an experimental price, this field contains the percentage discount of the experimental price compared to the baseline price. For example, if the baseline price is 34 and the experimental price is 19, the discount is about 44%: 1 - (19 ÷ 34)) × 100
The trial data for the product if available
The corrily trial id for the product
Number of days for the trial
Type of the trial. Possible values: `PAID`, `FREE`
The price data, formatted for payment gateway integration.
The currency that `products.{id}.integrations.{gateway}.amount` is priced in. If this currency is not the same as the top-level `currency` value in this response (the "local currency"), it means that the payment gateway does not accept the local currency.
The amount that you should pass to the payment gateway when creating the
subscription or charge. Many payment gateways use [smallest-unit currency
format](../payment-gateways/integration-guide#map-smallest-unit-currency-format-to-decimal-format)
in which case this value will be an integer.
As mentioned in `products.{id}.integrations.{gateway}.currency`, sometimes a
payment gateway doesn't support your user's local currency. If that's the
case, then `products.{id}.integrations. {gateway}.formatted` provides the equivalent price in a currency that the payment
gateway does support. This field is similar to `products.{id}.display.price` in
that it will be formatted in a way that your user will quickly understand.
Optionally include coupon details if the given request matches any segment for
which an active campaign is running
coupon id
coupon corrily api\_id
indicates if a coupon is percentage discount coupon or absolute amount
discount coupon
The value or percentage or absolute amount of discount for the coupon
Array of strings. Indicates sources of the coupon if the coupon maps to external
sources. For example: "coupon\_source": \["STRIPE", "CHARGEBEE"]
External Coupon corrily id
Coupon id from the external source if that coupon maps to an external
source
Indicates currency code if it is configured for the coupon
Datetime in YYYY-MM-DD HH:MM:SS format. Indicates the time the coupon is
valid for in UTC.
The price data after coupon application, formatted in various ways for display to your user.
The price data in a useful format after coupon application.
This value is formatted in the same way as `products.{id}.price`.
Uses `coupons[i].display.price_numeric` as a price source.
Numeric price after coupon application
The default price for the product. You can set this default price in your Corrily Dashboard.
`reference_interval_price` in numeric format
If `products.{id}.price` is an experimental price, this field contains the baseline price striked out, followed by the experimental price. You may want to present this to your user to help them see that they would be paying less than normal customers. Example: $̶3̶4̶ $19
If coupon is in percents, then here will be coupon value in percents.
If coupon is absolue value, then this field will contain relation in percents between base price and price after coupon is applied.
Price after coupon application
Price experimentation metadata. If you are not doing price experimentation,
you can ignore this object.
Whether or not this response contains experimental prices.
The ID of this experiment. An id of `0` means no experiment was run.
One of `price`, `packaging` or `null`.
In the case of a `packaging` experiment, the product API IDs to pick from
the `product` object of the response to show to users.
The experiment arm identifier, equal to `baseline` if experiment is run
but baseline arm was selected.
The first audience user matches.
Audience name
Audience API ID
All audiences user matches, sorted by name.
Audience name
Audience API ID
```json Example theme={null}
{
"success": true,
"user_id": "193.37.32.232",
"country": "SG",
"currency": "SGD",
"currency_symbol": "S$",
"products": {
"monthly": {
"price": 27,
"price_usd": 20.059360849328197,
"interval": "month",
"interval_count": 1,
"display": {
"price": "S$ 27",
"reference_interval_price": "S$ 27",
"price_with_baseline_strike": "S$ 27",
"discount_to_baseline": 0
},
"integrations": {
"stripe": {
"currency": "SGD",
"amount": 2700,
"formatted": "S$ 27"
}
}
},
"annual": {
"price": 144,
"price_usd": 106.98325786308372,
"interval": "year",
"interval_count": 1,
"display": {
"price": "S$ 144",
"reference_interval_price": "S$ 12",
"price_with_baseline_strike": "S$ 12",
"discount_to_baseline": 0
},
"integrations": {
"stripe": {
"currency": "SGD",
"amount": 14400,
"formatted": "S$ 144"
}
},
"coupon": {
"id": "coupon_id_392",
"type": "percentage",
"value": 10,
"coupon_source": "chargebee",
"external_coupon_id": "chargebee_coupon_392",
"currency_code": "USD",
"valid_till": "2022-02-07 15:49:49",
"display": {
"local_currency_symbol": "€",
"interval_local_price_formatted": "9 €",
"local_price_formatted": "9 €",
"usd_price_formatted": "$11",
"local_price_formatted_with_baseline_strike": "9 €",
"pct_discount_to_baseline": 0.0
}
}
}
},
"variants": {
"experimented_on": false,
"experiment_id": 0,
"treatment_group": null,
"experiment_type": null,
"arm_id": 0,
"products_to_select": []
},
"audience": {
"name": "First Audience",
"api_id": "audience_1"
},
"matching_audiences": [
{
"name": "First Audience",
"api_id": "audience_1"
},
{
"name": "Second Audience",
"api_id": "audience_2"
}
],
}
```
```bash Bash theme={null}
curl --request POST \
--url https://client.corrily.com/v1/prices \
--header 'Content-Type: application/json' \
--data '
{
"user_id": "string",
"products": [
"string"
],
"ip": "string",
"get_ip_from_request": true,
"country": "string",
"integrations": [
"string"
],
"currency": "string",
"override": true,
"run_experiment": true
}
'
```
```js Node theme={null}
const fetch = require("node-fetch");
const url = "https://client.corrily.com/v1/prices";
const options = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
user_id: "string",
products: ["string"],
ip: "string",
get_ip_from_request: true,
country: "string",
integrations: ["string"],
currency: "string",
override: true,
run_experiment: true,
}),
};
fetch(url, options)
.then((res) => res.json())
.then((json) => console.log(json))
.catch((err) => console.error("error:" + err));
```
```python Python theme={null}
import requests
url = "https://client.corrily.com/v1/prices"
payload = {
"user_id": "string",
"products": ["string"],
"ip": "string",
"get_ip_from_request": True,
"country": "string",
"integrations": ["string"],
"currency": "string",
"override": True,
"run_experiment": True
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)
```
# Create charge
Source: https://docs.corrily.com/api-reference/create-charge
POST https://client.corrily.com/v1/charges
Notify Corrily that one of your customers has initiated a one-time transaction.
```bash Bash theme={null}
curl --request POST \
--url https://client.corrily.com/v1/charges \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '
{
"amount": 50,
"amount_refunded": 0,
"created": 1662444537,
"currency": "USD",
"country": "US",
"origin": "stripe",
"origin_id": "sub_JaHy9Kl6ZMLvId",
"product": "monthly",
"status": "succeeded",
"user_id": "test_user"
}
'
```
Custom payment gateway integrations only
You do not need to use this endpoint if you have [granted Corrily automated
access](../payment-gateways/integration-guide#recommended-provide-automated-access-of-subscription-and-charge-events)
to your payment gateway's subscription and charge events.
## Body Params
The amount (price) of the charge.
The amount refunded by the charge. Only required if amount is undefined.
A 10-digit (seconds) [UNIX timestamp](https://en.wikipedia.org/wiki/Unix_time)
indicating when the charge was created. This timestamp should represent when
the charge was initialized, regardless of the status of the charge during the
initialization. It's rare to need to update `created` after initially setting
it.
The three-letter [(ISO
4217\)](https://en.wikipedia.org/wiki/ISO_4217#Active_codes) currency code.
User's two-letter ISO3166 country code.
The payment gateway that handled the charge. Accepted values: `stripe`,
`paypal`, `chargebee`.
A unique identifier for the charge. This can be the ID that the payment
gateway assigned to the charge.
A product ID string or a payment gateway integration object. Product IDs are
created by Corrily when you set up your Corrily account. Email
[support@corrily.com](mailto:support@corrily.com) if you don't know them. See the response of the [calculate
price](../calculate-price) endpoint for an example of the structure of
integration objects.
The status of the charge. Accepted values: `pending`, `succeeded`, `failed`.
A [User ID](../users/user-ids).
Free form key-value data. Metadata is useful for storing additional, structured information for a charge.
# Create Product
Source: https://docs.corrily.com/api-reference/create-product
POST https://client.corrily.com/v1/products
Create a Corrily product using an API instead of our dashboard.
```bash Bash theme={null}
curl --location -g --request POST 'https://client.corrily.com/v1/products' \
--header 'Content-Type: application/json' \
--data-raw '{
"api_id": "monthly",
"name": "Monthly Product",
"base_px": 10,
"interval": "month",
"interval_count": 1,
"country_prices": [
{
"country": "ES",
"amount": 50,
"currency": "EUR"
}
],
"integrations": [
{
"type": "stripe",
"stripe_product_id": "my_stripe_pid"
}
]
}'
```
```json Example theme={null}
{
"message": "Successfully created product.",
"success": true,
"api_id": "monthly",
"name": "Monthly Product",
"base_px": 10.0,
"baseline_id": null,
"country_prices": [
{
"country": "ES",
"local_price": 50.0,
"currency": "EUR"
}
],
"integrations": [
{
"type": "stripe",
"stripe_product_id": "my_stripe_pid"
}
]
}
```
## Body Params
The product identifier you will be calling this product with using [Corrily's
price endpoint](../api-reference/calculate-price).
The name of the product. Must be unique.
The base price of the product in `USD`.
A string value denoting how often to bill for a subscription. Possible values:
`day`, `week`, `month`, `year`, `one_time`.
An integer denoting how many intervals to bill for the. A monthly product
would have `month` as interval and `1` as interval counts, etc. One time
products should have an interval count of `1`.
A list of one ore more of the below country price object.
Two-letter ISO3166 country code.
The price of the product in this country.
The three-letter ISO4217 currency code of the price in this country.
A list of one or more of the below integration price objects. Not required in the case of a custom integration using the
[Subscription](../api-reference/create-subscription) and
[Charge](../api-reference/create-subscription) endpoints where Corrily product IDs are passed to alert us of
payment gateway revenue activity.
Should be set to equal `stripe`.
The matching [Stripe product ID](https://stripe.com/docs/api/products/object#product_object-id).
Should be set to equal `stripe_with_interval`.
The matching [Stripe product
ID](https://stripe.com/docs/api/products/object#product_object-id).
The frequency at which a subscription is billed. One of `day`, `week`,
`month` or `year`. Sometimes Stripe products actually encompass more than
what Corrily would consider a product. For example, the Stripe product for a
company's "Pro Plan" might have two [Price
objects](https://stripe.com/docs/api/prices/object) one for the monthly
version of the plan, the other for its annual version. Meanwhile, Corrily
would consider these to be two distinct products `pro_monthly` and
`pro_annual`. To capture such differences you can pass a
`stripe_with_interval` integration object which can help separate such
cases.
The number of intervals (specified in the interval attribute) between
subscription billings. For example, interval=month and interval\_count=3
bills every 3 months.
Should be set to equal `chargebee`.
The matching [Chargebee plan
ID](https://apidocs.chargebee.com/docs/api/plans?prod_cat_ver=1#plan_id).
Should be set to equal `recurly`.
The matching [Recurly plan
ID](https://developers.recurly.com/api/v2021-02-25/index.html#operation/create_plan).
Currency value
# Create subscription
Source: https://docs.corrily.com/api-reference/create-subscription
POST https://client.corrily.com/v1/subscriptions
Notify Corrily that one of your customers has started a subscription.
Custom payment gateway integrations only
You do not need to use this
endpoint if you have [granted Corrily automated
access](../payment-gateways/integration-guide#recommended-provide-automated-access-of-subscription-and-charge-events)
to your payment gateway's subscription and charge events.
## Body Params
The amount (price) of the subscription.
Users's two-letter ISO3166 country code.
A 10-digit (seconds) [UNIX timestamp](https://en.wikipedia.org/wiki/Unix_time)
indicating when the subscription was created. This timestamp should represent
when the subscription was initialized, regardless of the status of the
subscription during the initialization. It's rare to need to update created
after initially setting it.
The three-letter
([ISO4217](https://en.wikipedia.org/wiki/ISO_4217#Active_codes)) currency
code.
The payment gateway that handled the subscription. Accepted values: `stripe`,
`paypal`, `chargebee`
A unique identifier for the subscription. This can be the ID that the payment
gateway assigned to the subscription.
A product ID string or a payment gateway integration object. Product IDs are
created by Corrily when you set up your Corrily account. Email
[support@corrily.com](mailto:support@corrily.com) if you don't know them. See the response of the [calculate
price](../calculate-price) endpoint for an example of the structure of
integration objects.
The status of the subscription. Accepted values: `pending`, `trialing`,
`active`, `canceled`
A User ID.
A 10-digit (seconds) [UNIX timestamp](https://en.wikipedia.org/wiki/Unix_time)
indicating when the subscription will be canceled. In other words, the
customer will no longer have access to your service after this time. This
parameter should only be provided when `status` is set to `canceled`.
A 10-digit (seconds) unix timestamp indicating when customer expressed their
intent to cancel. This parameter should only be provided when `status` is set
to `canceled`.
A 10-digit (seconds) [UNIX timestamp](https://en.wikipedia.org/wiki/Unix_time)
indicating when the trial subscription ends. This parameter should only be
provided when `status` is set to `trialing`.
A 10-digit (seconds) [UNIX timestamp](https://en.wikipedia.org/wiki/Unix_time)
indicating when the trial subscription begins. This parameter should only be
provided when `status` is set to `trialing`.
```json Example theme={null}
{
"message": "[dev=False] subscription record 97#user123#stripe#origin789 was written.",
"success": true,
"record": {
"origin": "stripe",
"type": "subscription",
"latest_event_timestamp": 1628549872,
"latest_event_id": "corrily_evt_1628549872",
"corrily_product_id": 122,
"org_id": 97,
"origin_id": "origin789",
"user_id": "user123",
"created": 1628548983,
"interval": "month",
"interval_count": 1,
"status": "active",
"cancel_at": null,
"canceled_at": null,
"trial_start": null,
"trial_end": null,
"currency": null,
"amount": null,
"amount_usd": null,
"amount_usd_monthly": null
}
}
```
```bash Bash theme={null}
curl --request POST \
--url https://client.corrily.com/v1/subscriptions \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '
{
"amount": 0,
"country": "string",
"created": 0,
"currency": "string",
"origin": "string",
"origin_id": "string",
"product": "Your product api_id",
"status": "string",
"user_id": "string",
"cancel_at": 0,
"canceled_at": 0,
"trial_end": 0,
"trial_start": 0
}
'
```
```js Node theme={null}
const fetch = require("node-fetch");
const url = "https://client.corrily.com/v1/subscriptions";
const options = {
method: "POST",
headers: { Accept: "application/json", "Content-Type": "application/json" },
body: JSON.stringify({
amount: 0,
country: "string",
created: 0,
currency: "string",
origin: "string",
origin_id: "string",
product: "Your product api_id",
status: "string",
user_id: "string",
cancel_at: 0,
canceled_at: 0,
trial_end: 0,
trial_start: 0,
}),
};
fetch(url, options)
.then((res) => res.json())
.then((json) => console.log(json))
.catch((err) => console.error("error:" + err));
```
```python Python theme={null}
import requests
url = "https://client.corrily.com/v1/subscriptions"
payload = {
"amount": 0,
"country": "string",
"created": 0,
"currency": "string",
"origin": "string",
"origin_id": "string",
"product": "Your product api_id",
"status": "string",
"user_id": "string",
"cancel_at": 0,
"canceled_at": 0,
"trial_end": 0,
"trial_start": 0
}
headers = {
"Accept": "application/json",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)
```
# Delete charge
Source: https://docs.corrily.com/api-reference/delete-charge
DELETE https://client.corrily.com/v1/charges/{user_id}/{origin}/{origin_id}
```bash Bash theme={null}
curl --request DELETE \
--url https://client.corrily.com/v1/charges/user_id/origin/origin_id \
--header 'Accept: application/json'
```
## Path Params
The payment gateway that handled the charge. Accepted values: `stripe`,
`paypal`, `chargebee`.
A unique identifier for the charge. This can be the ID that the payment
gateway assigned to the charge.
The [User ID](../users/user-id) that you provided when you [created the
charge](./create-charge).
# Delete Product
Source: https://docs.corrily.com/api-reference/delete-product
DELETE https://client.corrily.com/v1/products/{product_api_id}
Delete an existing Corrily product.
```bash Bash theme={null}
curl --location --request DELETE 'https://client.corrily.com/v1/products/monthly'
```
```json Example theme={null}
{
"message": "Successfully deleted product my_new_product21",
"success": true
}
```
## Path Params
The api ID of the product.
# Delete subscription
Source: https://docs.corrily.com/api-reference/delete-subscription
DELETE https://client.corrily.com/v1/subscriptions/{user_id}/{origin}/{origin_id}
Delete Corrily's record of a subscription.
Custom payment gateway integrations only
You do not need to use this endpoint if you have [granted Corrily automated access](../payment-gateways/integration-guide#recommended-provide-automated-access-of-subscription-and-charge-events) to your payment gateway's subscription and charge events.
## Body Params
The amount (price) of the subscription.
Users's two-letter ISO3166 country code.
A 10-digit (seconds) [UNIX timestamp](https://en.wikipedia.org/wiki/Unix_time)
indicating when the subscription was created. This timestamp should represent
when the subscription was initialized, regardless of the status of the
subscription during the initialization. It's rare to need to update created
after initially setting it.
The three-letter ([ISO
4217](https://en.wikipedia.org/wiki/ISO_4217#Active_codes)) currency code.
The payment gateway that handled the subscription. Accepted values: `stripe`,
`paypal`, `chargebee`
A unique identifier for the subscription. This can be the ID that the payment
gateway assigned to the subscription.
A product ID string or a payment gateway integration object. Product IDs are
created by Corrily when you set up your Corrily account. Email
[support@corrily.com](mailto:support@corrily.com) if you don't know them. See the response of the [calculate
price](../calculate-price) endpoint for an example of the structure of
integration objects.
The status of the subscription. Accepted values: `pending`, `trialing`,
`active`, `canceled`
A User ID.
A 10-digit (seconds) [UNIX timestamp](https://en.wikipedia.org/wiki/Unix_time)
indicating when the subscription will be canceled. In other words, the
customer will no longer have access to your service after this time. This
parameter should only be provided when `status` is set to `canceled`.
A 10-digit (seconds) unix timestamp indicating when customer expressed their
intent to cancel. This parameter should only be provided when `status` is set
to `canceled`.
A 10-digit (seconds) [UNIX timestamp](https://en.wikipedia.org/wiki/Unix_time)
indicating when the trial subscription ends. This parameter should only be
provided when `status` is set to `trialing`.
A 10-digit (seconds) [UNIX timestamp](https://en.wikipedia.org/wiki/Unix_time)
indicating when the trial subscription begins. This parameter should only be
provided when `status` is set to `trialing`.
```json Example theme={null}
{
"message": "[dev=False] subscription record 97#user123#stripe#origin789 was written.",
"success": true,
"record": {
"origin": "stripe",
"type": "subscription",
"latest_event_timestamp": 1628549872,
"latest_event_id": "corrily_evt_1628549872",
"corrily_product_id": 122,
"org_id": 97,
"origin_id": "origin789",
"user_id": "user123",
"created": 1628548983,
"interval": "month",
"interval_count": 1,
"status": "active",
"cancel_at": null,
"canceled_at": null,
"trial_start": null,
"trial_end": null,
"currency": null,
"amount": null,
"amount_usd": null,
"amount_usd_monthly": null
}
}
```
```bash Bash theme={null}
curl --request DELETE \
--url https://client.corrily.com/v1/subscriptions/user_id/origin/origin_id \
--header 'Accept: application/json'
```
```js Node theme={null}
const fetch = require("node-fetch");
const url =
"https://client.corrily.com/v1/subscriptions/user_id/origin/origin_id";
const options = { method: "DELETE", headers: { Accept: "application/json" } };
fetch(url, options)
.then((res) => res.json())
.then((json) => console.log(json))
.catch((err) => console.error("error:" + err));
```
```python Python theme={null}
import requests
url = "https://client.corrily.com/v1/subscriptions/user_id/origin/origin_id"
headers = {"Accept": "application/json"}
response = requests.delete(url, headers=headers)
print(response.text)
```
# Get All Products
Source: https://docs.corrily.com/api-reference/get-all-products
GET https://client.corrily.com/v1/products
Retrieve details of all Corrily products.
```bash Bash theme={null}
curl --location --request GET 'https://client.corrily.com/v1/products'
```
```json Example theme={null}
{
"message": "Successfully retrieved product.",
"success": true,
"products": [
{
"api_id": "monthly",
"name": "Monthly Product",
"base_px": 10.0,
"max_px": 999.0,
"min_px": 0.0,
"integrations": [
{
"type": "stripe",
"stripe_product_id": "my_stripe_pid"
}
]
},
{
"api_id": "yearly",
"name": "Yearly Product",
"base_px": 100.0,
"max_px": 9999.0,
"min_px": 100.0,
"integrations": [
{
"type": "stripe",
"stripe_product_id": "my_stripe_pid_yearly"
}
]
}
]
```
## Response
Whether or not the request completed successfully.
Text information about request status. In case of an error here will be a useful explanation.
List of all your products
Look for the product description on the page [Get Product](get-product).
# Get charge
Source: https://docs.corrily.com/api-reference/get-charge
GET https://client.corrily.com/v1/charges/{user_id}/{origin}/{origin_id}
```bash Bash theme={null}
curl --request GET \
--url https://client.corrily.com/v1/charges/user_id/origin/origin_id \
--header 'Accept: application/json'
```
## Path Params
The payment gateway that handled the charge. Accepted values: `stripe`,
`paypal`, `chargebee`.
A unique identifier for the charge. This can be the ID that the payment
gateway assigned to the charge.
The [User ID](../users/user-id) that you provided when you [created the
charge](./create-charge).
# Get Coupons
Source: https://docs.corrily.com/api-reference/get-coupons
GET https://client.corrily.com/v1/coupons
Retrieve a list of coupons
```bash Bash theme={null}
curl --location --request GET 'https://client.corrily.com/v1/coupons'
```
```bash Bash theme={null}
curl --location --request GET 'https://client.corrily.com/v1/coupons?name=Discount-1'
```
```json Example theme={null}
{
"message": "Successfully retrieved all coupons.",
"success": true,
"coupons": [
{
"api_id": "coupon_1",
"name": "Coupon_1",
"currency": null,
"coupon_type": {
"coupon_type": "percentage"
},
"value": 10,
"active_from": null,
"active_till": null,
"org": 1,
"status": "ACTIVE",
"created_at": "2024-01-17T09:40:18.058647+00:00",
"updated_at": "2024-01-17T09:40:18.058647+00:00",
"coupon_external_coupons": []
}
]
}
```
## Query Params
Filter coupons by name
## Response
Whether or not the request completed successfully.
Text information about request status. In case of an error here will be a useful explanation.
An array of found coupons
A coupon object
Coupon's api\_id value
Coupon's name
Coupon's currency. Can be null
Coupon\_type object
Can be:
* 'percentage' for coupons with discount in percents.
* 'fixed\_amount' for coupons with fixed discount
Coupon's value for discount
ISODate value since which a coupon can be applied.
ISODate value until which a coupon can be used.
Coupon's status. Can be: "ACTIVE" and "NOT\_ACTIVE"
Attached to coupon external coupons
An external coupon object
External coupon object
External coupon source. Can be: "STRIPE", "CHARGEBEE", "RECURLY"
# Get Paywall
Source: https://docs.corrily.com/api-reference/get-paywall
POST https://client.corrily.com/v1/paywall
Fetch Corrily Paywall content including Paywall design config, Plans to display, currency & price for each Plan by providing country, IP/user_id and other characteristics.
## Body Params
Optional. If you know user's id, you can pass it to stick the response to that user.
A two-letter (Alpha-2) [ISO 3166 country
code](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes#Current_ISO_3166_country_codes).
Example: `US`
Optional. If you want to apply a specific paywall, you can pass it's id.
Optional product id to coupon id mapping to explicitly specify the coupon to be applied in the pricing. Coupons feature needs to be enabled for this. example `{"product_id_1": "coupon_id_1"}`. Multiple coupons can be applied by a comma-delimited string, for example: `{"product_id_1": "coupon_id_1,coupon_id2"}`
Will override the currency of any previously saved price and instead return
the price in the currency provided. If set to `true`, a `currency` param must
be passed. Unlike `override` the price amount will *not* be recomputed.
An object containing key-value pairs of arbitrary strings that represent features of this user which may affect their optimal price. See [User features](../users/02_user-features). Example: `{"platform": "ios"}`
If set to false, will make sure no experiment is run.
A currency you wish the price to be tied to. Currencies do not need to match
the passed country. For example, it is possible to return a price in EUR for
someone based in the Unites Sates. Note that the country-adjusted price will
be returned, regardless of the currency set.
## Usage
Paywall API is used by Corrily SDK libraries (e.g. [@corrily/react-sdk](/basics/00_introduction)) to fetch Paywall data.
This data is enough to render the complete Paywalls on customer's website or mobile app.
Response includes Paywall design configuration, set of Products to display, and price & currency for each Product.
The response depends on the user context (country, audience, characteristics) along with currently running experiments.
* `design configuration` - contains texts, colorus & layout config for visual customization
* `products` - set of products to show on Paywall. It comes from a Package, directly liked to a Paywall, or from a Packaging Segmentation config.
* `prices` - the same data, as the `/v1/prices` [API endpoint](/api-reference/calculate-price) response. Similarly to `/v1/prices` logic, once User see a Paywall, he'll be permanently sticked to the same design, set of plans and prices unless the `"override": true` parameter will be sent in the API request.
## Response
Whether or not the request completed successfully.
Id of the paywall
Is it a default paywall or not
Default buttons' caption
Default buttons' color
Background color
Should similar features in products be collapsed or not
Show product description
Header text abore the paywall
Text between title and paywall
Show header with description or not
Show annual discount percentage number near a monthly/annual switcher
Show feature comparison table below a paywall
Text on the button for showing feature comparison table
An object, where:
key - feature api id
value - badge title
Id of the product
Text unique id between organization's products
Name of the product
"month" or "year"
The period between payments
Optional
Optional
Optional
Optional
Optional
Optional
Id of the trial
Number of days provided by a trial
Type of the trial
Optional
Description override for a product in a paywall
Optional
Id of the feature
Text id of the feature
Feature type: "numeric", "boolean"
Feature's name
Optional. Value exists only for numeric features
Id of the feature value
Title for the unit
Optional
Feature description
Feature status: "draft", "active"
Feature status: "draft", "active"
Feature group id
Feature group name
Optional. Object with additional override info for a product
Optional. Override for a button caption of the product
Optional. Override for a button color of the product
Optional. Override for a description of the product
Optional. Override for a badge of the product
Optional. Override for a color of the product card
Optional. Url for a redirect on click the product's button
Optional. Flag: is product's price hidden, or not
Optional. Override for a product's price
# Get Product
Source: https://docs.corrily.com/api-reference/get-product
GET https://client.corrily.com/v1/products/{product_api_id}
Retrieve the details of a single Corrily product.
```bash Bash theme={null}
curl --location --request GET 'https://client.corrily.com/v1/products/monthly'
```
```json Example theme={null}
{
"message": "Successfully retrieved product.",
"success": true,
"api_id": "monthly",
"name": "Monthly Product",
"base_px": 10.0,
"max_px": 999.0,
"min_px": 0.0,
"baseline_id": null,
"product_trials": [],
"integrations": [
{
"type": "stripe",
"stripe_product_id": "my_stripe_pid",
"interval": "year",
"interval_count": 1
}
],
"country_prices": [
{
"country": "ES",
"local_price": 50.0,
"currency": "EUR"
}
],
"features": [
{
"api_id": "email_sync",
"name": "Full email sync with templates, open and click tracking and group emailing",
"description": "Full email sync with templates, open and click tracking and group emailing",
"status": "active",
"type": "boolean",
"unit": null,
"group_id": 1200,
"feature_group": {
"id": 1200,
"api_id": "collaboration",
"name": "Collaboration",
"description": ""
},
"value": 0
}
]
}
```
## Path Params
The api ID of the product.
## Response
Whether or not the request completed successfully.
Text information about request status. In case of an error here will be a useful explanation.
Product's corrily api\_id
Product's name
Product's base price in float format
ID of another product, considered as “baseline”.
It is used to calculate striked price(`price_with_baseline_strike`) in `/v1/prices` response.
A list of connected payment integrations (Stripe, Chargebee, ...)
Object, containts information about for a one payment integration
stripe\_with\_interval
Product id on the payment provider side
Payment interval (year, month)
Cadense of payments
A list of price objects for the given product
Object, containts information about for a one payment integration
Two-letter ISO3166 country code.
Price value
The three-letter ISO4217 currency code of the price in this country.
A list of features connected to the product
Feature object
Feature's corrily api\_id value
Feature's name
Feature's description
Feature's status (active, inactive)
Feature's status (active, inactive)
Feature's type (boolean, numeric)
Feature's measurement unit. Applicable only for numeric features. For boolean features equals null
Feature's threshold value. Applicable only for numeric features, for boolean always equals to 0
Feature's group id
Feature Group object
Feature's group id
Feature's group api\_d
Feature's group name
Feature's group description
# Get subscription
Source: https://docs.corrily.com/api-reference/get-subscription
GET https://client.corrily.com/v1/subscriptions/{user_id}/{origin}/{origin_id}
Retrieve the details of a single subscription for a single customer.
Custom payment gateway integrations only
You do not need to use this endpoint if you have [granted Corrily automated access](../payment-gateways/integration-guide#recommended-provide-automated-access-of-subscription-and-charge-events) to your payment gateway's subscription and charge events.
## Path Params
The payment gateway that handled the subscription. Accepted values: `stripe`,
`paypal`, `chargebee`
A unique identifier for the subscription. This can be the ID that the payment
gateway assigned to the subscription.
The [User ID](../users/user-characteristics) that you provided when you
[created the subscription](./create-subscription).
```json Example theme={null}
{
"success": true,
"subscription": {
"corrily_product_id2": 122,
"latest_event_id": "corrily_evt_1628549872",
"latest_event_timestamp": 1628549872,
"org_id": 97,
"origin": "stripe",
"origin_id": "origin789",
"type": "subscription",
"created": 1628548983,
"interval": "month",
"interval_count": 1,
"status": "active",
"user_id": "user123"
}
}
```
```bash Bash theme={null}
curl --request GET \
--url https://client.corrily.com/v1/subscriptions/user_id/origin/origin_id \
--header 'Accept: application/json'
```
```js Node theme={null}
const fetch = require("node-fetch");
const url =
"https://client.corrily.com/v1/subscriptions/user_id/origin/origin_id";
const options = { method: "GET", headers: { Accept: "application/json" } };
fetch(url, options)
.then((res) => res.json())
.then((json) => console.log(json))
.catch((err) => console.error("error:" + err));
```
```python Python theme={null}
import requests
url = "https://client.corrily.com/v1/subscriptions/user_id/origin/origin_id"
headers = {"Accept": "application/json"}
response = requests.get(url, headers=headers)
print(response.text)
```
# Get User country
Source: https://docs.corrily.com/api-reference/get-user-details
GET https://client.corrily.com/v1/users/{user_id}
Get country for the user if the user exists.
This endpoint returns the country for the user if the user exists. If the user does not exist, it returns an 400 error.
## Path Params
A [User ID](../users/01_users).
## Body Params
## Response
Object containing user details
Country of the user
Details of the error if response is not a success response
Has the value false if the response is not a success
```
curl --request GET \
--url 'https://client.corrily.com/v1/users/test_user_id' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json'
```
```js Node theme={null}
const fetch = require("node-fetch");
const url = "https://client.corrily.com/v1/users/test_user_id";
const options = {
method: "GET",
headers: { Accept: "application/json", "Content-Type": "application/json" }
}),
};
fetch(url, options)
.then((res) => res.json())
.then((json) => console.log(json))
.catch((err) => console.error("error:" + err));
```
```python Python theme={null}
import requests
url = "https://client.corrily.com/v1/users/test_user_id"
headers = {
"Accept": "application/json",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)
```
```json Example theme={null}
{
"user": {
"country": "AU"
}
}
```
```json Example theme={null}
{
"success": false,
"error_message": "User does not exist"
}
```
# Identify Call
Source: https://docs.corrily.com/api-reference/set-user-characteristics
POST https://client.corrily.com/v1/identify
Link IPs to users and update user features.
The identify endpoint serves three purposes:
1. It can be used to pass user-level features after (or before) a price has already been tied to them using the [main price endpoint](api-reference/calculate-price).
2. It can be used to link IPs to user\_ids so that prices persist to a user as they see a price -> login in -> check the price again.
3. It is used during mobile integrations to inform Corrily about original [Apple](/integrations/02_apple-iap-integration) and [Google](/integrations/03_google-iap-integration) purchase IDs.
## Body Params
A [User ID](../users/01_users).
An IP you wish to tie the user to. For more information read our [anonymous users guide](../users/03_handling-anonymous-users).
Optional field which indicates the country which the user belongs to. If the country given is not part of the experiment for the ip user, the user id given
wont be added to the experiment via identify. Please note this country wont be stored against user. This is used only for validating
if the experiment characteristics for previous user from the ip can be applied to the user id.
The user country will be locked to the country given in subsequent price call.
An object containing arbitrary key-value pairs that represent characteristics
of this user which may affect their optimal price. The keys can be any valid
JSON key. The values can be strings, integers, or floats.
Pass an `original_transaction_id` for a user when they purchase an IAP product. (Note that in the backend, this will not overwrite any previously stored transaction IDs for this user).
Pass an `purchaseToken` for a user when they purchase an in-app product. (Note that in the backend, this will not overwrite any previously stored purchase tokens for this user).
## Response
Its an optional field. If the identify call copied experiment attributes from ip to the user, this attribute will be included in the response. Otherwise, it wont be included in the response.
```bash Bash theme={null}
curl --request POST \
--url https://client.corrily.com/v1/identify \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '{ user_id: "string", features: {source: "youtube"} }'
```
```js Node theme={null}
const fetch = require("node-fetch");
const url = "https://client.corrily.com/v1/identify";
const options = {
method: "POST",
headers: { Accept: "application/json", "Content-Type": "application/json" },
body: JSON.stringify({ user_id: "test_user", features: {"source": "youtube"} }),
};
fetch(url, options)
.then((res) => res.json())
.then((json) => console.log(json))
.catch((err) => console.error("error:" + err));
```
```python Python theme={null}
import requests
url = "https://client.corrily.com/v1/identify"
payload = { "user_id": "test_user", "features": {"source": "youtube"} }
headers = {
"Accept": "application/json",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)
```
```json Example theme={null}
{
"status": "ok"
}
```
# Update charge
Source: https://docs.corrily.com/api-reference/update-charge
POST https://client.corrily.com/v1/charges/{user_id}/{origin}/{origin_id}
```bash Bash theme={null}
curl --request POST \
--url https://client.corrily.com/v1/charges/user_id/origin/origin_id \
--header 'Content-Type: application/json'
```
## Path Params
The payment gateway that handled the charge. Accepted values: `stripe`,
`paypal`, `chargebee`.
A unique identifier for the charge. This can be the ID that the payment
gateway assigned to the charge.
The [User ID](/api-reference/users/user-id) that you provided when you
[created the charge](/api-reference/charges/create-charge).
## Body Params
The amount (price) of the charge.
A 10-digit (seconds) [UNIX timestamp](https://en.wikipedia.org/wiki/Unix_time)
indicating when the charge was created. This timestamp should represent when
the charge was initialized, regardless of the status of the charge during the
initialization. It's rare to need to update `created` after initially setting
it.
The three-letter [(ISO
4217\)](https://en.wikipedia.org/wiki/ISO_4217#Active_codes) currency code.
The status of the charge. Accepted values: `pending`, `succeeded`, `failed`.
Free form key-value data. Metadata is useful for storing additional, structured information for a charge.
# Update Product
Source: https://docs.corrily.com/api-reference/update-product
POST https://client.corrily.com/v1/products
Update the properties of a Corrily product.
```bash Bash theme={null}
curl --location -g --request POST 'https://client.corrily.com/v1/products/monthly' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "Monthly Product",
"base_px": 10,
"interval": "month",
"interval_count": 1,
"country_prices": [
{
"country": "ES",
"amount": 50,
"currency": "EUR"
}
],
"integrations": [
{
"type": "stripe",
"stripe_product_id": "my_stripe_pid"
}
]
}'
```
## Path Params
The name of the product. Must be unique.
## Body Params
The name of the product. Must be unique.
The base price of the product in `USD`.
A string value denoting how often to bill for a subscription. Possible values:
`day`, `week`, `month`, `year`, `one_time`.
An integer denoting how many intervals to bill for the. A monthly product
would have `month` as interval and `1` as interval counts, etc. One time
products should have an interval count of `1`.
A list of one ore more of the below country price object. *Country prices provided during an update call with overwrite any existing ones*.
Two-letter ISO3166 country code.
The price of the product in this country.
The three-letter ISO4217 currency code of the price in this country.
A list of one or more of the below integration price objects. *Integration objects provided during an update call with overwrite any existing ones*.
Should be set to equal `stripe`.
The matching [Stripe product
ID](https://stripe.com/docs/api/products/object#product_object-id).
Should be set to equal `stripe_with_interval`.
The matching [Stripe product ID](https://stripe.com/docs/api/products/object#product_object-id).
The frequency at which a subscription is billed. One of `day`, `week`, `month` or `year`.
Sometimes Stripe products actually encompass more than what Corrily would consider a product. For example,
the Stripe product for a company's "Pro Plan" might have two
[Price objects](https://stripe.com/docs/api/prices/object)
one for the monthly version of the plan, the other for its annual version. Meanwhile, Corrily would consider
these to be two distinct products `pro_monthly` and `pro_annual`. To capture such differences you can pass
a `stripe_with_interval` integration object which can help separate such cases.
The number of intervals (specified in the interval attribute) between subscription billings.
For example, interval=month and interval\_count=3 bills every 3 months.
Should be set to equal `chargebee`.
The matching [Chargebee plan
ID](https://apidocs.chargebee.com/docs/api/plans?prod_cat_ver=1#plan_id).
Should be set to equal `recurly`.
The matching [Recurly plan
ID](https://developers.recurly.com/api/v2021-02-25/index.html#operation/create_plan).
Currency value
# Update subscription
Source: https://docs.corrily.com/api-reference/update-subscription
POST https://client.corrily.com/v1/subscriptions/{user_id}/{origin}/{origin_id}
Notify Corrily that a subscription has changed.
Custom payment gateway integrations only
You do not need to use this endpoint if you have [granted Corrily automated access](../payment-gateways/integration-guide#recommended-provide-automated-access-of-subscription-and-charge-events) to your payment gateway's subscription and charge events.
## Path Params
The payment gateway that handled the subscription. Accepted values: `stripe`,
`paypal`, `chargebee`
A unique identifier for the subscription. This can be the ID that the payment
gateway assigned to the subscription.
The [User ID](../users/user-ids) that you provided when you [created the
subscription](./create-subscription).
## Body Params
The amount (price) of the subscription.
A 10-digit (seconds) [UNIX timestamp](https://en.wikipedia.org/wiki/Unix_time)
indicating when the subscription will be canceled. In other words, the
customer will no longer have access to your service after this time. This
parameter should only be provided when `status` is set to `canceled`.
A 10-digit (seconds) unix timestamp indicating when customer expressed their
intent to cancel. This parameter should only be provided when `status` is set
to `canceled`.
A 10-digit (seconds) [UNIX timestamp](https://en.wikipedia.org/wiki/Unix_time)
indicating when the subscription was created. This timestamp should represent
when the subscription was initialized, regardless of the status of the
subscription during the initialization. It's rare to need to update created
after initially setting it.
The three-letter ([ISO
4217](https://en.wikipedia.org/wiki/ISO_4217#Active_codes)) currency code.
The status of the subscription. Accepted values: `pending`, `trialing`,
`active`, `canceled`
A 10-digit (seconds) [UNIX timestamp](https://en.wikipedia.org/wiki/Unix_time)
indicating when the trial subscription begins. This parameter should only be
provided when `status` is set to `trialing`.
A 10-digit (seconds) [UNIX timestamp](https://en.wikipedia.org/wiki/Unix_time)
indicating when the trial subscription ends. This parameter should only be
provided when `status` is set to `trialing`.
```json Example theme={null}
{
"message": "[dev=False] subscription record was written.",
"success": true,
"record": {
"origin": "stripe",
"type": "subscription",
"latest_event_timestamp": 1628609045,
"latest_event_id": "corrily_evt_1628609045",
"corrily_product_id": 122,
"org_id": 97,
"origin_id": "o123",
"user_id": "u123",
"created": null,
"interval": "month",
"interval_count": 1,
"status": "active",
"cancel_at": null,
"canceled_at": null,
"trial_start": null,
"trial_end": null,
"currency": null,
"amount": null,
"amount_usd": null,
"amount_usd_monthly": null
}
}
```
```bash Bash theme={null}
curl --request POST \
--url https://client.corrily.com/v1/subscriptions/user_id/origin/origin_id \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '
{
"amount": 0,
"cancel_at": 0,
"canceled_at": 0,
"created": 0,
"currency": "string",
"status": "string",
"trial_start": 0,
"trial_end": 0
}
'
```
```js Node theme={null}
const fetch = require("node-fetch");
const url =
"https://client.corrily.com/v1/subscriptions/user_id/origin/origin_id";
const options = {
method: "POST",
headers: { Accept: "application/json", "Content-Type": "application/json" },
body: JSON.stringify({
amount: 0,
cancel_at: 0,
canceled_at: 0,
created: 0,
currency: "string",
status: "string",
trial_start: 0,
trial_end: 0,
}),
};
fetch(url, options)
.then((res) => res.json())
.then((json) => console.log(json))
.catch((err) => console.error("error:" + err));
```
```python Python theme={null}
import requests
url = "https://client.corrily.com/v1/subscriptions/user_id/origin/origin_id"
payload = {
"amount": 0,
"cancel_at": 0,
"canceled_at": 0,
"created": 0,
"currency": "string",
"status": "string",
"trial_start": 0,
"trial_end": 0
}
headers = {
"Accept": "application/json",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)
```
# Introduction
Source: https://docs.corrily.com/basics/00_introduction
Explore our guides and examples on how to implement Corrily.
Structured for efficiency, this resource helps you to master the Corrily Platform,
which includes Price Management and Price Experimentation.
While invaluable for those new to Corrily, this documentation also serves Pricing experts
seeking in-depth knowledge in specific aspects of their domain.
Navigate through the three main sections - Overview for Beginners, Integration Guides for Advanced
and the Specific Product-Feature sections.
Also note, we’ve fine-tuned ChatGPT 4 specifically on our Docs and API.
It might save you time and directly answer your questions.
Simply hit `Cmd` + `K` to start a dialog.
## For Beginners
If you're new to Corrily, start with the Basics section to grasp the core value proposition of the platform.
Then, move on to the Quickstart guides to understand how to set up a simple application.
Finally, look at the code samples on the Github Repo.
* [What is Corrily](/basics/01_what-is-corrily)
* [Corrily Architecture](/basics/02_corrily-architecture)
* [Quickstart on Web](/basics/02_quickstart-web)
* [Implementation Examples Github Repository](https://github.com/corrily/integration-examples)
## For Advanced Users
If you're well-versed, we recommend going over the Quickstart guides and our [architecture overview](/basics/02_corrily-architecture).
After that, dig into Integration Guides that align with your specific billing platforms or features of interest.
### Billing Platforms supported
* Stripe
* Chargebee
* Recurly
* Braintree
* Apple iOs
* Google Play
* Custom Integration (for any other platform)
Don’t want to spend time digging into the docs? Corrily team is always available for [**Technical Calls**](https://www.corrily.com/schedule-demo?utm_content=utm_content=docs),
where we discuss both integration processes and technical aspects.
The call is also an opportunity to share best practices in Price Experimentation and Price Management.
## Dive Deeper into Corrily's Platform
1. [Paywall Builder](/paywall-builder/configure): Learn to leverage Corrily's ready-made Paywalls UI to save development time.
2. [Price Management](/prices/01_country-prices): Understand how to efficiently manage and adjust your pricing and packaging, either globally or based on specific countries and audiences.
3. [Users](/users/01_users): Get to know how to manage your user base, including anonymous visitors, and how to setup audiences for more fine-grained pricing, experimentation and analytics.
4. [Experimentation](/experiments/01_experiment_design): Master the art of designing and running experiments to optimize almost every aspect of subscription monetization.
5. [Analytics](/experiments/08_experiment_analytics): Use Corrily's subscription analytics to save money on the separate analytics tool and build better understanding on how read experiment results to make data-driven decisions.
6. [Security and Privacy](/security_privacy/00_security_compliance): Rest assured that Corrily prioritizes security and minimizes the use of sensitive user data.
# What is Corrily?
Source: https://docs.corrily.com/basics/01_what-is-corrily
Know exactly how to price and package your products, services, subscriptions.
Today, digital businesses leave both money and users on the table owing to various challenges with their
pricing and packaging strategy viz:
* Slow and expensive price discovery
* Lack of visibility into willingness to pay across different markets and user segments
* Lack of resources when it comes to experimenting with pricing and packaging viz. trials and trial lengths, features,
plans shown etc.
* Inability to change prices without complex billing engineering
[Corrily](https://www.corrily.com/product-demo?utm_content=docs) solves these challenges by making it extremely simple for
business users to personalize paywalls for different user segments, run live experiments to find hidden monetization opportunities,
make real-time changes to prices and packages, as well as get automated pricing insights.
Corrily natively integrates with all major payment gateways, subscription management and commerce platforms,
allowing you to:
## Run live monetization experiments across platforms
* **Paywall Experiments** wherever you show a paywall, pricing page or product prices to your users - be it on your website, mobile app,
in-product or via marketing channels
* **Price Experiments** to test what prices optimize your revenue and conversion. Pricing tests can span simple tests such as testing individual prices or currencies of products, or more complex tests such as relative pricing of products
* **Packaging Experiments** to test what combination of products, features and trials work the best - for e.g. for an e-learning businesses students are more likely to buy short term plans such as a month-long plan, whereas professionals may be inclined to buy longer-term plans such as 3-months, or even an annual plan.
* **Discount Experiments** to test what discounts work the best for your objectives i.e. does 10% discount off work better than a flat \$10 discount?
## Understand Price Elasticity
* Study the impact of any experiment using Corrily's analytics dashboard or export analytics-ready tables into your own analytics or data warehouse platforms
* Understand your product's demand curve across your users using in-built analytics
* Monitor key downstream metrics such as revenue, ARPU, Trial and Paid Conversion, LTV, Average Order Value (AOV) etc.
## Offer Personalized Discounts
* Personalize discounts for every user based on user features such as their location, device type, acquisition channel, product engagement etc.
* Run promotional campaigns such as during holiday periods or during other key marketing moments
## Manage Prices at Scale
* Manage the prices of products, including handling of different currencies, FX conversions, local price formats, as well as price-niceness strategies
* Assign unique prices and coupons to individual users or groups (segments) of users
# Corrily Architecture
Source: https://docs.corrily.com/basics/02_corrily-architecture
Explore Corrily Platform components, Data Model and Control Flow.
## Architecture Overview
The Corrily Platform is designed to control and optimize almost every aspect of Pricing
and Monetization for subscription-based businesses. Its architecture consists of several key components,
including Pricing API, Analytics dashboard and Integrations.
### Paywall Builder
With Corrily, your pricing page becomes dynamic and remotely-configured, eliminating the need
for hardcoded prices. The content of the Pricing Page (including sets of plans to display, prices itself,
currencies, discounts, trial info, etc) will be fetched via API, which can be consumed either
by your back-end code or directly from the front-end or Mobile App using Corrily's SDKs.
That approach enables a personalized and dynamic pricing experience for each user.
### Price Management and Price Experimentation
Corrily serves as an external “admin panel” that controls your prices, acting
as a source of truth for pricing and monetization. It allows you to configure and immediately
apply every aspect of subscription monetization through the Corrily dashboard.
On each API call, the platform dynamically determines the country and audience of the User,
deciding if the User falls into an active experiment, and then responds with the appropriate
pricing page content for that User.
After displaying prices to the User, Corrily records the displayed values,
ensuring the User consistently encounters the same prices across the product and checkout pages,
and even on different platforms like on web or mobile app.
### Revenue Data Ingestion
To enable Corrily's analytics capabilities, it’s required to feed revenue events into the Corrily Platform
from your Subscription Management solution (Stripe Billing, Chargebee, Recurly or other).
Revenue events are typically fetched by Corrily itself using API Key or OAuth integration,
but it’s also possible to use Subscription Management Platform webhooks
or to feed revenue events using Corrily CRUD API.
In summary, the Corrily Platform architecture is designed to provide rich control
and optimization capabilities for different aspects of your pricing page.
By leveraging these components, you can create personalized pricing experiences for your users,
make data-driven decisions, and ultimately maximize revenue.
## Control Flow
There are typically multiple places where users could see or interact with your pricing page:
* **Anonymous pricing page**, publicly visible for unauthenticated website users or mobile app visitors
* **Internal Paywall or Customer Portal**, visible for signed users
* **Checkout Flow**, hosted by you, by subscription management platforms, or by IOS/Google Play
### Control Flow before Corrily
Before Corrily, companies typically use this simple setup:
* Paywall Plans and Prices are hardcoded on the front-end, or stored in the database
* Checkout prices are configured on the billing platform catalog
That means any update requires engineering effort, at least on the billing engineering side,
and, optionally, on front-end.
Also, that means there are risks of different types of inconsistencies.
### Control Flow with Corrily
With Corrily your Pricing Page becomes dynamic and controlled by the centralized monetization service.
As a centralized service, Corrily will ensure the following aspects:
* **Config correctness** - Corrily determines User’s country and audience in real-time to let Users see the appropriate prices configured for that country and audience
* **User Experience consistency** - Corrily ensures User will see the same set of plans, prices and currencies across all the places in the product and all the platforms
* **Checkout consistency -** Corrily ensures Users will pay the same price that they have seen
* **Features consistency -** Corrily ensures Users will always see the same set of features available in each plan
Here's a more detailed explanation of the Control Flow with Corrily Platform integrated:
1. **Anonymous Pricing Page**: When a user visits your anonymous pricing page, either your backend or the Corrily Front-End Software Development Kit (FE SDK) sends the user's IP address as a parameter to the **[Price request](/api-reference/calculate-price)**. The request would look like this:
```json theme={null}
{
"ip": "127.0.0.1"
}
```
Corrily then stores the prices served for that IP address. This ensures that if the same Visitor opens a pricing page again, he’ll see the same price.
2. **Internal Paywall**: Once the user signs in and visits the internal paywall, your backend needs to send both the `IP` address and the `user ID` to Corrily. This allows Corrily to associate the `IP` address with the `user ID` and maintain the same price that was previously shown to the unauthenticated Visitor. The request would look like this:
```json theme={null}
{
"ip": "127.0.0.1",
"user_id": "my_user_1"
}
```
If it's not possible to accurately determine the user's `IP` address at the time of visiting the internal paywall, you can associate the user with the `IP` address at the time of signup by sending an [Identify Request](/api-reference/set-user-characteristics) to Corrily with the `IP` address and `user ID`.
1. **Subscription Creation**: When creating a new subscription, you need to pass the price provided by Corrily to the Subscription Management Platform. This is typically done using `Overrides` (refer to the integration guides for each platform for more details)
By passing overrides, you ensure that the user will pay the price they saw, not the price hardcoded in the Subscription Management Platform catalog. This is particularly important during price experimentation, where different users might see different prices for the same plans.
2. **Successful Payment**: After a successful payment, the new subscription and charge rows will be automatically pulled by Corrily via data ingestion pipeline (for OAuth and API-Key integrations), or via webhook, and will appear in analytics on the next iteration of dashboard metrics recalculation.
## Corrily Data Model
### Paywall
Corrily **Paywall** is a fully functional front-end component, displaying plans, prices, coupons, and other entities from Corrily's Core.
Essentially, it's a payment page, rendered by [front-end SDK](/integrations/02_paywall-integration),
that dynamically displays prices from Corrily's Core for each specific user.
### Package
A Package means a set of plans to be displayed on the [Paywall](#paywall) depending on the user's location or other attributes.
It encapsulates several plans, all of which are displayed on the [Paywall](#paywall).
This abstraction simplifies management of which plan sets to show to which [Audience](#audience) and in which country.
### Product
Corrily **Product** represents a Plan or Addon User might subscribe to.
Typically, Corrily **Products** are mapped 1:1 to Plans or Addons from billing platforms.
### Price
Corrily **Price** is an entity which holds the `amount` and `currency` to be displayed for the selected [Product](#product)
in the selected country for the User's [Audience](#audience).
#### Fixed vs. Ephemeral prices
*The concept of ephemeral prices in Corrily.*
Corrily price can be either **fixed** or **ephemeral**.
#### Fixed Prices
A fixed price is a set *\* pair that is set
via the dashboard, or one of our CRUD API endpoints, and identifiable by a *price\_id* prefixed by `price_`. The amount set will match exactly the
display values returned by Corrily's API.
#### Ephemeral Prices
Ephemeral prices are the product of price transformations applied to fixed prices such as explicit applications of
rounding, calls for the price to be displayed in another currency, coupon applications, or price overrides.
Ephemeral prices too are identifiable by price IDs but these are prefixed by `eph_price_`.
### Trial Period
A **Trial Period** is an entity that allows the addition of a free or paid trial period to a [Product](#product).
The parameters of the **Trial Period** are configured separately from the [Product](#product) itself
and can be experimented with by attempting different **Trials** for different **Products**.
### Coupon
Corrily **Coupon** represents a discount option,
which allows giving a specific User a fixed or percentage discount on a [Product](#product).
Typically **Corrily Coupons** map 1:1 to the coupons on billing platforms.
### Product Feature
Product Feature represents a piece of functionality included into a specific [Product](#product).
Corrily allows to dynamically show different sets of features included into [Products](#product) on the [Paywall](#paywall).
### User
Corrily **User** represents physical person, who can see the price and can have one or several subscriptions,
each subscription comprises one or several products.
Typically Corrily **User** maps 1:1 to billing platform **Customer**.
### User Attribute
Corrily might store any custom metadata **Attributes** assigned to a [User](#user).
Those attribute values can be used to define [Audiences](#audience) by a variety of rules.
### Audience
Corrily allows to segment [Users](#user) based on the rules on top of [User Attributes](#user-attribute).
### Experiment
An **Experiment** is a single A-B-test, or Bayesian test, allowing comparison of different [Packages](#package),
[Prices](#price), striked "old" prices, [Trial Periods](#trial-period), discounts, [Features](#user-feature),
and other pricing attributes User can see.
# Coupons
Source: https://docs.corrily.com/coupons_and_campaigns/01_discount-coupons
How to create coupons to use when showing prices or running campaigns.
Corrily Coupons can be used to apply discounts to the products and subscriptions you offer. Coupons in Corrily can be either
a fixed amount coupon such as 10 USD or 1000 JPY, or a percentage off, such as 10% or 15% off
You can use coupons to:
* Apply discounts to every invoice or only one invoice
* Reduce invoice amounts by a percentage or a flat amount
* Apply discounts to every subscription a customer has or only specific ones
Create a Corrily Coupon
Create coupons on the Dashboard or with the API
**Creating a coupon on Corrily Dashboard:**
* On the Corrily Dashboard, open the Coupons page.
* Click "Create Corrily Coupon"
* In the dialog, enter the coupon parameters
* Click Create coupon.
Alternatively, you can also import coupons from other systems such as from Stripe or Chargebee. This creates and maps
corresponding Corrily coupons.
***
Here are all the **settings for coupons** that Corrily supports right now:
* **Name:** The name of the coupon that appears on receipts and invoices
* **API ID:** A unique identifier for the coupon in the API. Corrily auto generates this for you, but you can also change it
to your liking
* **Type:** Determines whether a coupon discounts a subscription by a fixed amount or by a percentage
* **Value:** Percentage off or Discount amount
* **Currency:** currency for Fixed Amount coupons
We will soon be adding support for `coupon expiration`, `coupon targeting` (i.e. certain coupon codes can only be applied
on certain products for e.g.),
# How to Design Experiment
Source: https://docs.corrily.com/experiments/01_experiment_design
How to target the correct subset of users to experiment on.
Corrily allows users to design and run [various types of experiments](/experiments/02_experimentation_platform).
This article will describe the general experimentation framework,
guide you through the process of setting up and running any type of experiment,
as well as analyzing the results and taking appropriate actions after the experiment was concluded.
## From Hypothesis to Experiment Design
Before running an experiment, it is important to **outline what hypothesis you want to test**.
This could mean running a single experiment or it could mean breaking down the hypothesis into multiple experiments. For e.g. if your hypothesis is that there may be **higher willingness to pay for your products in North America**, you may want to break down the hypothesis into two experiments - one experiment across US and Canada (since they are likely similar markets for your business), and another for Mexico.
If your hypothesis is a little more specific - say, there is higher willingness to pay amongst *professionals in North America*, it is best to [**define the relevant audience**](/segmentation/01_audiences) on the Corrily dashboard, and segment your experiment further. However, if you do not have a strong hypothesis, we recommend starting broad, learning from the experiment analytics so you can form some hypothesis, and then running more specific experiments.
Other important consideration when designing your experiment is to think through [**what cohorts of users do you want to experiment on**](#experiment-policy). Do you want to experiment solely on new users who have never seen your pricing page before, or you want to experiment with just the free user base.
There are all important considerations in deciding who to experiment on and setting your experiments up for success.
Corrily’s pricing experts are always available to help you think through these trade-offs
## Experiment Setup
### **Experiment Status Flow**
Every experiment undergoes a systematic Status Flow from drafting to conclusion. This flow represents the progressive steps from initial consideration of an experiment to its final results.
1. **Drafting the Experiment Configuration**
* At this stage, you simply draft the configuration of your experiment. It's worth noting that while in the draft state, the experiment remains inactive and only serves as a saved template for any future modifications.
2. **Manual Testing**
* Initially, set the experiment traffic to `0%`. This enables manual testing without affecting production users.
* With Corrily API, you can force experimental prices to be returned even when traffic is set at `0%`. Additionally, users can be directly assigned an experimental price via the User Page on the dashboard.
* **Traffic Allocation - Step 1**: Navigate to "Experiments" on the left menu and select the desired experiment. Once inside, scroll down and click on the "Traffic Allocation" edit icon as illustrated below.
* **Traffic Allocation - Step 2**: In the subsequent window, set your experiment traffic (`0%` in this instance). Options are available for either automatic traffic allocation per experiment arm or manual adjustments. Confirm your settings by clicking "Save."
3. **Activating the Experiment**
* Once satisfied, activate your experiment. The experimental prices will instantly be served to your users.
* Activation can be done by selecting the chosen experiment under the "Experiments" window and then clicking "Start Experiment."
4. **Pausing and Resuming**
* If necessary, experiments can be temporarily paused and then resumed at a later time.
5. **Concluding the Experiment**
* When you've gathered sufficient data and insights, you can conclude the experiment, ceasing its operations permanently.
**Recommendation**: Avoid rolling out the experiment to `100%` of your traffic immediately after manual testing. Instead, initiate with a gradual rollout, targeting `5-10%` of your traffic. Monitor for at least a day to ascertain whether users are subscribing at the experimental price and if operations are proceeding as anticipated. If all appears in order, you can then scale the traffic percentage to encompass `100%`.
### **Experiment Products**
The first step in setting up an experiment is to add the products you want to run the experiment for. These products can be any kind of subscription plan or addon (monthly/annual, basic/advanced) and can be defined in the Products tab of the Corrily dashboard
### **Experiment Targeting**
A crucial aspect of Experiment Setup Process is **experiment targeting**. Generally, there's no obligation to modify prices for your entire user base. In many instances, businesses differentiate pricing based on the countries they operate in. This distinction arises from variations in Purchasing Power Parity (PPP) across countries.
Thus, the primary configuration option for experiment setup pertains to targeting. It allows specification of countries (based on purchasing power or region) or the identification of distinct countries for the experiment. Additionally, the target audience can either encompass all users or be tailor-made to focus on a specific audience found under the 'User Management' tab. Further details on audience creation are available at [Segmentation Documentation](/segmentation/01_audiences).
Corrily's advanced filtering system facilitates this, allowing businesses to categorize countries based on Purchasing Power, Regional distinctions, or specific country-based criteria
User audience targeting can be broad, encompassing all users, or tailored to focus on a particular group. This specified group can be designed under the 'User Management' tab. For comprehensive guidelines on crafting a specific audience, please consult our [Audience Segmentation Documentation](/segmentation/01_audiences).
### **Experiment Policy**
A crucial configuration in your experiment setup is the **Experiment Policy**. There are two primary options available:
* **NEW\_USER\_ONLY**: This policy focuses exclusively on new users. The idea behind this strategy is to protect your existing user base, ensuring they are not subjected to unexpected price modifications. Those who have previously seen the price or have transitioned to paying customers will remain unaffected by the experiment. Typically, this "new users only" approach is the default, offering a risk-free method, as there's no chance of existing users identifying any package alterations.
* **NEW\_AND\_EXISTING\_USERS**: Sometimes, due to a limited influx of new users or perhaps operating over an extended period, there may arise a need to update packages for all users, including the existing ones. This strategy addresses that, allowing experimental packages to be showcased to both new users and existing users who haven't been part of any ongoing experiment. This implies that irrespective of any previous packages these users might have seen, Corrily will present the newly determined experimental packages to them.
It's vital to select a policy that aligns with your business goals and the expectations of your user base.
We have to be careful because Billing Platforms like Stripe permanently stick Customer to a currency at the time of Customer creation.
Once customer is created it'll be impossible for the customer to pay in the different currency, so it makes it impossible to do package experiments for existing users. To avoid technical issues here we're recommending do the **special preparation steps** to group already paid users into the separate Audience, and to exclude that Audience from any experiment.
### **Traffic Allocation Strategies**
The fourth key concept is **Traffic Allocation**. It's recommended to use the default `Automatic` option, which will split the traffic equally among arms. Think of it as about an AB test or about ABN test. Almost everybody wants to send the same amount of traffic to each arm, but sometimes if you don't have enough traffic or if you're already confident in your baseline price starts and sure, there're no risk of seasonality change, you might want to avoid sending traffic to baseline arm.
You might want to change the Traffic Split manually. For example to:
* decrease baseline arm traffic %
* or to completely disable some poorly performing arms
## **Steps Before Starting the Experiment**
### **Backfill Existing Users**
Before starting an experiment, it is recommended to backfill existing users into Corrily to avoid serving experimental prices to the existing user base. This also allows you to create an audience for existing paid customers and exclude them from any experiment to avoid currency issues.
The backfill of the small userbase could be done using [Identify API Endpoint](/api-reference/set-user-characteristics). More information about segmentation can be found in [Audiences](/segmentation/01_audiences).
The backfilling process of the large userbase requires expertise and better to be executed by the Corrily team. Should you need to undergo this phase, kindly reach out to us for assistance.
### **Mitigating the Risks**:
**Potential Risks of Price Experimentation**:
1. **Payment Currency Discrepancy**: One of the primary risks is the possibility of a returning paying user encountering an wrong currency when they revisit, thus hindering them from making subsequent payments.
2. **Perception of Inconsistent Pricing**: There exists a risk of existing users detecting a shift in pricing. Such inconsistencies can lead to discomfort or displeasure among them.
To counteract these risks, it's advisable to implement a backfill of existing users into the Corrily system prior to initiating any price experiments. Undertaking this step ensures that existing users aren't inadvertently subjected to new experimental pricing. Furthermore, once these users are backfilled, an Audience dedicated to current paid users can be constructed, allowing them to be exempted from experimental changes, thereby averting potential currency discrepancies.
## **Analyzing Experiment Results**
### Compare ARPU or predicted LTV across the Arms
Corrily provides the comprehensive set of charts to study the experimental prices performance on every stage of the user funnel:
* starting from conversion from free Visitors to the signed Users,
* then conversion to a trial period or to a first payment,
* and then the revenue metrics on how much did user pay over time,
* and finally the retention and charts
Using experiment Analytics you'll be able to analyze the every step of the funnel individually, or to see the final experimentation KPI metric projection (usually, the "Predicted LTV").
### Look at the over-time charts to study trends
Also want to note that Experiment Analytics has "Over-time charts", which might help you to experiment quicker. That charts help to study trends in conversion or revenue across arms. It's not rare experiment stats stop changing over time, means nothing is going to be changed in the future with a more data coming in, so, there's a signal to conclude the experiment faster.
## **Steps after Concluding the Experiment**
### **Rollout New Prices or Packages**
If the optimal arm wins, you may want to roll out the new prices to your entire user base in the experimental countries and for experimented audiences. To do this, update the prices in the Price Control Center and decide if you want to migrate existing users to allow them to see the updated prices or packages instead of the previous ones
### **Migrate Experimental Users Back to Baseline**
If the baseline arm wins, you may want to reset the experimental users back to the baseline price. This can be done via `Prices Migration`.
The migration of the users might be done using [Calculate Price API Endpoint](/api-reference/calculate-price) with the `"override": true` flag in payload.
The migration process of the large amount of users requires expertise and better to be executed by the Corrily team. Should you need to undergo this phase, kindly reach out to us for assistance.
## **Limitations and Tricky Moments**
You should be aware that Corrily Data Model assumes that a **single user** can be part of at most **one experiment at the time.**
If there're multiple experiments running simultaneously in the same country, even if they'll be testing the prices for the different products, it wouldn't be possible for the same user to participate in both experiments.
It's strongly recommended to run no more than a single experiment in the same country, or for the same Audience at a time.
# Corrily Experimentation Platform
Source: https://docs.corrily.com/experiments/02_experimentation_platform
An introduction to running experiments with Corrily
Why use Corrily for your pricing, packaging, discount and paywall experiments?
Corrily provides an end to end experimentation platform for all things pricing, packaging, discounts and paywalls.
Corrily's experimentation platform abstracts the complexity involved in running experiments in-house, and manages
everything for you. Moreover, our **machine learning models are pre-trained, and continuously improving** over time based
on your business-specific data and anonymous data across our entire customer base. This, in combination with our
**data-efficient sampling algorithms**, results in **30-40% improvement** in experiment convergence over traditional AB/n
experiments.
What can you manage using Corrily's experimentation platform?
Experiments in Corrily can be set-up and managed using our dashboard or the API:
* Defining the experiment objective
* Managing control and experiment treatments
* Orchestrating experiments i.e. traffic allocation, adding and disabling particular experiment treatments
* Measuring experiment results across most common metrics such as ARPU, LTV, Conversion, Retention, MRR, ARR etc.
* Calculating statistical significance and Confidence Intervals
* Post-experiment clean-up
* Rolling out winning experiment treatments
Different types of Experiments in Corrily
Corrily's Experimentation platform allows you to run various kinds of experiments.
* **Pricing Experiments:** to find the best price for a set of users. Pricing experiments can be used to test different price
points, different strikethrough prices, as well as price formatting i.e. price rounding strategies and currencies shown
* **Packaging Experiment:** to find the best combination of products and plans for a set of users. Packaging experiments
can also be used to test things such as different feature combinations for a product, different trial strategies etc.
* **Discount Experiments:** to find the optimal discount for a set of users. Discount experiments can be used to test optimal
discount percentages or amounts or coupons
Things to know before setting up an experiment
A few things to note while running an experiment with Corrily:
* **Experiment Objective:** Corrily supports various experiment objectives that you can choose from, such as
Optimizing ARPU, LTV, Conversion or Retention. Make sure you are selecting right experiment objective
* **Experiment Targeting:** Experiments can be run on a subset of products, countries and audiences. We recommend spending
some time thinking through the experiment architecture, especially if you plan on running multiple experiments. At Corrily, our
Pricing and Data Science experts are more than happy to consult.
* **Experiment States:** An experiment can have multiple states: `Draft`, `Ongoing`, `In Review`,`Paused` `Completed`. You can
set-up an experiment in the `Draft` state, submit it for `Review` before going live. Once an experiment is live, you can decide
to `Pause` an experiment (say during critical business periods), and `Resume` it later. Experiments can move to a `Completed`
state when they reach desired statistical significance, or can be forced to end subjectively
* **Traffic Allocation:** Experiment traffic allocation can be manual or dynamic i.e. optimized automatically by Corrily's machine
learning models
* An experiment in Corrily does not necessarily need to have a control group
# Experiment on Prices
Source: https://docs.corrily.com/experiments/03_price_experiments
How Corrily runs price experiments.
What is Price Experiment
In the Corrily platform, there exists a [variety of experimental techniques](/experiments/02_experimentation_platform).
The most common of them is **"Price Experimentation."** This methodology allows for the systematic variation of prices and currencies displayed to different users, even when considering the same subscription plans. Essentially, every user will view the same range of plans, but the prices for each plan, or a subset of them, may differ.
### Key Applications of Price Experimentation:
1. **Baseline vs. 10% Increase**: This experiment can be designed to compare the baseline price with a price that's increased by `10%`. This provides insights into user behavior and purchasing decisions when exposed to a minor price hike.
2. **Localized Prices vs. USD Prices**: In this experiment, one group of users is shown prices in their local currency, while another group views the prices in USD. This helps in understanding user preference and reception towards localized pricing as compared to standardized USD pricing.
How to setup Price Experiment
### 1. **Select Experiment Products**
Upon naming your experiment, the subsequent step is to determine the products that will be part of the experiment. This could encompass various subscriptions or packages, ranging from monthly to annual durations or from basic to advanced offerings. These product definitions are located in the 'Products' tab of our dashboard. For a comprehensive guide on product creation and management, refer to [Products documentation page](https://docs.corrily.com/prices/07_products_configuration).
### 2. Add Arms and set prices for Experimental Arms
Corrily experiments utilize the concept of arms. Think about it as about "AB tests on steroids". Experiment will have a couple of arms to test, which should include the single baseline arm to compare with. For any arm except baseline you would need to change prices and/or currencies. This price changes could be done in the Experiment Setup Interface, similar to Price Control Center. Here you can use bulk actions to modify the prices and can change prices for each country / audience individually.
In our example, we are starting a new experiment, and after filling the first page of information related to the experiment we reach the Define Arms step. Here we have a Baseline arm and we need to add at least one more to be able to proceed. Note that if you wish to delete the Baseline arm you can do that but you need to set another added arm as the new Baseline.
After the experiment arms are added, we can proceed to the Set Prices step. Here we can see the prices for each product-country pair that was created based on our previous choices. We also have different arms added so we can manipulate the prices for those arms. In order to do that, we will use the filters below and select the arm/arms that we are interested in.
For the first example, I will choose the `-10%` arm to change the prices for. Once selected, we have a checkbox that I can use to select all of the prices at once.
This is helpful as we can then perform bulk actions on the selected prices. In my case I will choose the **Generate X% Lower Prices** option to lower my prices in regards to the Baseline values.
| Bulk action | Description |
| ------------------------------------- | --------------------------------------------------------------------------------------------------- |
| Set Display Price | This action allows to change the displayed price and the currency for the selected prices |
| Switch Currency | This allows to change the currency the user will see for the selected prices |
| Change Display Price to Default Price | This will change the price seen by the user to the Baseline price |
| Generate Parity Prices | This action will generate new prices based on the purchasing power |
| Generate X% Higher Prices | This action will calculate the new price as % higher than the current baseline price and display it |
| Generate X% Lower Prices | This action will calculate the new price as % lower than the current baseline price and display it |
| Convert prices to local currencies | This will convert the price to the user local currency using locked FX rates |
| Convert prices to currency X | This allows the conversion of a price to a desired currency using locked FX rates |
If desired, the prices can be edited one by one by clicking on the edit icon on the right of each price. This will open the edit window where you have the option to change the country, price and currency as well as the option to use the local currency for that product-country pair.
All procedures and steps that are identical for both these experiments have been centralized for clarity and ease of reference.
For a comprehensive guide on these shared procedures, kindly refer to the [Experiment Design](/experiments/01_experiment_design) documentation. This ensures that you have access to the most consistent and up-to-date information, eliminating the need to browse through multiple pages for common methodologies.
# Experiment on Discounts
Source: https://docs.corrily.com/experiments/04_discount_experiments
How to run discount campaigns.
Besides running discount experiments, Corrily also supports the ability to run discount campaigns. Discount campaigns can
be used to:
* Run promotional campaigns during key marketing windows across multiple channels - such as site-wide banners, in-product
or emails
* Personalize the discounts rather than a flat X% off for everyone, based on user characteristics
Soon, we will be adding capability to render these discount strategies on site-wide banners, so you don't have to worry about building
banners and pop-ups on your own. You will not only be able to use Corrily as the backend optimization engine, but also use Corrily
to display these discounts on your web properties
# Experiment on Packages
Source: https://docs.corrily.com/experiments/05_packaging_experiments
How Corrily runs packaging experiments.
## **What is Packaging Experiment**
**Corrily's packaging experiments** allow you to test various packaging strategies for your entire user base or a subset of users to find out, what combination of products and plans work best for them. You can use a packaging experiment to test:
* Different combination of plans for e.g. say you have 4 tiers of your product offering - **`Basic, Pro, Team, Advanced`**. A packaging experiment allows you to test showing different combination of those plans to different users (including targeted segments), and decide what's the optimal combination
* A packaging experiment also allows you to test different copies, feature highlights etc. by creating different variants of the same plans say **`Basic VariantA`** v/s **`Basic VariantB`**
## How to setup Packaging Experiment
### 1. **Select Experiment Products**
Upon naming your experiment, the subsequent step is to determine the products that will be part of the experiment. This could encompass various subscriptions or packages, ranging from monthly to annual durations or from basic to advanced offerings. These product definitions are located in the 'Products' tab of our dashboard. For a comprehensive guide on product creation and management, refer to [Products documentation page](/prices/07_products_configuration).
### 2. Add Arms and select Packages for Axperimental Arms
Corrily experiments utilize the concept of arms. Think about it as about "AB tests on steroids". Experiment will have a couple of arms to test, which should include the single baseline arm to compare with. For any arm except baseline you would need to set the products you want in that specific package. could be done in the Experiment Setup Interface, similar to Price Control Center.
In our example, we are starting a new experiment, and after filling the first page of information related to the experiment we reach the Define Arms step. Here we have a Baseline arm and we need to add at least one more to be able to proceed. Note that if you wish to delete the Baseline arm you can do that but you need to set another added arm as the new Baseline.
After the experiment arms are added, we can proceed to define the arm packages. In this step for the previously set arms we will choose which of the products added to the experiment will be added to a package. This can be edited at all times ensuring a high flexibility for experimentation.
For the Baseline I only selected the Essential product and with each arm I have increased the products inside. This is exactly what the final user will see when they enter the page. If one of the arms needs editing, this can be done by clicking the Edit icon next to each arm and the following modal will open where we can modify the products inside the package.
All procedures and steps that are identical for both these experiments have been centralized for clarity and ease of reference.
For a comprehensive guide on these shared procedures, kindly refer to the [Experiment Design](/experiments/01_experiment_design) documentation. This ensures that you have access to the most consistent and up-to-date information, eliminating the need to browse through multiple pages for common methodologies.
# Recommendation Engine
Source: https://docs.corrily.com/experiments/05_recommendation_engine
Using Corrily as an independent recommendation engine for your prices and products.
Most customers use **Corrily for end to end price management** i.e. all the way from managing master prices across all products
and countries, as well as serving experimental or personalized prices. Since Corrily is able to abstract the complexity of
managing multiple sets of prices, which can also change over time, this simplifies price management exponentially for our
customers. However, if your master list of prices are deeply integrated with other enterprise workflows, and you are looking
to utilize Corrily's price optimization capabilities while still managing the master set of prices on your own, you can
use us as a **Recommendation Engine**.
Corrily as a Recommendation engine
Running Corrily as a Recommendation Engine for your prices as well as products is really straightforward:
* Instead of integrating prices returned by Corrily on your pricing interface, you can call Corrily in parallel with your
regular pricing flow.
* You would still be calling Corrily's APIs every time a price needs to be fetched, and pass all the relevant information i.e.
`User IPs`, `User IDs`, `Product IDs`, `Country`, `Coupons` and any additional user characteristics such as device, acquisition
channel etc.
* Corrily's recommended price can now be stored as an ephemeral price and displayed based on certain business logic at your
end
* You would still be feeding revenue events to Corrily using pertinent integration options for you
Here is an illustration of how the end to end flow would look.
**Note:** If you are using Corrily to optimize your paywall (i.e. optimizing which plans and products to display), you would still be
following the same set of steps.
# Experiment Analytics
Source: https://docs.corrily.com/experiments/08_experiment_analytics
How Corrily measures experiment success.
Corrily's Experimentation Platform supports fully self-serve analytics on the dashboard. For all kinds of experiments,
we provide the following analytics charts out of the box. Custom metrics can easily be added depending on your business
needs. Depending on your business model, some of these analytics charts may not apply to you. Corrily only shows charts
which are relevant to your business model.
* **A summary of key metrics** at the experiment level for a quick sanity check (across both control and experiment treatment groups)
* Number of visitors, users and paying users in the experiment
* What countries are the visitors coming in from
* Total Revenue, Average Revenue per user and Conversion Rate
* Breakdown of each treatment group - i.e. what products and prices are included across each arm in a pricing experiment etc.
* Which experiment treatment is *active* v/s *disabled*
***
* **Conversion Analytics** across each experiment treatment:
* Number of sign-ups
* Free to trial conversion
* Trial to paid conversion
* Direct to paid conversion
***
* **Revenue Analytics** across each experiment treatment:
* Average Revenue Per User, and Averaae Revenue Per Paid User
* LTV per user, as well as per paid user
* MRR
***
As the experiment approaches convergence, Corrily's models grow more certain about winning treatment(s). **At the end of
an experiment**, you can compare key metrics such as ARPU, LTV, MRR, Conversion Ratio across each treatment, the trade-off
involved (for e.g. ARPU increased by 10% but conversion dropped by 5%), as well as push the changes across specific countries
or audiences and for specific cohorts of users (for e.g. apply a price change only for new users to the pricing page)
# Launch checklist
Source: https://docs.corrily.com/integrations/00_launch-checklist
A launch checklist to be used before going live with Corrily.
Before you show prices on your pricing interfaces using Corrily and start experimentation, please review this launch
checklist to make sure everything is set-up properly:
* **Create Corrily products** for every product that you wish to integrate with us. If you have a large catalog of products,
we recommend using a CSV file to upload this to Corrily, otherwise you can easily create these on our dashboard.
**TIP:** If you have created redundant products in the past on your subscription management or commerce platform in
order to experiment with different prices or simply serve your product in different currencies, **Corrily can help with
simplification of your backend product catalog**. Since Corrily can serve a product across diffeernt prices and currencies,
we often find our customers abstracting that complexity over to Corrily
* Gather what your **base price for the products** are - these would be default prices that you want to serve for your product.
You can send this info to Corrily using a CSV while uploading products or use the dashboard.
* Gather your **master pricing sheet**. Usually your pricing team will have a list of country prices and what currency you
are charging users in. Upload this information to Corrily using a CSV.
* Gather your **existing FX rates** if you are converting prices from your base currency to local currencies.
* Identify and **integrate Corrily API on all of your pricing interfaces** for a consistent user experience
* **Backfill old users** who Corrily doesn't know about. While this is an optional step, it helps Corrily's models learn faster
and also make sure that we can lock the prices for your existing paying user base. This can be alternatively achieved by letting
Corrily know while calling our API when a particular user is an old user, or explicitly asking a user to be excluded from an experiment
* Integrate Corrily in a **staging or sandbox environment** and test with a few user accounts to ensure prices are being served
correctly.
**TIP**: Test your pricing interface with a few anonymous/unauthenticated visits using different IPs, as well as different
logged-in user accounts (old and new) to smoke-test the expected prices. Corrily can also create a staging set-up for
your organization at our end.
* **Start small** with \~1-2% of your traffic on Day 1, monitor for a day, and if everything looks good - open the flood-gates to
100% of your traffic
* Once integrated and live, you are all set to run experiments on our platform.
## Prepare your product information
Gather the following information about your product/service:
* **Product name**: A human-readable, descriptive name for the product/service
* **Base price(USD)**: The default price of your product. Currently, Corrily only supports USD but will soon be supporting
other currencies
* **Recurring Interval:** What's the recurrence interval for your product (e.g. `Monthly`, `Yearly`, or `One time`)
* **Interval Frequency:** How frequently the product/service is charged for e.g a product with a `monthly` interval, and interval
count of 3 means that you are charging users every 3 months.
* **API ID**: The identifier that you'll provide when calling Corrily's REST API endpoints
* **Cents**: Whether you display your price with cents or not (e.g. $20 or $19.99)
If you have a subscription service with multiple billing plans, you should provide this information for each billing plan.
Example:
| Product/Service Name | Base Price (USD) | Recurrence Interval | API ID | Cents |
| -------------------- | ---------------- | ------------------- | -------------- | ----- |
| Monthly Plan | 10 | Month | monthly\_plan | True |
| Annual Plan | 60 | Year | annual\_plan | True |
| Lifetime Plan | 600 | One-Off | lifetime\_plan | False |
## Notify Corrily of payment gateway events
As explained in [Price optimization overview](../pricing/price-optimization-overview), Corrily needs to be notified of subscription
and charge events if you're doing price experimentation. The easiest way is through OAuth and just requires 2 clicks.
After access is granted, your payment gateway automatically notifies Corrily of subscription and charge events.
Otherwise you can [manually notify Corrily](../payment-gateways/integration-guide#manually-notify-corrily-of-subscription-and-charge-events).
If you're using Stripe and would like to get your Corrily integration started now, click the **Connect with Stripe** button to grant Corrily read-only access to your Stripe subscription and charge events.
# Invite the team
Source: https://docs.corrily.com/integrations/01_inviting_users
Manage your team members and their access level
## Inviting a new user
Corrily Admins can invite others users to the dashboard by following these steps:
* Navigate to the *Team* page on the dashboard
* Click on `Invite`. Enter user's email, name and the desired role
* Once invited, the user will receive an invitation email.
* They can then accept the invite and logon to the Corrily dashboard
You can check pending invites on the `Invited` tab on the Team Management Page. If a user hasn't accepted your invite, or
if you sent it to an incorrect email, you can revoke the invite from the same tab. Admins can change a Corily user's role/access-level
at any point in time, or even revoke access to the dashboard
## Corrily Roles
Corrily's dashboard supports role-based access so you can invite team members to the dashboard without worrying about giving
users (such as those on your support team) elevated privileges. Giving only the required level of access for a user mitigates
security risks and makes it easier to stay compliant with company policies. Because we understand that not every user in your
organization needs full access to the Corrily dashboard, we support four distinct user permission groups with varying
levels of access: `Admin`, `Member`, `Support`, `Support(read-only)`. Currently, there is no hard limit on the number of users
you can invite to the dashboard.
* **Admins:** Admins have access to the entire dashboard, including the ability to change prices, start/pause/stop experiments,
add/remove users from the dashboard, as well as change their roles. Admin role is recommended for anyone who needs to make
changes to the pricing using Corrily's platform - such as pricing managers, business planners, product and growth team members etc.
* **Members:** Members have read-only access to the entire dashboard. While members cannot start/stop experiments, they can
review experiment results, search and interact with prices
* **Support:** Support role is useful for customer-facing support team, such as L2 or L1 support. Support team members only
access to the `Users` page on the dashboard. On the Users page, they can look up users by keying in their *User IDs*
(internal to your system), and override the price and currency. This is helpful in edge cases where a particular user may have
been served a price that they were not expecting, and subsequently create a support request. Instead of escalating the request
to their pricing team, support team members with the right privileges can assign them another price.
* **Support(read-only):**: Read-only support roles is best suited for L3 support.They also have access just to the `Users`
page on the dashboard. They can key-in *User IDs* to look up prices for specific users, but cannot change those prices.
# Paywall JS SDK Integration
Source: https://docs.corrily.com/integrations/02_paywall-integration
Use Corrily SDK to render a complete Paywall with plans & prices coming from Corrily
Corrily no-code [Paywalls](/paywall-builder/configure) are used to minimize effort required for Pricing Page implementation
by providing ready-to-use front-end Paywall component.
Simply install Corrily SDK and render the complete paywall with a few lines of code.
Please, read [Paywalls Overview](/paywall-builder/configure) before following the integration guide below.
Corrily Paywalls works through a ReactJS SDK.
If your use another framework, please, send implementation request to **[hello@corrily.com](mailto:hello@corrily.com)**.
### Integrating ReactJS SDK
1. Install `@corrily/react-sdk` package:
```bash theme={null}
cd front-end
npm i --save @corrily/react-sdk
```
For React version lower, than 17.0.2:
```bash theme={null}
npm i --save @corrily/react-sdk --legacy-peed-deps
```
2. Get Corrily API Key from [Resources page](https://dashboard.corrily.com/resources) and store it in `.env.local`:
```
REACT_APP_CORRILY_API_KEY=...
```
3. Embed Paywall into your code:
```typescript theme={null}
import { CorrilyProvider, Paywall } from '@corrily/react-sdk';
import '@corrily/react-sdk/style.css';
// Corrily API Key
const CORRILY_API_KEY = process.env.REACT_APP_CORRILY_API_KEY as string;
export const App = () => {
// For authenticated users, provide user_id
// For unauthenticated, set userId to null
const userId = 'test-user-id';
// For authenticated users, provide country User belongs to
// For unauthenticated, use IP address to let Corrily guess country by IP
const country = "US";
const params = {
user_id: userId,
country: country,
};
return (
);
}
```
4. Define "on-click" behaviour for selected product in `ProductList.tsx`. To do that replace `` with `PaywallWrapper` in the code above:
```typescript theme={null}
return (
);
```
and define `PaywallWrapper` component itself:
```typescript theme={null}
export const PaywallWrapper = () => {
const handleProductSelected = async (product: Product) => {
// Redirect to signup page or send API call to create Stripe Checkout Session
fetch(`https://example.com/api/create-checkout-session`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
product_api_id: product.api_id,
// userId and country of your authenticated user
user_id: userId,
country: country,
success_url: "https://example.com/success",
cancel_url: "https://example.com/cancel",
}),
})
.then(async (response) => {
const res = await response.json();
// Assume checkout session created on back-end, and it's URL returned in response
window.location.href = res.session_url;
})
.catch((err) => {
console.error(err);
});
};
return (
);
};
```
## Customization
### Applying custom styles to Paywall elements
Use custom CSS styles to change the appearance of Paywall elements.
All elements inside Paywall have `corrily--` prefix to separate Paywall classes from your front-end classnames.
### Custom Product component
In case you want to change Product card layout, use `CardComponent` property of the ``.
Here is an example:
```typescript theme={null}
import { Product, CardProps } from '@corrily/react-sdk';
const CustomCard = ({
product,
previousProduct,
highlighted,
buttonCaption,
buttonColor,
onProductSelected,
}: CardProps) => {
return (
);
};
export const PaywallWrapper = () => {
...
return (
);
};
```
### Additional Parameters
Under the hood Paywall SDK will send API request to fetch products and calculate prices for a given user.
In some cases you might want pass additional parameters to that API request.
**Common usecases are:**
* send `"dev": true` at the time of testing to disable DB writes
* send `"experiment_id": ...` to enforce a specific experiment being used
* send `"arm_id": ...` to enforce a specific arm
* send `"override": true` to ignore previously stored user price and recompute prices from scratch
Check the full list of parameters available in [API Reference](/api-reference/calculate-price#body-params) in body params section.
Code example with custom params:
```typescript theme={null}
...
export const App = () => {
...
const params = {
user_id: userId,
country: country,
dev: true,
experiment_id: 1234,
arm_id: 5678,
};
return (
...
);
}
```
## Integration Examples
Check the Implementation Examples Repo to see the examples of React SDK usage:
[https://github.com/corrily/integration-examples/tree/main/react-sdk-integration](https://github.com/corrily/integration-examples/tree/main/react-sdk-integration)
# API Integration (Web & Mobile)
Source: https://docs.corrily.com/integrations/03_api-integration
Use Corrily API to display plans & prices on your Website Paywall
## API Keys & Authentication
*How to authenticate different parts of Corrily.*
An API key is needed when calling Corrily's API. API keys can be obtained from the `Resources` tab of the Dashboard. To
use an API key, just pass it using the `X-Api-Key` custom header field when sending a request.
For example, to fetch a price for a user you can use the cURL request below (notice where the API key goes):
```
curl --location --request POST 'https://client.corrily.com/v1/prices' \
--header 'X-Api-Key: ' \
--header 'Content-Type: application/json' \
--data-raw '{
"country": "US",
"user_id": "bob",
"products": [
"monthly",
],
}'
```
# iOS Swift SDK Integration
Source: https://docs.corrily.com/integrations/04_ios-integration
Show Corrily Prices on your iOS in-app Paywall
## Installation
### Swift Package Manager
1. Open your Xcode project settings
2. Go to project level (i.e. not target level)
3. Open tab **Package Dependencies**
4. Press **+**
5. Paste `https://github.com/corrily/ios-sdk.git` (or respective SSH URL) in the top right search field
6. Select desired Dependency Rule, the project you'd like to add the SDK to and press **Add Package**
7. Select `CorrilySDK`, the target you'd like to add the SDK to and press **Add Package**
### Cocoapods
Example:
```ruby theme={null}
pod 'CorrilySDK', :git => 'https://github.com/corrily/ios-sdk.git', :tag => '1.0.0'
```
## Usage
Import the SDK module: `import CorrilySDK`
All public methods are documented, please see details in their apidoc.
### SDK start
Call SDK's `start()` method at app launch time, for example:
```swift theme={null}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
CorrilySDK.start(apiKey: "your_API_key")
return true
}
```
### Request products
```swift theme={null}
CorrilySDK.requestPaywall(paywallApiID: "your_API_ID", userID: nil, country: .UnitedStates, isDev: true) {
guard let response = $0 else {
print("error requesting paywall: \($1)")
return
}
// process response.monthlyProducts and response.yearlyProducts
}
```
### Send charge request
This should be done in the `updatedTransactions` method of `SKPaymentTransactionObserver`. Call the method regardless of the transaction state, example:
```swift theme={null}
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
transactions.forEach {
// here `product` is StoreKit product, probably the one that's being purchased at the moment
// `corrilyProduct` is the corresponding Corrily product
CorrilySDK.requestCharge(
transaction: $0,
product: product,
paywallProduct: corrilyProduct,
userID: nil,
country: .UnitedStates
)
queue.finishTransaction($0)
}
}
```
# Apple App Store
Source: https://docs.corrily.com/integrations/13_platform-app-store
Integrating Corrily with the Apple App Store
Corrilys Apple App Store integration is designed
to work with the App Store's Catalog, In-App Purchases, and Server Notifications.
## App Store Connect App-Specific Shared Secret
The App-Specific Shared Secret allows Corrily to connect with Apple on your behalf.
1. **Generating an App-Specific Shared Secret**
* Log in to App Store Connect
* Navigate to "My Apps" and select your app
* Select "App Information" under the "General" section from the left side menu
* Select "Manage" under the App-Specific Share Secret section from the right side
* Generate and copy your shared secret
2. **Enter the Shared Secret in Corrily**
* Enter the secret in your iOS app settings on the [Corrily Integrations Page](https://dashboard.corrily.com/integrations)
## App Store Connect API
You may use an App Store Connect API Key to import Subscription, Products and Prices from App Store Connect.
1. **Create an App Store Connect API key**
* On App Store Connect, [create a new App Store Connect API Key](https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api) under Users and Access → Keys → App Store Connect API:
The newly created key needs at least the `App Manager` access level
* Download the generated key. You will receive a `.p8` key file.
* Copy the `Issuer ID` (shown above the "Active" table).
2. **Upload the App Store Connect API key to Corrily**
* Once everything is set up in App Store Connect, you need to upload the App Store Connect API key from the previous step to Corrily.
3. **Copy the Vendor Number**
* Then, navigate to the [Payments and Financial Reports](https://appstoreconnect.apple.com/itc/payments_and_financial_reports/) page and coopy `Vendor Number` from the left top corner of the screen.
## Setup server-side Notifications
Corrily needs a way of detecting revenue events coming in from various in-app purchase types
(consumables, non-consumables, auto-renewable subscriptions, or non-renewing subscriptions).
In each case Corrily needs to be informed of any charge
and subscription events relating to products being experimented on.
### Configure App Store Server-side Notifications (preferred)
Setup Apple [server-to-server notifications](https://developer.apple.com/documentation/storekit/in-app_purchase/original_api_for_in-app_purchase/subscriptions_and_offers/enabling_app_store_server_notifications)
with the URL provicded in Corrily Dashboard:
1. Navigate to the App Store Integration settings in Corrily Dashboard > Integrations.
2. Copy the Notification URL from Webhooks tab.
3. Log in to App Store Connect, go to **General** > **App Information** > **App Store Server Notifications**.
4. Paste the full URL from Corrily in both the Production Server URL field and the Sandbox Server URL field.
Corrily supports either Version 1 or Version 2 notifications, but Version 2 is preferred.
If you already have a central location that handles subscriptions and charge events,
you can forward Apple Notifications to Corrily using the same webhook URL.
# Custom integration
Source: https://docs.corrily.com/integrations/15_platform-custom-integration
Detailed guidance on how to integrate Corrily with a payment gateway.
## (Recommended) Provide automated access of subscription and charge events
The easiest and recommended way to notify Corrily of subscription and charge events is to grant Corrily automated access to your payment gateway's webhook events. Email [api@corrily.com](mailto:api@corrily.com) to get started.
If you're using Stripe and would like to get your Corrily integration started now, click the **Connect with Stripe** button to grant Corrily read-only access to your Stripe subscription and charge events.
If for whatever reason you cannot or do not want to grant Corrily automated access, you can manually notify Corrily instead.
## Manually notify Corrily of subscription and charge events
Only required when automated access is not an option
If you have granted Corrily automated access to your payment gateway's webhook events, as explained in the previous section, you can stop here.
"Charge" events is a generic term in this guide
In this guide we use the word "charge" to refer to a non-recurring payment. Different payment gateways use different words for this concept. When reading your payment gateway's documentation, "charge" may not be the word you're looking for.
### The basic workflow
* You set up your own webhook server
* Your webhook server receives subscription and charge events from your payment gateway
* Your webhook server extracts only the data that Corrily needs and sends it to Corrily via the subscription and charge REST API endpoints
Corrily needs to be notified of subscription AND charge events
Corrily needs to know about charge events because charge events are much closer to realized revenue than subscription events. In other words, charges are more like a realized commitment to pay, whereas subscription events are more like an intent to pay. Getting both signals helps Corrily's AI models provide more optimal prices for you.
### Build a webhook server
See the following docs for more guidance on building a webhook server for your particular payment gateway:
#### Stripe
* [Receive event notifications with webhooks](https://stripe.com/docs/webhooks)
* [Using webhooks with subscriptions](https://stripe.com/docs/billing/subscriptions/webhooks)
#### Chargebee
* [Events and Webhooks](https://www.chargebee.com/docs/2.0/events_and_webhooks.html)
* [Webhook Settings](https://www.chargebee.com/docs/2.0/webhook_settings.html)
#### Recurly
* [Webhooks](https://developers.recurly.com/reference/webhooks/)
#### Braintree
* [Webhooks](https://developer.paypal.com/braintree/docs/guides/webhooks/overview)
### Handle subscription events
Payment gateways emit webhook events over the entire subscription lifecycle. Keeping Corrily up-to-date on important events helps Corrily determine optimal prices for your customers. Examples of important events include:
* Signing up for a trial
* Converting to a paying customer
* Switching to a higher or lower price
* Canceling a subscription
The following sections detail which events you need to handle, depending on your payment gateway.
#### Stripe
* [`customer.subscription.created`](https://stripe.com/docs/api/events/types#event_types-customer.subscription.created)
* [`customer.subscription.updated`](https://stripe.com/docs/api/events/types#event_types-customer.subscription.updated)
#### Chargebee
* [`subscription_created `](https://apidocs.chargebee.com/docs/api/events?prod_cat_ver=2#subscription_created)
* [`subscription_activated`](https://apidocs.chargebee.com/docs/api/events?prod_cat_ver=2#subscription_activated)
* [`subscription_cancelled`](https://apidocs.chargebee.com/docs/api/events?prod_cat_ver=2#subscription_cancelled)
* [`subscription_changed`](https://apidocs.chargebee.com/docs/api/events?prod_cat_ver=2#subscription_changed)
#### Recurly
* [`new_subscription_notification`](https://developers.recurly.com/reference/webhooks/#new-subscription)
* [`updated_subscription_notification`](https://developers.recurly.com/reference/webhooks/#canceled-subscription)
* [`canceled_subscription_notification`](https://developers.recurly.com/reference/webhooks/#canceled-subscription)
#### Braintree
* [`Braintree::WebhookNotification::Kind::SubscriptionCanceled`](https://developer.paypal.com/braintree/docs/reference/general/webhooks/subscription)
* [`Braintree::WebhookNotification::Kind::SubscriptionExpired `](https://developer.paypal.com/braintree/docs/reference/general/webhooks/subscription)
* [`Braintree::WebhookNotification::Kind::SubscriptionTrialEnded`](https://developer.paypal.com/braintree/docs/reference/general/webhooks/subscription)
* [`Braintree::WebhookNotification::Kind::SubscriptionWentActive`](https://developer.paypal.com/braintree/docs/reference/general/webhooks/subscription)
* [`Braintree::WebhookNotification::Kind::SubscriptionWentPastDue`](https://developer.paypal.com/braintree/docs/reference/general/webhooks/subscription)
### Send subscription data to Corrily
Use Corrily's [Create subscription](../api-reference/subscriptions/create-subscription) and [Update subscription](../api-refernce/calculate-price/update-subscription) REST API endpoints to send the data from your webhook server to Corrily.
#### Handle subscription discounts
If you are providing a discount to a user, you should send Corrily the price that the user paid. You should not try to represent the discount with multiple calls to the Corrily API.
#### Map your payment gateway's subscription statuses to Corrily's values
The following tables explain how to map your payment gateways status values to Corrily's values.
##### Stripe
| Stripe's value | Corrily's value |
| -------------------- | --------------- |
| `incomplete` | `pending` |
| `incomplete_expired` | `pending` |
| `trialing` | `trialing` |
| `active` | `active` |
| `past_due` | `active` |
| `canceled` | `canceled` |
| `unpaid` | `canceled` |
##### Chargebee
| Chargebee's value | Corrily's value |
| ----------------- | --------------- |
| `future` | `pending` |
| `in_trial ` | `trialing` |
| `active` | `active` |
| `non_renewing` | `canceled` |
| `paused` | `canceled` |
| `canceled` | `canceled` |
##### Recurly
| Chargebee's value | Corrily's value |
| ----------------- | --------------- |
| `future` | `pending` |
| `in_trial ` | `trialing` |
| `active` | `active` |
| `expired` | `canceled` |
| `canceled` | `canceled` |
### Braintree
| Braintree's value | Corrily's value |
| ----------------- | --------------- |
| `Active` | `active` |
| `Canceled` | `canceled` |
| `Expired` | `canceled` |
| `PastDue` | `active` |
| `Pending` | `pending` |
### Handle charge events
Keeping Corrily up-to-date on important charge events helps Corrily confirm that the price it generated for a visitor was optimal. For example, a new subscription is a slight signal that a price was optimal. When the customer allows their card to be charged for the subscription, that's further confirmation that the price was optimal.
The following sections detail which charge events you need to handle, depending on your payment gateway.
#### Stripe
* [`invoice.paid`](https://stripe.com/docs/api/events/types#event_types-invoice.paid)
Stripe refers to one-time charge events as invoice events.
Why not use Stripe charges instead?
Stripe invoice events are closer to payments for individual subscriptions than Stripe charges are. In Stripe, charges pay for invoices which may contain payments for more than just a single subscription. If however it's easier for you to handle charges, you can also look to handle Stripe's `charge.captured`, `charge.refunded`, `charge.updated`, and `charge.succeeded` events.
#### Chargebee
* [`payment_succeeded`](https://apidocs.chargebee.com/docs/api/events?prod_cat_ver=2#payment_succeeded)
* [`payment_refunded `](https://apidocs.chargebee.com/docs/api/events?prod_cat_ver=2#payment_refunded)
On payment events (which pay for subscriptions) Chargebee sends your webhook server transaction, customer, and subscription objects. The transaction object contains the amount that was paid (or refunded, depending on the transaction `type`). The transaction object contains the data that needs to be passed to Corrily.
#### Recurly
* [`successful_payment_notification `](https://developers.recurly.com/reference/webhooks/#successful-payment)
* [`successful_refund_notification `](https://developers.recurly.com/reference/webhooks/#successful-refund)
#### Braintree
* [`Braintree::WebhookNotification::Kind::SubscriptionChargedSuccessfully`](https://developer.paypal.com/braintree/docs/reference/general/webhooks/subscription)
* [`Braintree::WebhookNotification::Kind::SubscriptionChargedUnsuccessfully `](https://developer.paypal.com/braintree/docs/reference/general/webhooks/subscription)
### Send charge data to Corrily
Use Corrily's [Create charge](../api-reference/charges/create-charge) and [Update charge](../api-reference/charges/update-charge) REST API endpoints to send the invoice data from your webhook server to Corrily.
#### Handle charge discounts
If you are providing a discount to a user, you should send Corrily the price that the user paid. You should not try to represent the discount with multiple calls to the Corrily API.
#### Map your payment gateway's charge statuses to Corrily's values
The following tables explain how to map your payment gateways status values to Corrily's values.
##### Stripe
| Stipe's value | Corrily's value |
| --------------- | --------------- |
| `draft` | `pending` |
| `open` | `pending` |
| `paid` | `succeeded` |
| `uncollectible` | `failed` |
| `void` | `failed` |
##### Chargebee
| Chargebee's value | Corrily's value |
| ----------------- | --------------- |
| `in_progress` | `pending` |
| `success` | `succeeded` |
| `voided` | `failed` |
| `failure` | `failed` |
| `timeout` | `failed` |
| `needs_attention` | `failed` |
##### Recurly
| Recurly's value | Corrily's value |
| --------------- | --------------- |
| `pending` | `pending` |
| `processing` | `pending` |
| `success` | `succeeded` |
| `chargeback` | `succeeded` |
| `error` | `failed` |
| `declined` | `failed` |
##### Braintree
| Braintree's value | Corrily's value |
| ------------------------------------------------------------------------- | --------------- |
| `Braintree::WebhookNotification::Kind::SubscriptionChargedSuccessfully` | `succeeded` |
| `Braintree::WebhookNotification::Kind::SubscriptionChargedUnsuccessfully` | `failed` |
# Funnel Analytics based on Billing Data
Source: https://docs.corrily.com/metrics/02_funnel_analytics_billing_data
Learn about the definitions of Funnel Analytics metrics based purely on Billing Platforms Data
# Corrily Analytics Modes explained
Corrily Platform provides multiple subproducts:
1. Price Management Dashboard and API
2. Paywall Builder (No-Code Paywalls)
3. **Analytics Platform**
4. Experimentation Platform
Corrily Analytics Platform aims to provide you with deep insights about your subscription metrics and company performance. It can be used separately from the rest of the Platform.
Corrily Analytics Platform can be used in two modes:
* `Billing Data Analytics` (active by default and would work once you connect Stripe, Recurly, Chargebee or other billing platforms)
* `Billing & Users Data Analytics` (provides more advanced analytics over your user flow, but requires you to start using [Corrily Price Management API](/integrations/03_api-integration) or Corrily [Web-](http://localhost:3000/integrations/02_paywall-integration) or [Mobile-](http://localhost:3000/integrations/04_ios-integration) Paywalls)
This article explains `Billing Data Analytics` metrics.
To learn more about `Billing & Users Data Analytics` mode, please go to [this page](/metrics/03_funnel_analytics_corrily_data).
***
# Billing Data Analytics Metrics Definition
All the metrics below are calculated based on the Billing Platform Data.
Typically, Billing Platforms provides Customers, Subscriptions, Invoices and Transactions data,
which is used to compute the metrics below.
## Conversion Metrics
### Number of New Subscribers
Number of Customers who are starting a subscription for the first time, either a free, trialing or paid subscription
### Number of New Trialing Subscribers
Number of Customers who are starting trials
### Number of New Paying Subscribers
Numbers of Customers who started a paid subscription for the first time
### Trialing to Paying Subscribers Conversion Rate
Percentage of trialing subscribers converting to paid subscribers
## Revenue Metrics
### Monthly Recurring Revenue (MRR)
Total recurring revenue every month.
MRR is a measure of the predictable and recurring revenue components of the subscription business.
It is a critical metric for subscription-based companies as it provides insight into the financial health
and trajectory of the company.
### Monthly Recurring Revenue Changes
Changes in MRR, including new MRR, Expansion, Contraction, and Churn
### Average Revenue Per Subscriber (ARPS)
MRR, divided by the number of active subscribers
### Average LTV per Subscriber
Expected lifetime value of a subscriber from sign-up to churn
```
LTV per Subscriber = ARPS / Percentage of Subscribers Churning
```
LTV is a prediction of the net profit attributed to the entire future relationship with a customer.
It is important for understanding the long-term value of customer relationships.
## Retention Metrics
### Number of Active Subscribers
Number of Subscribers, active by the given date
### Number of Subscribers Churning
Number of subscribers canceling their subscription
### Percentage of Subscribers Churning
Percentage of active subscribers cancelling their subscriptions
### MRR Churn
MRR impact of churning subscribers
### Percentage of MRR Churning
Percentage of total MRR churning
# Funnel Analytics based on Users and Billing Data
Source: https://docs.corrily.com/metrics/03_funnel_analytics_corrily_data
Learn about the definitions of Advanced Funnel Analytics metrics based on both - Billing Platforms Data and Corrily Users Data
# Corrily Analytics Modes explained
Corrily Platform provides multiple subproducts:
1. Price Management Dashboard and API
2. No-Code Paywalls
3. **Analytics Platform**
4. Experimentation Platform
Corrily Analytics Platform aims to provide you with deep insights about your subscription metrics and company performance. It can be used separately from the rest of the Platform.
Corrily Analytics Platform can be used in two modes:
* `Billing Data Analytics` (active by default and would work once you connect Stripe, Recurly, Chargebee or other billing platform)
* `Billing & Users Data Analytics` (provides more advanced analytics over your user flow, but requires you to start using [Corrily Price Management API](/integrations/03_api-integration) or Corrily [Web-](http://localhost:3000/integrations/02_paywall-integration) or [Mobile-](http://localhost:3000/integrations/04_ios-integration) Paywalls)
This article explains the advanced `Billing & Users Data Analytics` mode and it's metrics.
To learn more about `Billing Data Analytics` mode, please, go to [this page](/metrics/02_funnel_analytics_billing_data).
***
# Users and Billing Data Analytics Metrics Definition
Once you'll start using [Corrily Price Management API](/integrations/03_api-integration) or [Corrily Paywalls](/paywall-builder/configure),
you'll be able to track Users metrics, such as Signup Conversion Rate, Average Revenue Per User, e.t.c.
Each of these metrics plays a vital role in analyzing the performance of a business,
particularly those that operate on a subscription model or rely on website traffic for conversions and sales.
They can help in making informed decisions about marketing, product development, customer service,
and overall business strategy.
## Conversion Metrics
### Number of Visitors
The total number of unique users who visit your pricing page.
### Number of Signups
The count of new Users who have registered for an account on your website or platform.
Sign-ups are tracked by counting the number of completed registration forms or account creations. This metric is important for understanding the initial interest and the effectiveness of the registration process.
### Number of New Trials
The count of users who begin a trial period for your service.
This is tracked by counting the number of users who have started a free or discounted trial period.
It's an important indicator of potential future paid users.
### Number of New Paying Users
### Sign-up Conversion
The percentage of visitors who sign up for an account out of the total number of visitors.
$\text{Visitor to Sign-up Conversion Rate} = \left( \frac{\text{Number of Sign-ups}}{\text{Total Number of Visitors}} \right) \times 100\%$
This metric measures the effectiveness of the site in encouraging visitors to sign up and is calculated by dividing the number of sign-ups by the total number of visitors and then multiplying by 100 to get a percentage.
### Sign-up to Trial Conversion
The percentage of users who start a trial after signing up.
$\text{Sign-up to Trial Conversion Rate} = \left( \frac{\text{Number of New Trials Started}}{\text{Number of Sign-ups}} \right) \times 100\%$
This conversion rate helps businesses understand how well they are converting new sign-ups into trial users.
### Trial to Paid Conversion
### Sign-up to Paid Conversion
The percentage of sign-ups that convert to paying customers.
$\text{Sign-up to Paid Conversion Rate} = \left( \frac{\text{Number of New Subscribers}}{\text{Number of Sign-ups}} \right) \times 100\%$
This metric is crucial for understanding the effectiveness of the conversion funnel from initial interest to paid subscription.
## Revenue Metrics
### Average Revenue Per Visitor (ARPV)
The average revenue generated per visitor to the website.
$\text{ARPV} = \frac{\text{Total Revenue}}{\text{Total Number of Visitors}}$
ARPV is calculated by dividing the total revenue generated in a time period by the total number of visitors in the same period. This metric helps in understanding the value each visitor brings to the business.
### Average Revenue Per User (ARPU)
The average revenue received from each active user over a certain period.
$ \text{ARPU} = \frac{\text{Total Revenue}}{\text{Total Number of Users}}$
ARPU is a measure of the revenue generated per user and is calculated by dividing the total revenue by the total number of users over a specific time frame. It's an important metric for evaluating the revenue impact of user growth.
### Average Revenue Per Paid User (ARPPU)
The average revenue generated from each paying user.
$\text{ARPPU} = \frac{\text{Total Revenue from Paid Users}}{\text{Total Number of Paid Users}}$
ARPPU focuses on the revenue from paying users only, excluding free or trial users, by dividing the total revenue from paid users by the number of paid users.
### LTV (Average LTV per User)
The total revenue a business can expect from a single User throughout their relationship.
$\text{LTV} = \text{ARPU} \times \text{User Lifetime}$
`LTV` is a prediction of the net profit attributed to the entire future relationship with a customer.
It is important for understanding the long-term value of customer relationships.
### Paying Users Life Time Value (Average LTV per Paying User)
Average total revenue a business can expect from a single Paying User
throughout their relationship (from paid signup to churn).
$\text{ALTVPPU} = \text{ARPPU} \times \text{Paid User Lifetime}$
# Paywall Builder
Source: https://docs.corrily.com/paywall-builder/configure
Corrily Paywalls Overview
## Dynamic and Personalized Paywalls
Corrily's no-code Paywall Builder allows subscription companies to personalize, segment and experiment with their
pricing pages without writing a single line of code. Once you integrate Corrily's Paywalls on your website or in-product,
you can continuously optimize them to increase conversion and revenue.
Corrily Paywalls are a great way to save engineering time and use fully-managed, pre-built paywalls - allowing
PMs, Growth Teams, Marketers, Pricing Managers and others to customize the design, segment users, and experiment with every aspect of the paywalls and pricing pages.
## What is a Corrily Paywall?
Instead of showing “hardcoded” one-size-fits-all paywalls or pricing pages that show the same prices, plans, features to all your users, you can use Corrily's Paywall to make your paywalls or pricing pages fully dynamic and personalized. Corrily Paywalls allow
you to easily change:
* Plans included on the paywall
* Features associated with each plan
* The feature comparison table
* Prices and currencies for the plans
* Design aspects - such as call-to-action, highlighted plan, feature callouts etc.
## Key Features of the Corrily Paywalls
* **WYSIWYG Interface**: Corrily's no-code WYSIWYG interface allows for easy visualization of changes so that even
those without technical expertise can design and implement a new paywall.
* **Realtime Updates**: Changes made to the paywalls can be pushed live immediately without any engineering cycles.
* **Paywall Segmentation**: Create unlimited number of paywall and pricing page variants. Use paywall segmentation
capabilities to show different paywalls to users across different countries and audiences.
* **Easy Experimentation**: Run targeted A/B tests and use in-built analytics to not only understand which ones
convert better, but also the revenue and LTV impact.
* **Seamless Integration**: Works effortlessly with Stripe, Recurly, and Chargebee, with more integrations coming soon.
## How a Corrily Paywall Works:
* **Design Your Paywall**: Use the WYSIWYG interface to design your paywall.
* **Set Segmentation Rules**: Use Corrily's Paywall segmentation capabilities to show different versions of the paywall
based on user's country or other user attributes.
* **Run Experiments**: Conduct A/B tests to experiment with different designs, plans, features, prices, or calls-to-action.
* **Analyze and Optimize**: Use Corrily’s analytics tools to understand conversion rates, user behavior, and more.
Optimize your paywall based on these insights.
## Integrating a Paywall on to your website
### Creating a Paywall
* Navigate to: **`Product Catalog`** > **`Paywalls`** on your Corrily dashboard.
* Give your paywall a descriptive name - for e.g. Default Pricing Page or Pricing Page Design Variant etc.
* Choose the optional channel and type of paywall
* Select the package you would like to use for the paywall. A package is a combination of products and offers that
will be rendered on the paywall. If you don't have a package created already, please make sure you create the package
you would like to use for the paywall.
You can also create Dynamic Paywalls using Corrily. If you don't select a Default Package when creating
a paywall, and leave it empty, Packaging Segmentation rules will be used to decide which package to show
for that paywall.
In practice, it is recommended to create a dedicated paywall for each package that you want
to show to your users so you can customize the design based on the plans being shown.
### Segmentation rules for Paywalls
* To create segmentation rules for your paywalls, click on `Add Paywall Segmentation`
* Select the `country` and `audience` for which you want to create the segmentation
* Select the paywall you would like to show users who match the country and audience criteria.
### SDK Integration
Corrily Paywalls works through a JS SDK.
If your JS framework is not supported, you can send an implementation request to **[hello@corrily.com](mailto:hello@corrily.com)**.
👉 Check out [Paywall Integration Guilde](/integrations/02_paywall-integration) for more details.
## Further Information
💡 Want to get a deeper dive into how to leverage Corrily’s no-code paywall builder for your business?
Book a call here: [https://www.corrily.com/schedule-demo](https://www.corrily.com/schedule-demo)
# Country Prices
Source: https://docs.corrily.com/prices/01_country-prices
Setting prices across countries
You can use Corrily to set prices in specific countries. As an example, let's say we wish to serve a local price for
user coming in from Japan (`JP`). This can be done by going to the `Prices` tab and setting local prices for
products in Japanese Yen (`JPY`).
### How Corrily decides where a user is from
When calling Corrily's API you can either:
* Pass an explicit two-letter country code to the `country` parameter.
* Pass an IPv4/6 address to the `ip` parameter which Corrily resolves to the correct country.
* Pass a `get_ip_from_request:true` parameter which lets Corrily know to find the IP address in the `X-Forwarded-For` header field.
# Audience Prices
Source: https://docs.corrily.com/prices/02_audience-prices
Setting prices for segments of users using audiences.
Corriy can also help you set audience specific prices. For e.g., let's say you have created an audience called
`Students`. On Corrily's `Prices` section under `Product Catalog`, you can then set specific prices for
this particular audience. You can enable these audience prices for all or some specific countries. For e.g. `Students` in the US
can be shown specific prices in USD, whereas `Students` in Mexico can be shown another pries in Mexican Pesos ('MXN')
How to set Audience Prices in Corrily
* On the [Corrily dashboard](http://dashboard.corrily.com/), navigate to `Product Catalog` ->`Prices`.
* Navigate to the Audience Prices tab, and click on `Add Audience Prices`.
* Select the product and countries you want to generate these audience prices for.
* Audience prices will inherit respective country prices be default, but can be changed using Pricing Bulk Actions before setting
final audience prices.
* If you no longer need audience segmentation for prices, you can delete these audience-specific prices.
# Experimental Prices
Source: https://docs.corrily.com/prices/03_experiment-prices
Setting experimental prices.
When a user falls under an active experiment, they can be served one of the pre-defined experiment treatment prices,
or a personalized price.
Few things to note about how Corrily treats experimental prices:
* Corrily's machine learning models determine what price a user gets assigned
* A uniquely identifiable user, whether using an `IP` or a `custom ID` is locked to an experiment treatment i.e. as long
as Corrily can identify them, they will be served the same experiment treatment.
* Certain experiment prices may be disabled during an active experiment if they are under-performing (when traffic allocation
is set to automatic)
* To check which users have been assigned what prices, anyone with `support` or higher privileges on the Corrily dashboard
can key in the `User ID` on the *Users* page to see what experiment treatment they were assigned.
* You can edit and override a user's price here so they no longer see an experimental treatment
* For any user, when you call Corrily with the `override:true` flag, it resets the old price and a new price may be assigned
at that point in time. The flag is local to the call i.e. next time you wish to reset a user's price, you would need to pass the
flag again.
* Once an experiment is completed, experimented-on users will continue seeing control or experiment prices. You can move them
to a new price using Corrily's dashboard.
# Price Rounding
Source: https://docs.corrily.com/prices/05_price-rounding
Applying rounding strategies
### Applying rounding to base prices to quickly populate country prices
Rounding can be applied on the dashboard when populating country prices. If you know the base price of a product and want
to quickly localize its price in local currencies and across different countries, you'll need to decide how best local prices
should be rounded. These "bulk" actions can be launched in the **Prices** section of the dashboard and generally
follow the same rules as the rounding strategies described in the below section.
### Applying rounding strategies to ephemeral prices
[Ephemeral prices](prices/04_ephemeral-prices) are often not well rounded, and need to have rounding applied to them.
For example, a monthly product selling at `9.99` will be priced at `7.4925` after a 25% coupon application. To bring
this price back to looking nice a rounding strategy can be applied to shift it to a nice number like `7.49` or `7.50`.
The rounding strategies Corrily supports are governed by a simple language which is best described through example.
Imagine you want to round up to `.25`, `.49`, `.75` or `.99` for prices between 0 and anything less than 100 and for
anything equal to 100 or more you want to round to the nearest multiple of 50. A rounding rule to define this would then
be:
* `0_<100:f25_f49_f75_f99_nearest|>=100_inf:multiple_50_up`
Notice how the rule is split into segments delimited by `|` each of which contains a range `0_<100` and `>=100_inf` followed by
`:` and a **range body** which can either be:
* a sequence of numbers to round to (`f25_f49_f75_f99` where `f` denotes a float, i.e. the ability to round to say `4.59`)
* a function such as `multiple_50` (the other important function is `sigfig_` which rounds to the nearest number of
significant figures.
Finally, each range body must end with either `_down`, `_up` or `_nearest` which defines whether numbers are rounded down, up or to the nearest number defined in the **range body**.
# FX Rates
Source: https://docs.corrily.com/prices/06_fx-rates
Setting and updating FX rates in Corrily.
If you are billing users in multiple currencies, FX rates are critical since they can affect the revenue collected. Let's say
your base currency is `USD` and you display and charge users in Europe in `Euro`. With Corrily, you can either use
`Fixed FX rates` or `Floating FX rates` to arrive at the final display price. When using `Floating FX rates`, Corrily
also let's you decide the frequency at which you want to update the FX rates. You can manage your FX rates and monitor
FX rate movements in the `Prices` section under `Product Catalog`.
To give an **illustrative example**, let's say the price of a product is **100 USD** (your base currency) and you were simply
doing currency conversion to arrive at the corresponding price in Euro(without doing any purchasing power parity adjustments).
If you fixed your `USD-EUR` currency pair FX rate (at **0.87** on Jan 1, 2022), you would have fixed your price at 87 Euros in Europe.
On Oct 31, 2022, that would have meant you recognized **86.5 USD** in revenue for a new purchase of the product.
That's a 12.5% hit on your revenue. Corrily helps you monitor and adjust your prices in local countries based on FX movements.
# Features
Source: https://docs.corrily.com/products/features
Creating and managing product features in Corrily dashboard
A `Feature` is a smallest piece of functionaly, that you want to sell your customers.
Every product may be represented as a set of features.
You can combine features in different combinations to form different products.
In Corrily features can be of two types:
* `Boolean` - a flag, which singlizes that a particular functionality is included into a product;
* `Numeric` - a flag with a threshold value, that allows you to limit consumption of a resource.
For a numeric feature you can create many threshold values, including "Unlimited" threshold value;
## Creating a Feature
* navigate to `Product catalog` > `Features` on your Corrily dashboard
* click on `Create Feature` button
* Fill the form
If you want to create a numeric feature, then you will need to specify measuring unit (for example: Gb, RPS, ...) and
a list of threshold values.
After feature is created, you can attach it to a product (see [product configuration page](/products/products_configuration)).
Features allow you to create detailed descriptions of your products for paywalls.
**Example:**
# Packages
Source: https://docs.corrily.com/products/packages
Combining products into packages
`Packages` is a way to group products to manage them together.
For example, you want to conduct an experiment and show for a one group of users 3 products and for another group - 4 products.
You can leverage packages for it. You will need to create two packages with different sets of products and then you will be able
to get via [calculate price API](/api-reference/calculate-price) by `package_id` different groups of products with all their information.
## Creating a Package
* navigate to `Product catalog` > `Packages` on your Corrily dashboard
* click on `Create Package` button
* Fill the form
This package is intended to show 3 products (3 monthly and 3 yearly).
After a package is created, you can use it's api\_id in your [calculate price](/api-reference/calculate-price) requests
or you can use packages in paywalls to choose which products should be shown.
# Products
Source: https://docs.corrily.com/products/products_configuration
Creating and managing products in Corrily dashboard
In Corrily, a `Product` represents a sellable item, such as a subscription or a one-time purchase,
that can be offered to users. It is typically mapped 1:1 to plans or add-ons from billing platforms and
can be configured with various parameters like base price, billing interval, currency, and country-specific prices.
## Creating a Product
* navigate to `Product catalog` > `Products` on your Corrily dashboard
* click on `Create Product` button
* Fill a multi-step form
**Step 1**
On the first step you need to fill all base information about your product.
This information you will be able to get later via `calculate price` API or via `Paywall integration`.
**Step 2**
On the second step you can set up trial configuration for your product.
**Step 3**
On the third step you can enter information to bind you Corrily product with a product from your payment system.
Also, you can create products via [API](/api-reference/create-product)
### Product features configuration
After a product is created, you can attach `Features` that that product. To do so, click on the product name and
you will see a product information page. On that page there is `Product Features` section.
By clicking on an item in features list, you can attach chosen feature to a product.
# Security and Compliance
Source: https://docs.corrily.com/security_privacy/00_security_compliance
Security and Compliance at Corrily
We understand that pricing is one of the most crucial elements of your business, and handing pricing information to a partner
like Corrily requires utmost trust in our security, data privacy and compliance procedures.
Corrily's promise to you
At Corrily, we take data integrity and security very seriously. Due to the nature of the product and service we provide,
it is important that we acknowledge our responsibilities both as data controller as well as a data processor. We store and
process your data and that of your customers with care and help you be compliant so that you can continue to build trust
while enhancing customer experiences.
Our infrastructure, processes and systems are reliable, robust and are being third-party tested. We continuously look for
opportunities to make improvements and give you a highly secure, and scalable experimentation and price management platform.
Here are some of the concrete actions we have taken:
* Our infrastructure, software applications, policies etc. are being continuously monitored using Vanta. This allows us
to detect and address any discrepancies pro-actively
* We secure your customers' pricing information by staying compliant with GDPR. Moreover, we do not store any personally identifiable
information.
* We adhere to strict ISO and SOC 2 standards to ensure security of your data that rests with Corrily
* We force HTTPS for all services using TLS (SSL), including our public website and the Dashboard to ensure secure
connections. We regularly audit the details of our implementation, including the certificates we serve, the certificate
authorities we use, and the ciphers we support.
* Implementing network, application and operational level security policies. We are currently working with a security assessmgnet
vendor to conduct an independent penetration testing and will make the results available shortly
# GDPR Compliance
Source: https://docs.corrily.com/security_privacy/01_GDPR
Corrily's GDPR Commitment
Corrily only collects and stores information that is necessary to offer our service, and we do this only with the consent of
our customers. Our approach towards privacy, security, and data protection align with the core tenets of GDPR.
Corrily takes Data Protection seriously - we have completed a comprehensive review of our operations to ensure that
we are able to meet our obligations under all applicable regulations in all territories. We have **completed a GDPR
implementation** project to ensure we have all required policies and procedures in place, and that we respect the rights
of our data subjects at all times. We have documented policies and procedures covering all aspects of compliance including:
Privacy Policy, SAR Policy, Data Breach Policy, Information Security Policy and have adopted the GDPR DPA document as
standard.
Additionally, we have appointed an **Article 27 Representative** and have established relationships with data protection
consultants to ensure we meet our ongoing obligations and operate to the highest level of compliance at all times.
# Audiences
Source: https://docs.corrily.com/segmentation/01_audiences
Segmenting users using audiences.
A successful pricing strategy needs to look beyond a one-size-fits-all strategy. **Corrily Audiences** help you get a deeper
understanding of how your user base is reacting to your current pricing and packaging, any experiments that you run,
or any price or packaging changes that you roll out. For e.g. your link shortener subscription service may generally be
seeing higher churn amongst the `freelancers` audience segment, and `boutique creative studios` may have higher willingness
to pay for your products.
Corrily Audiences can not only help you uncover these insights, you can also use these insights in your price segmentation
strategy.
A Corrily Audience can consist of any number of custom attributes. For e.g. you can create an audience called
`Young students`, using a combination of attributes `Age` and `User Type`.
To create a new Corrily Audience:
* Navigate to `User Management` -> `Audiences`
* Click on `Create Audience`
* Give the audience a name, such as "Young Students"
* Choose the relevant Attributes (these need to be pre-defined)
* Select the desired matching function
* Specify Attribute value
# Attributes
Source: https://docs.corrily.com/segmentation/02_attributes
Creating attributes which combine to make up an audience.
You can use **Corrily Attributes** to build the audiences that see your experiments or specific prices, or to simply
analyze how well your current pricing and packaging strategy is working. Attributes can either be a `string`,
a `number` or a `boolean`. You can use the Attributes tab under Audiences to create and manage all your user attributes.
When you create a new audience, you can choose different **matching functions** depending on the Attribute format viz.
comparison operations like `greater than` or `equal to` for *number*, `starts with` for *string* and `true/false` for *boolean*
To create a new attribute:
* Navigate to `User Management` -> `Audiences` -> `Attributes`
* Click `Create Attribute`.
* Enter a name, description and a key for the attribute. For example, an attribute to describe different kinds of users
on your platform might be called "User Type" (*string*), or an attribute to describe the length of the users on your platform
may be called "User Length" (*number*)
# Users
Source: https://docs.corrily.com/users/01_users
Users in Corrily.
One of the first things you need to do before using Corrily is to decide on a long-term system for identifying users.
Without stable user identifiers (IDs), Corrily will not be able to learn from the results of the price experimentation.
In other words, Corrily needs user IDs so that it can associate the following events together:
* The price that Corrily generated for this user (and you subsequently displayed to the user)
* Whether or not the user subscribed to your service, purchased an item, cancelled or renewed their subscription and so on
## Types of IDs
### Custom IDs
If your system uses any kind of stable, long-term ID system, such as UUIDs, you can provide that ID to Corrily.
See Segment's description of [User ID](https://segment.com/docs/connections/spec/identify/#user-id) for more general discussion of this concept.
### IP addresses
You can use IPv4 or IPv6 addresses to represent users. If you call Corrily's API endpoints directly from a client device, Corrily can derive the user's IP from the API request.
# User Features
Source: https://docs.corrily.com/users/02_user-features
Associating features with users in Corrily.
Corrily can take user features other than location into account when it calculates experimental prices. A feature is any attribute of the user that you think may affect the optimal price for that user.
## Supported values for user features
Any single-leveled object of key-value string pairs is valid user features data. Example:
```json theme={null}
{
"platform": "ios"
}
```
The values can be strings, integers, or floats.
You can provide whatever keys and values you want. The keys and values have no intrinsic meaning to Corrily.
To Corrily these are simply labels for training its AI models. For example, suppose that you provide a `platform`
key every time you calculate a price. Sometimes the value is `ios`, sometimes it's `android`, and sometimes it's `web`.
If the users who have their `platform` field set to `web` consistently convert at a higher price more than the `ios` and
`android` users, Corrily takes this data into account when calculating future prices for any user with `platform` set
to any of these 3 fields. The `web` users would probably be presented higher prices, and the `ios` and `android` users
would probably be presented lower prices.
The important thing is to consistently provide the user features data as much as possible when calculating prices. In other words, you need a consistent set of keys, and each key needs a consistent set of values. Corrily's AI models can't learn if the keys and values are constantly changing.
## How to set user features
* Use the `features` parameter of the [calculate price](../api-reference/calculate-price) API endpoint when you want to take into account temporary or frequently changing factors that may affect the user's optimal price.
* Use the [set user features](../api-reference/set-user-characteristics) API endpoint.
# Handling Anonymous Users
Source: https://docs.corrily.com/users/03_handling-anonymous-users
Handling anonymous user and keeping prices and experiments consistent throughout the user journey.
Corrily supports anonymous users who might one day register and get a stable user\_id.
In general, converted users should always see the same price they saw on their first visit, before sign up.
The flow for anonymous users will require you to send the user’s IP in the request payload or header. Corrily will remember the price displayed to the given IP, and will stick the registered user to this price on the first API call where both `ip` and `user_id` fields are present.
**How the flow works**
1. For anonymous users send an `ip` without any `user_id`:
`{“ip”: “XXX.XXX.XXX.XXX”} -> price1`
2. The `ip` is now tied to the above price. When a user converts and their `user_id` is known, send both their `ip` and `user_id`:
`{“ip”: “XXX.XXX.XXX.XXX”, “user_id”: “user1”} -> price1`
3. After the above call, the user will be tied to `price1`. Requests with only `user_id` will now always be tied to the same price and any anonymous users visiting with the same `ip` likewise.
**Backfilled user case**
In the case of backfilled old users, they will see the price assigned to them. However, if they happen to visit the page anonymously, they might see another price until they log themselves in.