We’ve migrated our documentation to a new site, which means some URLs have changed.
Subscriptions

Subscription Linking

Overview

Comparing entities between the external subscription management system and Piano

In Management + Billing’s most basic flow, the user purchases a "term" to get access to a piece of content called “resource”.

  • For the external subscription management system (or "external system" for short), we will designate the entity comparable to Piano's resource as an “external product”.

  • The external system's entity defining the conditions of access to your product will be called "external term" (not to confuse with Piano's external service term).

  • A subscription to the external term bound to the Linked term is an "external subscription".

Technically, Piano will automatically create a standard resource for each "external product" you define in creating a Linked term. If your Linked term is to work with multiple products, Piano will additionally create a bundled resource to group all the involved standard resources.

You can find the standard and bundled resources created in this way on the Resources page (Publisher dashboard->Manage->Resources page). Please note that we do not recommend editing them manually, as it might cause unpredictable behavior of Composer’s segmentation and other access-related features.

Using a Linked term

Synchronizing your external subscriptions with your Linked term can be charted as follows:

Link-Terms-Integration-3.png

Main scenario

1. The end user explores the client's website integrated with Composer.

2. Composer shows an offer at a certain step of the experience.

3. The end user selects the Linked term in the offer.

4. Piano's frontend sends a JS SDK callback (linkedTermSelected) to a dedicated callback function on the external frontend. The callback contains info about the Linked term and Piano user session that will be used to initiate external checkout for the associated external term.

5. The end user is forwarded to the external system's checkout where they complete their purchase.

6. The external system's backend creates a new subscription to the just-purchased external term.

7. Creating a subscription to the external term triggers sending a call to Piano's Sync API endpoint. The call body specifies the Linked term ID, the action performed with the subscription (this time it is subscription_create) and other information.

8. Piano's backend uses the received call data to create a new subscription to the relevant Linked term.

9. From now on, the external backend will use this endpoint to communicate every action it performs on the external subscription for Piano to do the same with its Linked term subscription. The available actions/events and their use cases are described below.

The end user can see their new Linked term subscription in MyAccount. Clicking Manage will forward them to the relevant subscription management URL which gets exSubId as a mandatory parameter.

Short scenario

There may be situations where you do not use Composer to start the flow: when creating an external subscription yourself or leading end users to checkout without using the Piano offer. For such cases, you can observe your interaction with Piano starting from step 5 of the Main scenario. However, we don't recommend altering the main flow as it affects data exchange and, therefore, restricts the benefits you can get from using the Linked term feature.

Linked term feature integration

Integration overview

The first thing you need to do is create an auxiliary "zero" Linked term to test your frontend and backend configurations in their interaction with Piano. So term creation becomes the first element of feature integration. Your zero term may exist only in your sandbox or be a functional production-env term, either migrated within a catalog or created individually. After the integration is complete, you can create or import Linked terms without configuring any extra settings.

Therefore, your linked term integration includes the following steps:

  1. In your Piano app: Create a "zero" Linked term.

  2. On the frontend: 1) Set up showing the Piano offer on your webpage (with Composer's Show offer action). 2) Implement a callback function handling the linkedTermSelected Piano callback event triggered when an end user selects the Linked term in the Piano offer.

  3. On the backend: Configure calls to publisher/linkedTerm/event updating your Piano Linked term according to the end user's actions with the external subscription.

  4. With Piano support: If you have users already subscribed to your external term, get their subscriptions migrated to Piano (please do it before turning to the Linked term API).

Creating a Linked term

You can create a new Linked term:

  1. In Publisher Dashboard.

  2. Migrated within a catalog.

  3. Through the API.

In Publisher Dashboard

If the feature is activated for your app, you can create a linked term in your Publisher Dashboard by following Manage>Terms>New>Linked term. You will get to the General settings window where you can define:

  • Title of term: the name of your Linked term.

  • Description: an optional description of your Linked term.

  • External term ID: the ID you use to address the Linked term in the external system (used to map the Linked term with a term analog in the external system).* 

  • External product IDs:External products" are entities of the external system accessed by users. If you enter multiple values in this field (hit Enter after each), Piano will create a standard resource for each product and also a bundled resource that will group them.

  • Subscription management URL: a link to the external system where users can manage their subscriptions (similar to Piano’s MyAccount).

Create when ready.

createLT-1.png

You will see the term menu with all the term details editable and a Custom data field where you can add extra properties of your term. In the UI of the Manual tab, you set them as key-value pairs (Value accepts String including JSON string). To manage your custom data in JSON format, you can switch to the Code mode. Please note that the Manual editor supports up to 12 entries after which you can go on adding custom parameters as Code.

custom-data1.png

Please note that our system considers all new Linked term subscriptions to be shared subscriptions with 0 child accounts.

Via Piano API

Linked term configuration endpoint

To create or update a Linked term via API, you can send a call to publisher/linkedTerm/configuration containing:

1) your AID added to the URL and

2) the term settings defined in the JSON-string body.

The properties sent in the call body are as follows:

Field name

Type

Required/Optional

 Description

external_term_id

String

Required

The ID you use to address the Linked term in the external system (used to map the Linked term with a term analog in the external system)*

term_name

String

Required

The name of your Linked term.

description

String

Optional

An optional description of your Linked term.

external_product_ids

String

Required

External products" are entities of the external system accessed by users. If you enter multiple values (separated by a comma), Piano will create a standard resource for each product and also a bundled resource that will group them. Example: "digital_prod,print_sub_access,main_articles".

subscription_management_url

String

Optional

A link to the external system where users can manage their subscriptions (similar to Piano’s MyAccount).

custom_data

JSON object

Optional

The custom data similar to that entered in PD on Linked term creation.

You can find the JSON schema and API call examples for creating/updating a Linked term here.

With term catalog migration

Piano requires a .csv file with the settings of your new Linked terms. The values you need to specify for each Linked term are similar to those passed in the API call body when creating/updating a Linked term (see the table above).

Example of a migration file

external_term_id,term_name,description,external_product_ids,subscription_management_url,custom_data
my_term1,"My first LT term 1","Description,I,want","ltprod1,ltprod2",,"{""my_data"":""my_value""}"
my_term2,"My second LT term","Another description","digital312,pro_pass",,"{""sub_number"":""44574""}"
my_term3,"My third LT term",,"sale_access,ltprod3",,

*If you delete a Linked Term, the same External term ID cannot be used again.

Updating a Linked term

You can update an existing Linked term from the Term menu of your Piano Dashboard or via the API endpoint you used to create your Linked term: publisher/linkedTerm/configuration.

You can find the JSON schema and API call examples for creating/updating a Linked term here.

Please note that you can update the External term ID or the External product IDs of your Linked term only if this term doesn't yet have conversions.

Creating/updating custom fields and their values

When your Linked term is created, you can additionally set up custom fields for its forms using a special endpoint: /publisher/linkedTerm/customField/configuration.

The structure of the request body is:

Field name

Type

Required/Optional

Description

custom_fields

Array

Required

An array containing the configurations (lists of properties as below) of the custom fields for the Linked term.

custom_fields.name

String

Required

The ID of the field.

custom_fields.title

String

Required

The title of the field.

custom_fields.type

String

Required

The type of the field. Allowed values: “text”, “number”.

Find the JSON schema and a request example here.

To update the user's custom field values, include them in the optional user_custom_fields field of your request to the Sync API endpoint (we currently support the subscription_create, subscription_renew, and subscription_terminate events). Received by Piano, such values can subsequently be sent to Piano Audience for user segmentation.

Custom fields are user-level, not subscription-level, data. So, if the same custom field is sent to Piano from two subscriptions of the same user, the latter value overrides the former one.

Please also note that if user_custom_fields contains new values for multiple custom fields and one of them is invalid, other values won't be updated either.  

Frontend integration

In configuring your integration, you want to use JS SDK to register a special callback function. It will handle the linkedTermSelected callback event triggered on Piano's side every time an authenticated end user selects your Linked term in the Piano offer.

This callback function (named onLinkedTermSelected in our code examples) accepts two parameters: session_id and LinkedTerm.

Session ID

Name

Type

Required/Optional

Description

session_id

String

Required

Identifies the user session and, when subsequently passed to Piano with every subscription synchronization API call, will be used to identify this user session in Piano reports and Piano's other systems (Analytics, Audience, Composer C1X). This kind of "session" is not related to user tracking or SSO session handling but exists only in the context of Linked term. The session ID is valid for 3 days after it is generated.

Example: S0ghJ6IRyFVIZqz2rK5OSpi0GN82RIZo

LinkedTerm object

The JSON schema and an example of this object are given here.

Name

Type

Required/Optional

Description

linkedTerm

Object

Required

Contains the data required for the initiation of checkout in the external system.

linkedTerm.name

String

Required

The name of the Linked term.

linkedTerm.description

String

Optional

An optional description of the Linked term.

linkedTerm.linkedTermData

Object

Required

The data object of the Linked term.

linkedTerm.linkedTermData.
externalProductIds

String

Required

A unique combination of the product IDs for the Linked term.

linkedTerm.linkedTermData.
externalProductIds.
subscriptionManagementUrl

String

Optional

A link to the external system where the end user can manage their associated external subscription.

linkedTerm.linkedTermData.
customData

Object

Optional

Custom data of the Linked term.

linkedTerm.linkedTermData.
externalTermId

String

Required

The ID of the Linked term in the external system.

linkedTerm.linkedTermData.
userHasActiveSubscriptionToTerm

Boolean

Required

Indicates whether the end user already has a subscription to this Linked term. If TRUE, you can inform the user that a subscription is already purchased and forward them to another offer.

You can find an example of the callback function for handling the selectedLinkedTerm callback on the external frontend here.

To call an external checkout manually, use the tp.offer.startLinkedTermCheckout(termId, offerId) method.

Where:

  • TermID is the Linked term ID for which you want to start a purchase process.

  • OfferId: The ID of the offer to which the Linked term belongs. An optional parameter.

Subscription synchronization: Sync API endpoint

Events sent with publisher/linkedTerm/event can be divided into three groups according to the action causing the event:

  1. Subscription-related events where the event action creates, updates, or terminates a subscription.

  2. Shared-access-related events where the event action shares or unshares access to a subscription.

  3. Subscription-import event where the event action creates a subscription as a part of import.

While being just variants of the same LT event, they considerably differ in terms of parameters passed, so we describe them separately.

Please note that the endpoint /publisher/linkedTerm/event does not provide details about your Piano subscription. To access subscription details, we recommend using /publisher/linkedTerm/subscription.

The synchronization between subscriptions to terms in external systems and Linked term subscriptions is maintained via Piano API. Every new event in the external subscription is passed to Piano in the body of a call to the Sync API endpoint (publisher/linkedTerm/event) that updates your Linked term subscription correspondingly (starting from the event that creates this very Linked term subscription).

Every subscription-related event passed in the Sync API endpoint specifies:

  • The action to be performed on the Linked term subscription (in this doc and on the examples page, we will distinguish events by their action name, e.g. "subscription_create event")

  • The current state of a subscription (presented as an object)

  • The data of the new conversion (if any)

  • The session ID for Piano's statistics: you get the user session ID by handling the linkedTermSelected callback sent when the end user selects your Linked term in the Piano offer.

For subscription-related events (unlike shared-access ones), our system expects to get the full current state of your subscription rather than just recent changes. So even if you don't need to upgrade certain optional parameters of your subscription, we strongly recommend including such unchanged values in the JSON of your Sync API call. Otherwise, the Piano system would reset such skipped parameters to default.

