Showing offers

Showing offers is at the bedrock of the Piano platform. Some examples of when Piano would typically show an offer are:

  • After someone's meter expires
  • When viewing a premium article and doesn't have access
  • When someone lands on the dedicated marketing page

Go here for a high-level overview of offers.

Showing an Offer

To show an offer, you would place this code inside of an init callback.

tp.push(["init", function() {{
        offerId: "O123456",
        templateId: "OTABCDEF",
        displayMode: "inline",
        containerSelector: "#my-inline-offer",
        termIds: [ "T1234", "T2345" ],
        loginRequired: function() {},
        close: function() {},
        complete: function(params) {},
        customEvent: function(params) {}

The Offer/Template

Fundamentally, any offer can be rendered inside of any template. The offerId and templateId parameters are both required parameters to and provide the “loose” coupling between the offer and the template.

The offerId can be found by browsing to Manage > Offers and copying the unique offer id from the dashboard. The templateId is located under Manage > Templates > Offer.

Display Mode

When showing an offer, you can specify how and where on the page Piano should render the template. If you don't pass in the displayMode parameter, we default the displayMode to modal.

By default, Piano will show an offer with displayMode : modal. This will present render a semi-transparent backdrop that takes over the entire page, and place an iframe 40 pixels from the top of the page with the offer template inside of it. A close button will be placed outside of the iframe container in the upper righthand corner of the iframe.


When displaying an offer in a modal, the close button is positioned partially outside of the offer template frame. Because of this, the element must be styled in the parent page, not within the Piano dashboard. To style the close button, override this CSS selector:

.tp-modal .tp-close {
    border-color: #f00;

To style the semi-transparent backdrop that appears when displaying an offer in a modal, override this CSS selector: {
    opacity: .5;


To display a template inline, e.g. “cutting off” a premium story for someone who doesn't have access, you can pass in displayMode: “inline”. If you do this, you must specify a containerSelector as well.

The containerSelector must be a valid jQuery selector. If it is not, we will display an error in the browser's console. Become familiar with how Piano does DOM polling to understand how we render a template inline when the call to show offer is made before the containerSelector is in the DOM.


There are several events that the template will postmessage back to the parent frame when certain things happen inside the child frame.

Close Event

The close event fires when the modal or popup closes. There should be no assumption that the user has checked out at this point. You could hook into this event to, for example, present to the user another abandonment prevention offer.

tp.push(["init", function() {{
        offerId: "O12345",      // the regular offer
        templateId: "OTABCDEF", // the regular template
        close: function() {
            tp.api.callApi("/access/check", { rid: "PREMIUM_ACCESS" }, function(response) {
                if (!response.access || !response.access.granted) {
                        offerId: "O23456",    // the don't-abandon! offer
                        templateId: "OTBCDEF" // the don't-abandon! template

Login Required

The loginRequired event fires when someone is not logged in and attempts to start checkout. If your users are stored in a local database - either in Wordpress, Drupal, or your own identity management system - and you're not using a third-party database like Piano ID, Janrain, or Gigya, you will need to implement this callback to redirect the user to your login or registration screen, or to display the login or registration modal.

Once the user is logged in, checkout can continue.

tp.push(["init", function() {{
        offerId: "O1234",
        templateId: "OTABCDEF"
        loginRequired: function(params) {        
            // this is a reference implementation only
            // your own custom login/registration implementation would
            // need to return the tinypass-compatible userRef inside the callback
            mysite.showLoginRegistration(function(tinypassUserRef) {
                tp.push(["setUserRef", tinypassUserRef]);
            // this will prevent the Piano error screen from displaying
            return false;


The complete event fires when the conversion process has completed. If you are leveraging webhooks for access control, Piano strongly recommends that you implement this method to update your local datastore, since webhooks are sent asynchronously.

tp.push(["init", function() {{
        offerId: "O12345",
        templateId: "OTABCDEF",
        complete: function(conversion) {

It will return a conversion object similar to:

    "rid": "RID",
    "chargeAmount": 0.99,
    "chargeCurrency": "USD",
    "termConversionId": "TCFWOJYTCJXU",
    "termId": "TMKDX3UBJ723",
    "startedAt": "2015-06-30T17:04:57.643+0000",
    "expires": 1438275897,
    "uid": "",
    "token_list": "{jax}gknXo8OXR1YqCnbITixDvWnAqkXqZ2lcFenTsovIfgtxXtd4vXpCSmedPkJlq-RpBkkkcgq2CmjVRoxfKXs733OIP5DMdN__DlJ3ItIad0I1g-crBanGU5ON-cN52T6geMFoeUwMVFAmCIySOWweg6n34FW4WcbsR7sCSaNZNaXICmb17qM270IwoQ7mRvKZ"

The token_list string is the updated Access Token List that contains all of the RIDs that a user has access to. Piano will automatically set the __tac cookie with this updated value on every successful checkout.

Custom Event

The customEvent can be used to track custom events within the checkout process. When editing your checkout templates, any element with the external-event will trigger and event to be fired on click. In the case where you want to embed custom links or actions that a user might do that require the main publisher site to handle the event external-event should be used.

Below is a sample snippet from a checkout template

 <span external-event="login">Click here to login</span>

And here is the the javascript configuration for listening to the custom event.{
    offerId : "O123456ABCD",
    templateId : "OT123456ABC",
    customEvent : function(event) {
        // this fires if you embed a custom button that directs the user to login before checking out
        switch(event.params.eventName) {
            case "login":
                window.location.href = "/login";

Dynamic Terms

Typically, you define what terms need to be rendered inside the offer by selecting and ordering the terms inside of the Piano dashboard under Manage > Offers.

If you need to pass in terms dynamically to render, you can pass in a termIds parameter. The termIds parameter can either be a string with a single termId or an array of termIds.

tp.push(['init', function() {{
        offerId: "O123456",
        templateId: "OTABCDEF",
        termIds: [ "TXYZ", "TUVW" ]

On display, the two terms that have been added dynamically will be added to the end of any terms that have been configured in the dashboard. If the offer that has been configured in the dashboard does not have any terms inside of it, only the dynamic termIds that you pass in will be displayed.

DOM Polling

The execution flow is as follows:

Other Functions

The tp.offer object offers various helper methods to further control the offer behavior.


The tp.offer.close() method can be used to close the Piano offer if the Piano offer is in modal mode.


The tp.offer.showBackdrop() method will show the semi-transparent backdrop.


The tp.offer.hideBackdrop() method will hide the semi-transparent backdrop.


The tp.offer.centerBackdrop() method will center the semi-transparent backdrop.


The tp.offer.reload(iframeId, config) method will reload the offer config. You can use this method to solve the problem where a user logs into your site after the Piano offer template has already loaded on the page for the previously not-logged-in user.