Offer templates

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

Piano uses angularjs to render templates to end users that 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 offer template is a dedicated marketing “Subscribe” page. You can have the entire page, or just part of it, delivered by Piano.

 CNBC Subscribe Page

Metered Paywall Modal

You can use a modal offer template to display a “subscribe” prompt after someone has reached the free page view limit.


Premium Story Inline Offer

For users who are viewing premium content that they do not have access to, you can offer inline templates. The example below truncates the article and renders the offer inline.

 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 have easy-to-customize elements so you can create a brand experience that your users are familiar with.


If a user is logged in, the user object will be populated with that user's specific 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 anon user data.

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


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

  "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


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 a user clicks “Already a subscriber? Login Now” link, your mysite.performLogin(); javascript will execute.


If you are not implementing a responsive checkout and need different markups to be rendered to different users, you can use 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 markups to be rendered to different users, you can leverage the “desktop” and “mobile” directives. Any markup contained within the “desktop” directive will only be rendered on desktops.

<div desktop>
    I am on a desktop

Methods In Scope


The isUserValid method will return a boolean confirming if the user is valid and can start checkout. Because login/registration is a prerequisite for checkout, if the user is not registered 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 have this HTML in your offer template, logged in users will see the link to Checkout Now. Users who are not logged in will see the Login/Register Now prompt.


To actually perform checkout, your offer template needs 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 the single parameter, the “termId,” that the user is using to check out.