The subscription-related actions/events include:

  • subscription_create: Creating a subscription and a conversion.

  • subscription_import: Creating a subscription and a conversion without segmentation and reporting 

  • subscripton_upgrade: Creating a subscription and a conversion as a result of an upgrade from another subscription.

  • subscription_renew: Updating an existing subscription with creating a conversion.

  • subscription_update: Updating an existing subscription without creating a conversion. This usually reflects minor changes to the subscription, such as updates to state, valid_to, auto_renew, purchase, trial, access_custom_data, or period.

  • subscription_terminate: Permanently deactivating a subscription as a result of active (the end user's cancellation) or passive (subscription expiration) churn from the external system. The churn type is defined in the "event.subscription.churn.type" parameter of the event (see the table below). Regardless of the external churn type, in Management + Billing such terminated subscriptions will be labelled "cancelled".

subscription_terminate must always be the final event in any Linked Term subscription lifecycle.

Please also note that the subscription_terminate event permanently deactivates a subscription, preventing it from receiving synchronization events or being reactivated. For temporary deactivation or reactivation of your subscription, you can send a subscription_update event to the Sync API with an updated "state" parameter of the "subscription".

Below is the structure of the event represented as a diagram and a table:

sub-related-events.drawio.png

Field name

Type

Required/Optional

Description

event

Object

Required

The external event passed to the Linked term synchronization endpoint.

event.action

String

Required

The action the end user or the external system takes on the external subscription.

(shared_access_grant and shared_access_revoke are described separately)

event.subscription

Object

Required/
NA

The new external subscription.

event.subscription.subscription_id

String

Required

The ID of the new external subscription. Set by the client in the external system. 

event.subscription.user_token

String

Required/
NA

The JWT token of the end user the subscription belongs to.

  • Required when creating a subscription.

  • NA in other cases.

event.subscription.user_id
(deprecated)

String

Optional

The ID of the end user (Piano UID) the subscription belongs to.

event.subscription.product_ids
(deprecated)

Array

Required

The IDs of the external products the end user is subscribed to. Provided by the external system.

event.subscription.external_term_id

String

Required

The ID of the term in the external system. Provided by the external system.

event.subscription.state

String

Required

The state of the subscription: "Active" or "Not active".

event.subscription.valid_to

Integer

Required

The expiry moment of the subscription. Equals zero for unlimited access.

event.subscription.auto_renew

Boolean

Optional

Whether auto renewal for the subscription is enabled. "true" or "false" (case sensitive). If not defined, reset to "false".

event.subscription.purchase

Object

Optional

The purchase data of the subscription. Applicable when the end user acquired the subscription by making a purchase. If not defined, keeps the previous purchase trial state.

event.subscription.purchase.trial

String

Required

The kind of trial applied to the subscription. The allowed values are "Free", "Paid", and "No".

event.subscription.upgrade

Object

Optional/
NA

The upgrade data of the subscription. Applicable when the end user created it via an upgrade from another subscription.

  • Optional when creating a subscription

  • NA in other cases

event.subscription.
upgrade.from_subscription_id

String

Required

The ID of the external subscription the end user upgraded from. Set by the client in the external system.

event.
subscription.access_custom_data

String

Optional

Any custom data that should be linked with the subscription (not to confuse with user_custom_fields). Provided by the external system. If not defined, considered empty.
Can be fetched:
1) As the AccessDTO.custom_data parameter of the response to publisher/user/access/list or publisher/user/access/check API calls, or
2) As the UserSubscription.access_custom_data parameter of the response to /publisher/linkedTerm/subscription/publisher/subscription/get, or /publisher/subscription/list API calls.

event.subscription.period

String

Optional

The billing (or renewal) period of the subscription. Allowed values: 1 week, 4 weeks, 1 month, 1 quarter, bi-annual, annual. If not defined, considered empty.

event.subscription.churn

Object

Optional

Revenue lost because of user churn.

event.subscription.churn.amount

Number

Optional

The amount of the loss caused by the churn.  Estimated by the client. If defined, also requires "currency". 

event.subscription.churn.currency

String

Optional

The payment currency. Currency code defined by ISO 4217. If defined, also requires "amount".

event.subscription.churn.type

String

Optional

Can be either "active" (when the user cancels the subscription) or "passive" (when the subscription expires).

event.subscription.user_custom_fields

JSON

Optional

A list of the end user custom fields. Example:{“field_name”: “value1”, “field2_name”: “value2”} Please note that if at least one custom field has an invalid value, none of the custom fields values will be updated.

event.conversion

Object

Required/
NA

The conversion data in the external system to synchronize with.

  • Required when creating, renewing, or upgrading a subscription.

  • N/A when updating or terminating a subscription.

event.conversion.conversion_id

String

Required

The ID of the conversion to be created. Provided by the external system.

event.conversion.create_date

Integer

Required

The conversion creation date.

event.conversion.payment

Object

Optional

The payment data (if the conversion supposes payment).

event.conversion.payment.amount

Number

Required

The payment amount.

event.conversion.payment.currency

String

Required

The payment currency. Currency code defined by ISO 4217.

event.session_id

String

Optional

The ID of the user session associated with this event, valid for 3 days after it is generated. Passed in the "linkedTermSelected" JS callback (at Step 4 of the Main scenario). Required for Composer conversion report.

You can find the JSON schemas and API call examples for subscription-related events here.

Every time the end user shares (or unshares) their subscription, the external system sends a Sync API event with "shared_access_grant" (or "shared_access_revoke") as an action. One Linked term subscription can be shared with 100 child users max.

A shared-access-related event can be represented as:

Linked-terms-vision-Shared-access-event-SK-1.png

Child accesses within a shared subscription cannot be updated individually and only mimic the state of the parent access.

Field name

Type

Required/Optional

Description

Event

Object

Required

The external event passed to the Linked term synchronization endpoint.

event.action

String

Required

The action the end user or the external system takes on the external subscription:

  • shared_access_grant.

  • shared_access_revoke.

event.session_id

String

Optional

The ID of the user session associated with this event. Valid for 3 days after it is generated. Passed in the "linkedTermSelected" JS callback (at Step 4 of the Main scenario).

event.shared_access

Object

Required

The (un)shared access data.

event.shared_access.subscription_id

String

Required

The ID of the subscription which access will be shared or revoked. Set by the client in the external system.

event.shared_access.user_tokens

Array of strings

Required

The JWT tokens of the users getting or losing the status of a child account of the subscription.

You can find the JSON schema and API call examples for shared-access-related events here.

Subscription-import event

To facilitate the import of Linked term subscriptions from external systems, we have introduced a special event, subscription_import. The API call includes the same parameters as that for subscription_create but does not affect segmentation and reporting. We strictly recommend importing Linked term subscriptions via this endpoint instead of subscription_create.

You can find the JSON schemas and API call examples for subscription-import events here.

Sync API calls for Piano features

 

Audience segmentation by custom fields 

Linked terms allow the collection of the user's custom fields and use them for segmentation in Piano Audience. 

There should be a preconfigured set of custom fields

Custom field values should be sent within subscription events. 

Linked terms in Reports

  1. The New Activity report, Revenue report, Subscription Log report, Subscription Visual report, Management + Billing conversion report includes Linked term conversions by default.

  2. To include Linked term conversions in a Composer conversion report, make sure that the corresponding create event specifies session_id.

Information about Exposures for Linked terms will appear in conversion reports only if the Linked term has been shown in a Management + Billing offer.

Composer segmentation by "Users with accesses"

This feature depends on the availability of an active subscription registered in Management + Billing with a Sync API event. The end user has active access from a subscription_create event till some subscription_terminate event.

C1X segmentation by “Brand Relationship”

Free trial users and Paid trial users

This feature works with subscription_create, subscription_upgrade, subscription_renew, and subscription_update events.

The subscription object in these events should include the optional purchase object to indicate whether your subscription has a free trial or paid trial period. Example:

"purchase": {
    "trial": "Free"/"Paid"
}

Subscribed users

This feature depends on the availability of an active subscription registered in Management + Billing with a Sync API event. The end user has active access from a subscription_create event till some subscription_terminate event.

Churned users

Likehood-to-Cancel user segmentation requires that, after the termination of a linked term subscription, the type of churn (active or passive) be specified among the parameters of the subscription_terminate event (path: event/subscription/churn/type).

C1X segmentation by "Expiring Subscribers"

The user has disabled auto renew

This feature works with subscription_create, subscription_upgrade, subscription_update, and subscription_renew events.

The subscription object in these events should include the optional auto_renew property: first, with a True value and later with a False value - to indicate that subscription auto renew is disabled.

Example:

"subscription": {
    "auto_renew": false
}

C1X segmentation by "Subscription Length"

Weekly-, 4-week-, monthly-, quarterly-, bi-annually- and annually-subscribed users

This feature works with subscription_create, subscription_upgrade, subscription_update, and subscription_renewevents.

The subscription object in these events should include the optional period object to indicate which period the subscription has been converted for. Example:

"period": {
    "length": "1 week"/"4 weeks"/"1 month"/"1 quarter"/"bi-annual"/"annual"
}

C1X segmentation by "Subscription Tenure"

First-24-hour, week-1, week-2, week-3, week-4, month-1-2, month-3-6, and month-6+ subscribers

This feature works with subscription_create and subscription_upgrade events.

The conversion object in this event should include the create_date property to indicate the date the subscription has been converted for the first time. Example:

"conversion": {
    "create_date": <the unix timestamp>
}

C1X segmentation by "Checkout Abandonment"

You can track end users that haven't completed the checkout flow to give them a special offer, collect statistics, or perform other business actions. 

By default, Management + Billing tracks a checkout start when the end user clicks your Linked term and the linkedTermSelected callback is sent. If needed, you can manually configure checkout start tracking using the /publisher/linkedTerm/checkout/trackStart endpoint. Please contact Piano Support to have this endpoint enabled.

Enabling this manual tracking disables the automatic external_checkout_start event triggered by the linkedTermSelected callback.

If a publisher wants to leverage Checkout Abandonment C1X segmentation, they should ensure checkout start is tracked using the /publisher/linkedTerm/checkout endpoint.

List of parameters and attributes:

Field name

Type

Required/Optional

Description

aid

String

Required

The application ID.

external_term_id

String

Required

The ID of the term in the external system. Provided by the external system.

browser_id

String

Required

The unique ID of the browser.

checkout_start_date

Integer positive number

Required

The Unix timestamp of the moment when the end user started the checkout.

Sample request:

curl --location 'https://api.piano.io/api/v3/publisher/linkedTerm/checkout/trackStart?api_token=***' \
--header 'Content-Type: application/json' \
--data '{
  "aid": "your_app_id",
  "browser_id": "lzgmjp72ip97ddry",
  "external_term_id" : "term_name_id",
  "checkout_start_date" : 1744049700
}'

Emails and webhooks

Linked Term Sync API events update the subscription record, write a history entry, and send Piano Analytics events. They do not send webhooks or emails, with one exception: the subscription_import event fires the subscription_created and access_granted webhooks.

User provider integrations

In terms of user management, the Linked term feature can be used in applications with:

  • a direct Identity Management integration

  • Identity Linking with some external user provider (external SSO)

  • Identity Linking and Identity Management as an external user provider  

The Linked term API identifies users using JWT tokens.

Identity Linking + External SSO

End users can either be imported by client users or automatically created during the subscription creation process. A JWT token for the Linked term Sync API is taken from an external SSO or generated by user_id and email from an external SSO. See the detailed process here.

Identity Management

Since the Linked term Sync API can only process existing users, client users have to import or create users in advance. Piano does not support JWT tokens generated by external SSO or other tools. 

The client can create new Management + Billing users via publisher/identity/register (with publisher/register as a deprecated option).  The JWT token is returned in the access_token field of the response.

If the user is already logged in, their JWT token can be obtained via a tp.pianoId.getToken() JS call on the frontend. Alternatively, a new JWT token for any existing user can be generated with a publisher/token API call. Please note that the allowGenerateToken Identity Management setting must be enabled for the app.

Linked terms can be used with Identity Management apps united with Global Mode.

Skipping user auth check when starting external checkout

If you prefer a custom flow, where the user does not have to log in immediately after selecting a term, you can disable user authentication with Identity Linking and PianoID so that the linkedTermSelected callback can be triggered even if the user is not logged in. In this case you need to add:

tp.push(["setRequestUserAuthForLinkedTerm", false]);

before the Management + Billing offer on your integration page.

Last updated: