An offer is a collection of terms. For instance, if the News Access and Sports Access resources each have two terms: $5/mo and $20/yr - one offer that you wanted to display inline might contain only the monthly payment terms from each resource.

Creating an offer is as simple as selecting which terms you want to include in the offer, and when Piano detects that a user doesn’t have access to a resource, that’s when the user is presented with the offer. You’ll be able to make as many offers as you want, and have them display wherever you’d like.

Association with Templates

Offer templates are one of the foundations of the Piano platform. Read documentation here on how to display an offer to your users.

Piano uses angularjs to render templates to end users. This allows you to leverage certain variables and directives that Piano makes available for customization and styling.

Use Cases

You can create as many offer templates as you like. Common use cases for an offer template are:

Dedicated Marketing Page

A common use case for offer templates is the dedicated marketing “Subscribe” page. You can have the entire page, or just a portion of the page, delivered by Piano.

 CNBC Subscribe Page

Metered Paywall Modal

You can use a modal offer template to display a prompt to subscribe after someone has reached the end of their free metered paywall experience.


Premium Story Inline Offer

Another common way to leverage Piano is to display an offer to a user if they reach a premium story and don't have access. In this case, an inline offer template is rendered after the post is truncated.

 CNBC Inline Offer

To create an offer template, go to Manage → Templates → New.

If you call without a templateId parameter, Piano will render the default, unstyled “Default Offer” template, which is located under Manage → Templates → System → Default Offer.


All offer templates are populated with certain variables that you can use to customize the experience for your end users.


If the user is logged in, the user object will be populated with the user's information. If you are performing logic based on whether the user is logged in, Piano recommends using the isUserValid() method.

  "uid": "92837648",
  "email": "",
  "displayName": "John Smith",
  "firstName": "John",
  "lastName": "Smith"

If the user is not logged in, the user object will be populated with the anon user.

  "uid": "anon",
  "email": null,
  "displayName": null,
  "firstName": null,
  "lastName": null


When you iterate over the terms array in an offer template, each term is populated with certain information which can be used to customize the checkout experience further.

  "termId": "TM3F10520FXR",
  "name": "Monthly Subscription",
  "description": "The monthly subscription term description",
  "resource": {
    "name": "Premium Access",
    "rid": "PREMIUMACCESS",
    "url": null
  "displayLine": "$2.99 per month / with trial of $0.99 for 2 months",
  "billingPlanTable": [
      "cycles": "2",
      "isTrial": "true",
      "pricelessBillingPre": "2 payments of ",
      "isFreeTrial": "false",
      "isPayWhatYouWant": "false",
      "date": "Today",
      "period": "month",
      "billing": "2 payments of $0.99 per month",
      "duration": "2 months of access",
      "pricelessBillingPost": " per month",
      "billingPeriod": "1 month",
      "price": "$0.99",
      "isFree": "false",
      "shortPeriod": "/mo"
      "cycles": "2147483647",
      "billing": "$2.99 per month",
      "pricelessBillingPost": " per month",
      "duration": "monthly until cancelled",
      "billingPeriod": "1 month",
      "price": "$2.99",
      "pricelessBillingPre": "",
      "isFree": "false",
      "isPayWhatYouWant": "false",
      "shortPeriod": "/mo",
      "period": "month",
      "date": "Jul 29, 2015"
  "chargeDisplayAmount": "$0.99",
  "chargeAmount": 0.99,
  "chargeCurrency": "USD",
  "isSubscription": true,
  "hasFreeTrial": false,
  "firstRealPrice": "$0.99",
  "oneOffPaymentMethods": [
      "id": 4,
      "name": "Amex Credit Card",
      "identifier": "credit"
  "subscriptionPaymentMethods": [
      "id": 4,
      "name": "Amex Credit Card",
      "identifier": "credit"
  "isCustomPriceAvailable": false,
  "forceAutoRenew": false,
  "newCustomersOnly": false,
  "firstPeriod": "1 month",
  "allowPromoCodes": false

Accessing execution parameters and custom variables in templates

In some cases it is necessary to access the information about the environment that the offer template is being instantiated from, or pass some data to the template directly. In Piano’s Offer Templates there’s a special object available containing data regarding the circumstances of this template render. This object contains the following parameters:

Parameter Description May be used for
aid Application ID Sharing the same template across different applications with minor changes to each one.
debug Debug mode (true/false) Displaying debugging data for the debug mode
displayMode Display mode (inline/modal) Displaying some elements different when the template is inline rather than in modal
experienceId Experience ID Altering some elements of the template based on which experience triggered it
iframeId Id of the html iframe element with the template Rendering ID of the element for debug
OfferId Current Offer ID Adding different text for different offers
tags Page tags Personalizing offer by generating links to different pages with similar tags
templateId Current template ID Rendering the template ID for debugging purposes
url URL of the page where template has been created Generating links to the login page with redirect to the current page
width Width of the template Hiding some attributes based on template’s width

Any of these parameters can be accessed by calling them from “params” object within the template. A few examples:

<!-- This link will let users be redirected back to the current page after successful login -->
<a href="{{params.url}}" target="_parent">Login</a>
<div ng-if="params.debug">
  This is raw data about the page tags in debug mode:
<div ng-show="params.displayMode=='inline'">This element will only be rendered for inline template</div>

There’s also an option to bypass some custom values directly to the template, using custom variables. Any custom variable that has been set prior to showing the template will be available within the “custom” object. The custom variable name is case-sensitive and should only contain alphanumeric characters and underscore.


Setting custom variables in JavaScript:

  tp = window["tp"] || [];
  // Setting custom variables
  tp.push(['setCustomVariable', 'fullName', 'John Smith']);
  tp.push(['setCustomVariable', 'city', 'New York']);
  tp.push(['setCustomVariable', 'weather', '70']);

Using custom variables in template:

<span>Hello, {{custom.fullName}}. It's nice weather in {{}}: {{}}°F</span>


Piano provides several angular directives that you can use to further customize your user's experience.


By default, Piano tries to do all of the heavy lifting for you so you don't have to worry about the inner workings of displaying a credit card entry form, validating input, displaying errors, etc. In some cases, however, you may need to execute your own javascript inside of our offer or system templates. To do so you can use the custom-script directive.

<div custom-script>
    var scriptPath = "http://path/to/your/script.js";
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.src = scriptPath;

This allows you to add your own external javascript files to the Piano template for execution. You can use this approach, for example, to add Omniture integration to various parts of the checkout process.


Because Piano operates within a cross-domain iframe, it's necessary to use postmessage to send messages to and from for security reasons. The external-event directive can be used to send messages from the Piano offer iframe to the parent frame (i.e. your page).

The most common use case is for a Already a subscriber? Login Now link that would render the login prompt to the user.

<div external-event="login">Already a subscriber? Login Now</div>
    <p>You must be a subscriber to access this restricted content</p>

In your config, you could have something similar to this.

tp.push(["init", function() {{
        offerId: "O12345",
        templateId: "OTABCDEF",
        customEvent: function(params) {
            switch (params.eventName) {
                case "login":

When the user clicked the Already a subscriber? Login Now “link”, your mysite.performLogin(); javascript would execute.


If you are not implementing a responsive checkout, and need different markup to be rendered to different, you can leverage the desktop and mobile directives. Any markup contained within the mobile directive will only be rendered on mobile devices.

<div mobile>
    I am on a mobile device


If you are not implementing a responsive checkout, and need different markup to be rendered to different, you can leverage the desktop and mobile directives. Any markup contained within the desktop directive will only be rendered on desktop devices.

<div desktop>
    I am on a desktop

Methods In Scope


The isUserValid method will return a boolean on whether the user is valid, and can therefore start checkout. Because login/registration is a prerequisite for checkout, if you don't have a valid user, we will fire the loginRequired callback if you don't handle this logic yourself.

If you don't implement, and return false, from your loginRequired callback - Piano will display an error screen.

<div ng-show="isUserValid()">
    <div ng-click="startCheckout()">Checkout Now</div>
<div ng-show="!isUserValid()">
    You must login before checking out. <div external-event="login">Login/Register Now</div>

If you had this HTML in your offer template, logged in users would see the link to Checkout Now. Users who weren't logged in would see the prompt to Login/Register Now.


To actually perform checkout, your offer template will need to contain code that actually starts checkout with the selected term.

<div ng-repeat="term in terms">
  <div ng-click="startCheckout(term.termId)">Purchase {{}}</div>

The startCheckout method takes a single parameter, which is the termId that the user is checking out with.