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

How do I build custom subscription and unsubscribe landing pages for Piano ESP?

What you can and cannot customize

You can:

  • Redirect the unsubscribe link in your email footer to a landing page you control.

  • Build a "manage newsletter preferences" page showing every mailing list with checkboxes for the user's current subscriptions.

  • Create custom call-to-action (CTA) buttons that subscribe users to specific newsletters, with their own copy, styling, and confirmation flow.

  • Pop a custom email-collection modal for anonymous users who click a Subscribe CTA — the default opt-in flow does exactly this when the user is unknown.

You cannot:

  • Edit the default Piano-hosted subscription-management page (the page reached by the standard unsubscribe link if no custom redirect is configured).

  • Remove the unsubscribe feedback form that appears when a user unsubscribes via the default flow. The data the form collects is still accessible via the ESP feedback API.

  • Send emails directly through an API. Email composition and sending are dashboard-driven in ESP; there is no API for sending an arbitrary email or for setting strings in a specific template at send time.

If the default translations of the public-facing pages (subscription manager, Double Opt-In confirmation, etc.) are wrong for your audience, you can request a translation update; note that changes apply to all clients using the same language.

Step 1: Get your ESP API key

API calls from your landing pages are authenticated with an API key tied to your ESP Site. Retrieve the key from the ESP dashboard under Setup → Integrations; the value is revealed when you hover over the information icon next to the API key field.

The key is per-Site. If you manage multiple ESP sites, make sure you are using the key for the site that owns the mailing lists you want to operate on, or you will see validation errors during API calls.

Step 2: Retrieve mailing lists for the site

Your preference page needs to know which mailing lists to display. Use the publisher listing endpoint:

GET http(s)://api-esp.piano.io/publisher/pub/<site_id>/sq?api_key=<api_key>

This returns every mailing list available to the site, including its Id, Name, Active state, and the campaign IDs that have it attached. Use this list to render the checkbox grid on your preferences page, and to populate the metadata on any custom CTA offers — for example, list name, description, and target campaigns.

If you need only the mailing lists attached to a specific campaign, use:

GET http(s)://api-esp.piano.io/publisher/pub/<site_id>/ml/<campaign_id>/sq?api_key=<api_key>

Step 3: Identify the user on the landing page

Your landing page needs to know which user it is operating on. There are three common patterns, and you typically combine them:

%SUBSCRIBERINFO% merge field (from emails)

Insert the %SUBSCRIBERINFO% merge field anywhere in an ESP email template. At send time, ESP replaces it with a Base64-encoded JSON object containing the recipient's subscriber information. If your unsubscribe link points at your custom landing page, append the merge field as a query parameter:

https://example.com/unsubscribe?data=%SUBSCRIBERINFO%

When the recipient clicks the link, your landing page receives the Base64 payload, decodes it server-side, and uses the contained email (and any other fields you need) to call the ESP API.

Important: simply including data=%SUBSCRIBERINFO% in the URL does not unsubscribe the user. The merge field is informational — it delivers the identity to your page, but your page is still responsible for calling the ESP unsubscribe API in response to the user's action (clicking a button, ticking checkboxes and submitting, etc.).

Identity Management logged-in user

If you are using Identity Management and the visitor is already logged in, you do not need to parse %SUBSCRIBERINFO% — their email can be taken from Piano's side directly. Use the Identity Management session as the source of truth and skip the merge-field decode.

Anonymous visitors

For anonymous visitors who click a Subscribe CTA on a public page, prompt them for their email in a pop-up modal. Once they confirm, subscribe them to the selected mailing list(s). This is also how the default opt-in pop-up works — you are simply reproducing it in your own design.

Step 4: Subscribe and unsubscribe from your buttons

The same /tracker/securesub endpoint that powers the default opt-in widget also powers your custom buttons.

Subscribe:

POST http(s)://api-esp.piano.io/tracker/securesub?api_key=<api_key>
Content-Type: application/json
{
  "email": "<reader_email>",
  "mlids": [<list_id_1>, <list_id_2>, ...]
}

Unsubscribe:

DELETE http(s)://api-esp.piano.io/tracker/securesub?api_key=<api_key>
Content-Type: application/json
{
  "email": "<reader_email>",
  "mlids": [<list_id_1>, <list_id_2>, ...]
}

A "manage preferences" page typically renders the user's current state by querying subscription status per list, then on submit issues a combination of POST (for newly ticked lists) and DELETE (for newly unticked lists) until the back-end matches the user's selections.

Use the Double Opt-In (DOI) flow where regulations require explicit confirmation. With DOI enabled on a list, a POST creates a pending record and triggers a confirmation email; the user's status becomes active only after they click the confirmation link.

Step 5 (optional): Read unsubscribe feedback

When users unsubscribe via the default flow, a feedback form is presented to collect reasons for unsubscribing. This form cannot be removed, but the data it captures is retrievable via the ESP backend API:

GET /publicpages/feedback/<PUB_ID>?event=unsubscribe&date_start=<YYYY-MM-DD>&date_end=<YYYY-MM-DD>&api_key=<api_key>

campid, sbid, and userid filter parameters are available. The maximum window is 90 days per call.

Pre-built tools and limitations

No "send email" API

There is no API to send an email directly through ESP, and no API to set dynamic strings into a specific template at send time. All email composition happens in the ESP dashboard. Personalization is handled with merge fields populated at subscription time (via /userdata/umfval/...) and rendered by ESP at send.

Putting it together: a typical custom unsubscribe flow

  1. Email template includes a link like https://example.com/unsubscribe?data=%SUBSCRIBERINFO%.

  2. ESP sends the email, replacing %SUBSCRIBERINFO% with a Base64-encoded JSON identity payload.

  3. The recipient clicks. Your server decodes the payload and identifies the user.

  4. The server calls GET /publisher/pub//sq?api_key= to list mailing lists, and (optionally) checks the user's status on each.

  5. Your page renders a branded "manage preferences" UI with the right checkboxes already ticked.

  6. On submit, your server issues the appropriate POST and DELETE calls to /tracker/securesub to align ESP state with the user's selections.

  7. Optionally, a follow-up server call to /publicpages/feedback/... records or surfaces the reason if the user has unsubscribed.

The same building blocks work for a "subscription centre" linked from your account page, an in-app preferences screen, or an AMP-restricted sign-up form (where the email is collected by AMP and the subscribe call happens entirely server-side). If you are using Identity Management for authentication, see the related Identity Management integration article for tying these subscribe / unsubscribe calls back into the identity layer.

Last updated: