We’ve migrated our documentation to a new site, which means some URLs have changed. If you hit a broken link, submit a support ticket.
Subscriptions

Piano Metadata in Stripe

Piano Metadata in Stripe

When Piano connects your subscriptions and payments to Stripe Billing, it automatically adds metadata to Stripe objects. This metadata links each Stripe record back to its corresponding Piano entity (your application, subscriber, subscription, or resource) so you can trace any Stripe object to its Piano source. Filtering of webhook events based on relevant Piano metadata is implemented to ensure non-Piano related events are ignored.

This documents every metadata field Piano sets in your Stripe account, what it represents, and the values you will see. Metadata is read-only: do not modify these fields directly in Stripe.

Piano manages all metadata automatically. You do not need to configure or maintain these fields.

They are provided for reporting, integration, and troubleshooting purposes.

Stripe objects covered:

  • subscription_schedule

  • subscription

  • invoice

  • tax_rate

  • payment_intent

  • customer

  • product

payment_method and price objects do not carry Piano metadata.

Field naming

All Piano metadata fields use camelCase and begin with the prefix "piano". For example: pianoAid, pianoUid, pianoSubscriptionId. A small set of fields on payment_intent use an older naming style without the prefix; these are listed in the payment_intent section.

The value PIANO_VX on pianoCreationReason and creationReason in the tables below is a fixed API identifier retained for backward compatibility. It does not indicate a legacy subscription.

subscription_schedule

Piano creates a Stripe subscription schedule for each managed subscription. Metadata is stored at the phase level, not at the schedule root. The root-level subscription_schedule.metadata is always empty; all fields are set on subscription_schedule.phases[n].metadata.

Fields that do not change between phases are only written on the first phase. Stripe carries them forward automatically. Fields that stop applying to a period are explicitly cleared so they are not inherited.

Metadata Field

Purpose

Values

pianoCreationReason

Identifies why this schedule exists in Stripe.

PIANO_VX: created by the Piano Stripe Billing integration.

pianoOrigin

Records the channel through which the subscription was originally purchased.

CHECKOUT: Piano Checkout
ATW: Piano ATW (Apply Term Wizard)
MANUAL: manually by the publisher
MARKING_JOB: automated by Piano
SWITCH_COCKPIT: via Piano Switch Cockpit

ATW (Apply Term Wizard) is the internal name for the Dashboard surface labeled Subscribe User. They are the same channel, not two.

pianoAid

Your Piano application identifier.

Piano application ID (string).

pianoUid

The subscriber's Piano user identifier.

Piano user ID (string).

pianoSubscriptionId

The Piano subscription this Stripe subscription corresponds to.

Piano subscription ID (string).

pianoAutoRenewalEnabled

Whether auto-renewal is currently enabled for the subscription.

true or false.

pianoInTrial

Marks a phase that represents a free or paid trial period.

true when a trial is active. Field is absent in all other phases.

pianoPromoCodeId

The promo code applied to this billing period.

Piano promo code ID (string). Field is absent when no promo code is applied.

pianoPromotionId

The promotion applied to this billing period.

Piano promotion ID (string). Field is absent when no promotion is applied.

pianoPayMoreApplied

Marks a billing period where the subscriber chose to pay more than the standard price.

true when Pay More is active. Field is absent otherwise.

pianoNextBillingDateManuallyUpdated

Marks the bridging phase created when a publisher manually changes a subscription's next billing date. This zero-amount phase spans from the change date to the new billing date.

true. Field is absent in all other phases.

pianoSubscriptionManuallyPrebilled

Marks the prepaid phase created when a subscription is renewed early by the publisher. This zero-amount phase spans the pre-paid period.

true. Field is absent in all other phases.

pianoInitialCustomPeriod

Marks the bridging phase that covers the period between the date a subscription was connected to Stripe Billing and its first Stripe renewal date. This phase carries no associated invoice.

true. Field is absent in all other phases and does not apply to subscriptions connected via a manually created invoice.

pianoVoucherId

The voucher applied to extend this subscription period.

Piano voucher ID (string). Field is absent when no voucher is applied.

pianoSubscriptionUpgraded

Marks the first billing period of a subscription that was created as a result of an upgrade.

true. Field is absent in all other phases.

subscription

subscription.metadata reflects the currently active phase of the subscription schedule and is kept in sync by Piano as phases change. When no schedule exists (after the schedule completes), the metadata retains the values from the last active phase.

The fields are identical to those on subscription schedule phases. The key difference is how they behave over time:

  • Fixed fields (pianoCreationReason, pianoOrigin, pianoAid, pianoUid, pianoSubscriptionId) are set once and do not change over the subscription's lifetime.

  • State fields (pianoInTrial, pianoPromoCodeId, pianoPromotionId, pianoPayMoreApplied, pianoInitialCustomPeriod, pianoNextBillingDateManuallyUpdated, pianoSubscriptionManuallyPrebilled, pianoVoucherId, pianoSubscriptionUpgraded) reflect the current period: present when that condition is active, absent otherwise.

  • pianoAutoRenewalEnabled reflects the subscriber's current auto-renewal setting.

Metadata Field

Purpose

Values

pianoCreationReason

Identifies why this subscription exists in Stripe.

PIANO_VX.

pianoOrigin

Records the original purchase channel.

CHECKOUT, ATW, MANUAL, MARKING_JOB, or SWITCH_COCKPIT. See subscription_schedule section for definitions.

pianoAid, pianoUid, pianoSubscriptionId

Application, user, and subscription identifiers.

Piano IDs (strings).

pianoAutoRenewalEnabled

Whether auto-renewal is currently enabled.

true or false.

State fields (as above)

Reflect the current period.

Present when active; absent otherwise.

The following subscription locations are always empty: subscription.items[n].metadata, subscription.items[n].plan.metadata, subscription.items[n].price.metadata, subscription.plan.metadata.

invoice

Piano creates or enriches Stripe invoices in several contexts: the first invoice when a subscription is connected to Stripe Billing, renewal invoices (automatic and manual), upgrade invoices, one-time payment invoices, and gift invoices. All carry a common set of core fields; additional fields appear where applicable.

Metadata Field

Purpose

Values

pianoCreationReason

Identifies why this invoice exists in Stripe. Present on all Piano-managed invoices.

PIANO_VX.

pianoOrigin

Records the original purchase channel. Present on all Piano-managed invoices.

CHECKOUT, ATW, MANUAL, MARKING_JOB, or SWITCH_COCKPIT.

pianoAid, pianoUid, pianoUserPaymentId

Application, user (gifter for gift invoices), and Piano payment record identifiers. Present on all invoices.

Piano IDs (strings).

pianoInvoiceSource

Identifies the type of manually created connection invoice.

SUBSCRIPTION_PURCHASE: first period of a connected subscription
SINGLE_PAYMENT_PURCHASE: one-time payment
GIFT_PURCHASE: gift subscription purchase

stripeAttachedPaymentIntentId

The Stripe payment intent that was manually attached to this invoice.

Stripe payment intent ID (string).

pianoPromoCodeId, pianoPromotionId, pianoPayMoreApplied, pianoVoucherId, pianoSubscriptionManuallyPrebilled, pianoSubscriptionManuallyGraceEscaped, pianoSubscriptionUpgraded

State markers for the billing period the invoice covers.

Piano IDs or true; absent when not applicable.

pianoTaxExclusionReason

Explains why the tax amount on this invoice is zero, when taxes are configured but the calculated amount is zero.

not_configured / exempt_or_zero_rate / could_not_calculate. Absent when taxes are non-zero or not applicable.

pianoTaxabilityReasonDetails, pianoResidenceCountryCode, pianoCountryCode, pianoBillingZipCode, pianoPostalCode, pianoTaxSupport, pianoBillingAddressId

Tax calculation context (provider, country/residence codes, postal/ZIP, billing address).

Strings / ISO codes; present only where relevant to the tax configuration.

Other invoice locations
  • invoice.lines[n].metadata: mirrors subscription.metadata at the current moment for subscription invoices (a live reflection, not a snapshot). Always empty for one-time payment and gift invoices.

  • invoice.lines[n].plan.metadata and invoice.lines[n].price.metadata: always empty.

  • invoice.parent.subscription_details.metadata and invoice.subscription_details.metadata: a snapshot of subscription.metadata at the time the invoice was created; do not change after creation. Always empty for one-time payment and gift invoices.

tax_rate

Piano automatically creates tax rate objects in Stripe based on your Piano tax configuration. One metadata field is set on each tax rate object: pianoAid (your Piano application identifier). Do not modify tax rates created by Piano directly in the Stripe Dashboard.

payment_intent

Piano sets metadata on the payment intents it creates for subscription purchases, renewals, one-time payments, and gift purchases. Payment intents created automatically by Stripe for subscription renewals carry no metadata.

Metadata on payment_intent uses an older naming style that predates the current piano-prefixed convention. Field names on this object do not carry the piano prefix. This is maintained for backward compatibility.

Metadata Field

Purpose

Values

creationReason

Identifies why this payment intent exists in Stripe.

PIANO_VX.

source

Identifies the Piano payment integration used to process the payment.

e.g. STRP (Stripe), STRP_EL (Stripe Elements), APSTRP (Apple Pay via Stripe).

aid, uid, termId, termName, subscriptionId

Application, user, term, and subscription identifiers/names.

Piano IDs / names (strings).

taxCountry, taxState

Country/state codes used for tax calculation on this payment.

ISO/region codes; absent when not applicable.

trackingId, mandateReference

Internal transaction tracking ID and consent mandate reference (e.g. cards issued in India).

Strings; mandate absent when none required.

payment_intent.charges[n].metadata automatically mirrors payment_intent.metadata.

customer

Piano maps each subscriber to a Stripe customer record. One metadata field is set on customer creation and does not change: UID, the subscriber's Piano user identifier (uppercase, consistent with the older naming style used when customer records were first created).

product

Piano creates a Stripe product object for each Piano resource associated with a subscription or one-time payment: pianoRid, the Piano resource this Stripe product represents.

A deprecated pianoProductId field may be present on older product objects. It is being removed; pianoRid is the canonical resource identifier.

payment_method and price

payment_method.metadata and price.metadata are always empty for all Piano-managed objects. No metadata is set on these entity types.

Feature support and roadmap

Features that are not yet supported, the Coming Soon roadmap, and Piano features that remain outside the Stripe Billing integration are documented on Feature Support and Roadmap.

Billing behavior and limits

Minimum charge amounts

Stripe applies minimum charge amounts, which may affect very small invoice amounts that might not be charged to the end-user. Here's how it works:

If an invoice amount due is less than the minimum chargeable amount for the currency, Stripe automatically marks the invoice as paid. The amount is debited from the customer's credit balance rather than processing a card payment. If there's no credit balance, the customer effectively gets that amount for free.

The minimum chargeable amounts vary by currency. For example: USD: $0.50, EUR: €0.50, GBP: £0.30.

When this happens: the invoice status changes to paid automatically; no payment attempt is made against the customer's payment method; the small amount is recorded as paid (either from credit balance or effectively waived). This commonly occurs with prorated amounts from subscription changes, very small subscription costs, tax-only invoices with minimal amounts, and remaining balances after partial payments.

Rounding rules for tax

Stripe rounds to the smallest currency unit, for example cents for USD. Currencies without decimal places, such as JPY, are unaffected.

The invoice-level versus line-item-level rounding setting applies only to invoices with manual tax rates. Invoices calculated with automatic Stripe Tax always sum tax amounts first and then round, which is one more reason Stripe Tax must not be enabled while Piano manages tax.

Piano's tax providers, including TaxJar and Avalara, apply their own rounding rules, which may differ from Stripe's. Where a provider's rules differ from Stripe's, the tax amount and invoice total can differ from the amount taken in Piano Checkout or Subscribe User. This applies to exclusive tax only and the difference is never more than 0.01 in the smallest currency unit.

Subscription renewal date handling for subscription switching

  • Anchoring is a feature that allows you to return to the same calendar date back at renewals.

  • Example: monthly subscription purchased on Jan 31. With anchoring to 31st of month: renewed on Feb 28, Mar 31, Apr 30, May 31. Without anchoring: renewed on Feb 28, Mar 28, Apr 28, May 28.

  • Anchoring is important for monthly and yearly billing periods (because of leap years). Any other billing periods (weekly, several days) aren't affected by anchoring.

  • For new purchases with Stripe Billing, Piano anchors to the moment of purchase.

  • For existing subscriptions switched to Stripe Billing, Piano anchors to the next renewal date (the 1st charge in Stripe).

  • The anchor date is reset when a subscription enters a new billing period, for example after a trial or promotion to a full price billing period.

  • In case the subscription does not have a monthly/yearly trial period, anchor is set once it enters any monthly/yearly period.

  • Anchor timestamp is passed to Stripe as is. Stripe calculates calendar date anchor based on UTC time zone.

Renewal process date handling in Stripe

  • Piano passes the renewal date in UTC to Stripe

  • In the majority of cases billing cycle anchor is used to set the first renewal date for all switched subscriptions from which Stripe Billing renews the subscription.

  • Stripe Billing honors the end of the month for the subscription renewal

Subscription Billing intervals

Please note, Stripe is currently allowing billing intervals to be set up to 3 years as maximum at the Price object. Therefore, any subscriptions with billing interval set to more than 3 years cannot be currently switched or purchased once switched to Stripe Billing.

  • Same limitation affects the manual changes to Next Billing Date (NBD) on active subscriptions, and the NBD cannot be set to a future date longer than date +3 years.

For more details, see Billing periods and the 3-year guideline.

Looking for the Stripe-to-Piano data mapping tables (subscription and payment object field mapping) and the Piano-to-Stripe entity/architecture mapping? Those now live together on Piano-Stripe Entity & Data Mapping.

Last updated: