Stripe's billing_cycle_anchor is the reference timestamp that lines up a subscription's billing periods. It fixes the day of the week for weekly prices, the day of the month for monthly and yearly prices, and the month for yearly prices. It defaults to the creation time or trial end. Set it at creation with a timestamp or `billing_cycle_anchor_config`, reset it later with `now`, and control the partial-period charge with `proration_behavior`.

This guide is for engineers who own a Stripe Billing integration and need renewal dates that finance, support and customers can predict. It covers both ways to set the anchor, month-end dates, billing everyone on the 1st, resets, trials, backdating, every `proration_behavior` value, mixed intervals, time zones, test clocks, webhook events and common mistakes. Every behaviour is checked against Stripe's billing cycle documentation as of October 2026; where it depends on API version or billing mode, we say so.

What is billing_cycle_anchor in Stripe?

The billing cycle anchor is a Unix timestamp, in seconds and in UTC, stored on every Subscription as `billing_cycle_anchor`. Stripe aligns all future billing period boundaries to it. A subscription's billing period depends on two things only: the recurring interval of its price (monthly, yearly, weekly and so on) and this anchor.

What the anchor fixes depends on the interval. For `week` intervals it sets the day of the week, so a weekly subscription anchored on a Friday bills every Friday. For `month` and `year` intervals it sets the day of the month, and for `year` intervals it also sets the month of the year. Stripe's own examples make this concrete: a monthly subscription anchored on September 2 always bills on the 2nd; a weekly one anchored on Friday, June 3 bills every Friday after that.

The default anchor

If you do nothing, the anchor is the subscription's creation time, or the trial end if it starts with a trial. A customer who signs up at 14:37 UTC on March 9 renews at 14:37 UTC on the 9th of each month. That stops working when finance wants everyone billed on one day, when a contract says invoices go out on the 1st, or when migrated customers already have renewal dates.

Anchor, first full invoice and current period

Three dates are easy to confuse. The anchor is the alignment point. The first full invoice covers the first complete period; Stripe says it is often the anchor date and always interval-aligned with it. The current period is the window being billed now, and in current API versions each subscription item carries its own `current_period_start` and `current_period_end`. Creation time is also not the start date, which can be earlier if you backdate.

How do you set the billing cycle anchor when creating a subscription?

There are two ways to set the anchor on a new subscription: pass `billing_cycle_anchor` as a future Unix timestamp, or pass `billing_cycle_anchor_config` and let Stripe calculate the timestamp. The two parameters are mutually exclusive. Stripe recommends `billing_cycle_anchor_config` for monthly and yearly prices because it accounts for short months and leap years.

Option 1: billing_cycle_anchor as a timestamp

A timestamp works with every interval, including daily and weekly prices, and must be in the future. You do the date arithmetic yourself, which is where integrations go wrong: "the 31st of next month" is an invalid date in half the year, and "midnight" built in a server's local time zone is hours off what finance expected.

Checkout accepts the timestamp form only, as `subscription_data.billing_cycle_anchor`, and only when the session is in `subscription` mode. Checkout does not support combining a trial with a billing cycle anchor, and you cannot use one-off prices in a Checkout Session when `proration_behavior` is `none`.

Option 2: billing_cycle_anchor_config

billing_cycle_anchor_config describes the anchor instead of stating it. Stripe sets `billing_cycle_anchor` to the next occurrence of the day you describe, at the hour, minute and second you give, in UTC. It is only valid with monthly and yearly price intervals. Its fields are:

  • `day_of_month`, required, 1 to 31. The day the subscription renews.
  • `month`, optional, 1 to 12. The month that full cycle periods start, for yearly and multi-month prices. A yearly subscription with `month: 7` and `day_of_month: 1` renews on July 1.
  • `hour` (0 to 23), `minute` (0 to 59) and `second` (0 to 59), optional. If you leave them out, Stripe uses the hour, minute and second of the subscription's creation time.

That last default surprises people. A subscription created at 5 PM in New York with only `day_of_month: 1` is anchored at 22:00 UTC on the 1st (during standard time), not at midnight. If you want a clean boundary, set all three time fields explicitly. If you want a new subscription to renew at exactly the same moment as an existing one, read the existing anchor's day, hour, minute and second and pass the same four values.

Two documented edge cases are worth knowing. First, `billing_cycle_anchor_config` can produce an anchor more than one billing period in the future, but the first full invoice always falls within one billing period of creation or of the trial end. Stripe's example is a two-month price created in February with `day_of_month: 31`: the next qualifying 31st on a two-month cycle is August 31, so that becomes the anchor, but the customer still gets a short prorated period to the end of February and then full two-month periods. Second, `billing_cycle_anchor_config` does not support a backdated start date; use a timestamp if you need both.

Creating a subscription anchored on the 1st (Node.js)
import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

// Bill every customer on the 1st of the month at 00:00 UTC.
// Assumes the customer already has a default payment method.
export async function createCalendarSubscription({ customerId, priceId, userId, chargeFirstPartialPeriod }) {
  return stripe.subscriptions.create(
    {
      customer: customerId,
      items: [{ price: priceId }],
      // Monthly and yearly prices only. Stripe computes the next
      // occurrence of this day and handles short months for you.
      billing_cycle_anchor_config: { day_of_month: 1, hour: 0, minute: 0, second: 0 },
      // create_prorations (default): invoice the partial period now.
      // none: the partial period is free and the first invoice is on the 1st.
      // always_invoice is not supported when creating a subscription.
      proration_behavior: chargeFirstPartialPeriod ? "create_prorations" : "none",
      metadata: { user_id: userId },
      expand: ["latest_invoice"],
    },
    { idempotencyKey: "sub-create-" + userId + "-" + priceId },
  );
}

// A daily or weekly price, or an exact moment, needs a timestamp instead:
//   billing_cycle_anchor: 1798761600  // Unix seconds, UTC, in the future
// billing_cycle_anchor and billing_cycle_anchor_config are mutually exclusive.
Illustrative. How one anchor on the 1st turns into a prorated first invoice and then full periods.

What the first invoice looks like

Whichever parameter you use, Stripe by default creates a prorated invoice for the time between creation and the first full invoice date, and charges it immediately. Stripe classifies that line as a proration, even in the edge case where the anchor is exactly one interval after creation and the "partial" period is in fact a full month. If you key revenue reports or customer emails off the `proration` flag, account for that.

If you do not want to charge for the partial period, you have two documented options. Set `proration_behavior` to `none`, which makes the time until the first full invoice free and creates no invoice at all until then. Or combine a trial with the anchor by setting `trial_end`, covered later in this guide. On creation, `proration_behavior` accepts `create_prorations` (the default) and `none`; `always_invoice` is unsupported.

What happens when the anchor is the 29th, 30th or 31st, and in which time zone?

A monthly subscription anchored on a day that a month does not have renews on that month's last day, then returns to the anchor day in the next month that has it. Stripe's example: an anchor of January 31 bills on February 28 (February 29 in a leap year), then March 31, April 30 and so on. All of these dates are computed in UTC.

With `billing_cycle_anchor_config`, set `day_of_month: 31` to bill at the end of every month: Stripe documents that in months with fewer than 31 days the subscription renews on the last day of that month. The same last-day rule is how Stripe handles anchors on the 29th and 30th in February. The anchor itself does not move. A common misreading is that a subscription anchored on the 31st "drifts" to the 28th after February; it does not, because every period is computed from the original anchor rather than from the previous period's end date.

Month-end anchors renew on the last day of short months and return to the anchor day afterwards.

Two practical consequences follow. Periods have different lengths, so a monthly price does not buy a constant number of days; if your product meters something per day, compute against the item's actual `current_period_start` and `current_period_end`. And customer-facing copy such as "renews on the 31st" is wrong four or five times a year, so show the next renewal date from Stripe instead of a rule.

Time zones

Stripe stores and computes the anchor in UTC. A subscription anchored at 00:00 UTC on the 1st renews on the evening of the previous day for customers in the Americas, and because UTC has no daylight saving, a fixed UTC hour lands at a different local hour in summer and winter. Decide which time zone your business means by "the 1st", convert once when you build the config, and show customers renewal dates computed from Stripe's timestamps in their own time zone.

How do you bill every customer on the 1st of the month?

Create each subscription with `billing_cycle_anchor_config` set to `day_of_month: 1` (plus explicit `hour`, `minute` and `second`), then choose how the partial first period is handled: charge it now with the default `create_prorations`, give it away with `proration_behavior: none`, or cover it with a trial. That is calendar billing, and it is the single most common reason teams touch the anchor.

The three choices produce visibly different first invoices for a customer who signs up on the 15th of a 30-day month (illustrative):

  • Charge the partial period (`create_prorations`, the default). An invoice is created and charged at sign-up for the 15th to the 1st, roughly half the monthly price, then a full invoice on the 1st. You collect money and validate the card on day one; the customer sees two charges in about two weeks.
  • Give the partial period away (`none`). No invoice until the 1st, then a full invoice. Simple for the customer. The card is not charged at sign-up, so a bad card is only discovered on the 1st, and every new customer gets up to a month free. Decide whether that is a pricing decision or an accident.
  • Use a trial that ends on or before the 1st (`trial_end`). The trial end becomes the anchor. Stripe documents that a trial combined with an anchor can still produce a prorated invoice after the trial: with a 7-day trial and an anchor on the 1st, a customer who subscribes on the 15th gets a prorated invoice on the 22nd for the 22nd to the 1st, then full invoices on the 1st. A customer who subscribes on the 28th has a trial that runs past the 1st, so the prorated period runs into the next month.

Calendar billing also concentrates load. Every renewal for the month is created within the same hour, so failures, `invoice.payment_failed` events, dunning emails and support tickets arrive in one burst. Size webhook workers for that peak, and check your subscription payment retries schedule against support staffing, because retry waves land on the same days too.

How do you reset the billing cycle anchor on an existing subscription?

You cannot set an arbitrary new anchor on an existing subscription. Stripe documents two ways to change the billing date: reset the anchor to the current time with `billing_cycle_anchor` set to now, or add a trial whose `trial_end` becomes the new anchor. The `unchanged` value tells Stripe to keep the existing anchor during an update that might otherwise move it.

The parameter shape depends on your API version

This changed recently. API version 2026-09-30.endive made `billing_cycle_anchor` on subscription updates, subscription resumes and preview invoices an object with a `type` field, and Stripe lists it as a breaking change in the changelog. On that version and later, the subscription update reference expects `{ type: "now" }` or `{ type: "unchanged" }`. On earlier versions, send the strings `now` or `unchanged`. For preview invoices for a new subscription with a future anchor, the timestamp becomes `{ type: "timestamp", timestamp: 1234567890 }`. Creating a subscription still takes a plain timestamp for `billing_cycle_anchor`.

What a reset does to invoices

In classic billing mode, resetting the anchor to now starts a new period immediately and Stripe sends an invoice straight away. Enable proration so the customer is credited for days already paid in the previous period; Stripe warns that disabling proration on a reset might overcharge the customer. In flexible billing mode, a reset to now does not automatically generate a new invoice. To invoice on the reset, set `proration_behavior` to `always_invoice`, unless the same update also changes items so that the prorations do not cancel out, in which case Stripe invoices immediately anyway. Which mode you are in matters, so read `billing_mode.type` on the subscription before you rely on either behaviour.

Changing the date with a trial

Setting `trial_end` on an update moves the anchor to that timestamp. Stripe's worked example: an active subscription due to bill on July 23 gets a trial ending August 1, added on July 15. The customer receives a zero-amount invoice on July 15, is not billed on July 23, and pays a full period on August 1 and on the 1st of every month after. Because the trial length already accounts for the paid days, Stripe suggests `proration_behavior: none` in this case. This is the cleanest way to move an existing customer onto calendar billing without a surprise charge.

When the anchor moves without you asking

In classic billing mode, some updates reset the anchor on their own: switching to a price with a different `recurring.interval` resets it to now, adding a paid price to an all-free subscription resets the period, setting `cancel_at` before the next renewal moves it to the cancellation date, and a trial starting or ending resets the billing date. In flexible billing mode, Stripe documents that the anchor is never reset automatically.

How does proration_behavior work with billing_cycle_anchor?

`proration_behavior` decides what Stripe does with the partial-period difference created by an anchor, a reset, a trial or an item change. `create_prorations` (the default) creates proration invoice items that are normally collected on the next invoice; `always_invoice` creates them and invoices immediately; `none` creates none for that request. Read the Stripe prorations guide for the full rules; the essentials follow.

What triggers a proration

By default, prorations come from adding or removing items, changing an item's price, changing a quantity, adding `trial_end` or `trial_from_plan` to an active subscription, changing `billing_cycle_anchor`, and setting `cancel_at` within the current period. Many updates never prorate, including metadata, `default_payment_method`, `collection_method`, `retry_settings`, `pause_collection`, `cancel_at_period_end` and coupons or discounts on their own. A discount change only affects a proration when it is made in the same call as a change that prorates; Stripe then calculates the proration with the modified discount.

How the amounts are calculated

Stripe prorates to the second by default, using the current period's start and end. A proration has two halves: a credit for unused time on the old price and a debit for the remaining time on the new one. Stripe uses the subscription's discounted price when calculating them, and marks proration lines `discountable: false` so no further discount applies to them. Negative prorations are not refunded automatically and positive ones are not billed immediately unless you ask for that. Usage-based prices are not prorated at all; they bill for what was used.

Billing mode changes the credit side. In classic mode the credit is based on the current price, so a customer moved to a higher price with `none` and later downgraded can be credited for time at a price they never paid. In flexible mode the credit is based on the last price actually billed for that item, so the same sequence nets to zero. Stripe documents both outcomes side by side; if you have older subscriptions in classic mode, check which rule your reconciliation assumes.

Illustrative, based on Stripe's test clock example. The same upgrade under each proration_behavior value.

Unpaid invoices and prorations

Stripe calculates prorations assuming earlier invoices will be paid, so a customer who changes plan with an unpaid invoice can be credited for time they never paid for. Stripe's guidance: use `proration_behavior: none`, then either create a one-off invoice for new charges or reset the anchor to now, and void the old unpaid invoice so the customer cannot pay twice. If `past_due` customers can change plans, this path is real.

What invoices result from changing plans mid-cycle?

A same-interval price change keeps the anchor and, with the default `create_prorations`, adds a credit and a debit to the next renewal invoice. A change to a different interval, from free to paid, or a trial starting or ending, resets the billing date in classic mode and charges immediately. To charge an upgrade now without moving the date, use `always_invoice`.

Stripe's test clock walkthrough gives a clean worked example (illustrative): a $50-a-month basic plan created on January 1 is upgraded to a $100-a-month premium plan on January 16. With the default behaviour, the update creates two pending invoice items, -$25 for unused basic time and +$50 for the remaining premium time, and no invoice. On February 1 the renewal invoice combines the two prorations with the full February premium charge, for $125. The subscription reference describes the same rule with a May example: a $100 price changed to $200 on May 15 yields a $250 invoice on June 1.

That default has two side effects to decide on purpose. The customer uses the premium tier until renewal before paying for it, and the next invoice is larger than the plan price, which prompts support questions. If either matters, use `always_invoice` for upgrades and pair it with `payment_behavior: pending_if_incomplete` so the upgrade only applies if that payment succeeds.

Downgrades produce a credit. With `create_prorations` it reduces the next invoice; with `always_invoice` it is invoiced straight away as a negative amount. Either way, Stripe does not refund negative prorations to the card automatically. Many products avoid this entirely by scheduling downgrades for the period end, so the customer keeps what they paid for and the lower price starts at renewal.

Preview first, then apply the same proration_date

Show the customer the amount before they confirm by creating a preview invoice with the proposed change. Because prorations are calculated to the second, the amount can change between preview and update; pass the same `proration_date` to the preview (as `subscription_details.proration_date`) and to the update so both use the same moment. `proration_date` is also how you implement day-level proration yourself, and it must fall within the current period of the subscription's items, except during the first period of a backdated subscription.

Previewing and applying a plan change, and resetting the anchor (Node.js)
// 1. Preview the change, so the customer confirms a real amount.
export async function previewPlanChange(subscriptionId, newPriceId) {
  const sub = await stripe.subscriptions.retrieve(subscriptionId);
  const prorationDate = Math.floor(Date.now() / 1000);

  const preview = await stripe.invoices.createPreview({
    customer: sub.customer,
    subscription: subscriptionId,
    subscription_details: {
      items: [{ id: sub.items.data[0].id, price: newPriceId }],
      proration_date: prorationDate,
    },
  });

  const prorationLines = preview.lines.data.filter(
    (line) => line.parent?.subscription_item_details?.proration === true,
  );
  return { prorationDate, amountDue: preview.amount_due, prorationLines };
}

// 2. Apply exactly what was previewed: same proration_date.
export async function applyPlanChange(subscriptionId, newPriceId, prorationDate) {
  const sub = await stripe.subscriptions.retrieve(subscriptionId);
  return stripe.subscriptions.update(subscriptionId, {
    items: [{ id: sub.items.data[0].id, price: newPriceId }],
    proration_behavior: "always_invoice", // charge the difference now
    proration_date: prorationDate,
    payment_behavior: "pending_if_incomplete", // keep the old plan if payment fails
  });
}

// 3. Move the renewal date to now.
// API version 2026-09-30.endive or later takes an object.
// Earlier versions take the string: billing_cycle_anchor: "now".
// In flexible billing mode a reset only invoices immediately with
// always_invoice; that value also credits the time already paid for.
export async function resetBillingDateToNow(subscriptionId) {
  return stripe.subscriptions.update(subscriptionId, {
    billing_cycle_anchor: { type: "now" },
    proration_behavior: "always_invoice",
  });
}

How do trials, backdate_start_date and mixed intervals affect the billing cycle anchor?

A trial moves the anchor to `trial_end`. `backdate_start_date` moves the start into the past without moving the anchor; combine it with `billing_cycle_anchor` to keep an existing renewal date. In flexible billing mode, items on different intervals each keep their own billing period.

Trials

When a subscription is created with a trial and no explicit anchor, the trial end becomes the anchor, so a 14-day trial started on the 3rd renews on the 17th of every month. Combining `trial_end` with an anchor on the 1st aligns the paid periods to the calendar, at the cost of a possible prorated invoice between trial end and the first anchor date, as described above. A trial generates a zero-amount invoice at creation, so do not treat the first `invoice.paid` as proof that money was collected. Stripe sends `customer.subscription.trial_will_end` three days before a trial ends, which is the moment to check that a payment method exists before the first real charge.

backdate_start_date

Per Stripe's backdating guide, `backdate_start_date` takes a past timestamp and sets the subscription's start date to it. On its own, the first invoice includes the time from that start date to now. With `proration_behavior: none`, `start_date` is backdated but nothing is charged for the elapsed time, which is the usual choice when the customer already paid in a previous system. Combined with `billing_cycle_anchor`, Stripe creates a prorated item for the time between the backdated start and the anchor. Stripe's migration example: on October 15, a subscription that started September 1 and renews November 1 is created with `backdate_start_date` September 1 and `billing_cycle_anchor` November 1; Stripe immediately invoices the pro-rata amount for September 1 to November 1 and issues the next invoice on November 1.

Classic billing mode bills the backdated period as one prorated line; flexible mode creates one line per natural billing period. Coupons on a backdated subscription start counting from the backdated date, so a short repeating coupon can be used up before the first real invoice. For the full cutover process, see our guide to migrating subscriptions to Stripe Billing.

Mixed-interval subscriptions

Mixed-interval subscriptions require flexible billing mode (API version 2025-06-30.basil or later), and every item's interval must be a multiple of the shortest: one month with one year works, two months with three months does not. In Stripe's example, a monthly item and a quarterly item created on January 1 share the first invoice, the monthly item renews alone on February 1 and March 1, and both renew on April 1. Read item-level periods, not the subscription's. If all retries for one item's invoice fail, dunning applies to the whole subscription.

How do you test billing dates and prorations with test clocks?

Use a test clock: create a clock frozen at a chosen time in a sandbox, attach a new customer to it, create the subscription, then advance the clock and inspect the invoices and events that result. It is the only practical way to see a month-end renewal, a leap-year February or an annual anchor without waiting for real time to pass.

The details that trip teams up are all in Stripe's test clock guide:

  • The clock's time can only move forward, and each advance can be at most two intervals of the shortest price on the clock: two months at a time for a monthly subscription.
  • A clock can hold up to three customers, each with up to three subscriptions or schedules. The customer is attached with `test_clock` at creation; the subscription is linked through the customer.
  • Advancing takes a few seconds. Wait for `test_helpers.test_clock.ready`, or poll the clock until its status is ready, before you assert anything.
  • Renewal invoices stay in draft for about an hour, so advance an extra hour or two to see them finalized and paid.
  • Clocks and everything attached to them are deleted automatically after 30 days, or when you delete the clock.
Simulating a 1st-of-month anchor with a test clock (Node.js)
// Sandbox only: simulate a Jan 15 sign-up on a 1st-of-month anchor.
const at = (y, m, d, h = 0) => Math.floor(Date.UTC(y, m - 1, d, h) / 1000);

async function advanceTo(clockId, frozenTime) {
  await stripe.testHelpers.testClocks.advance(clockId, { frozen_time: frozenTime });
  for (let attempt = 0; attempt < 60; attempt += 1) {
    const clock = await stripe.testHelpers.testClocks.retrieve(clockId);
    if (clock.status === "ready") return clock;
    await new Promise((resolve) => setTimeout(resolve, 2000));
  }
  throw new Error("Test clock did not become ready");
}

export async function runAnchorSimulation(priceId) {
  const clock = await stripe.testHelpers.testClocks.create({
    frozen_time: at(2027, 1, 15, 12),
    name: "Anchor on the 1st",
  });

  const customer = await stripe.customers.create({
    email: "anchor-test@example.com",
    test_clock: clock.id,
  });
  const pm = await stripe.paymentMethods.attach("pm_card_visa", { customer: customer.id });
  await stripe.customers.update(customer.id, {
    invoice_settings: { default_payment_method: pm.id },
  });

  const sub = await stripe.subscriptions.create({
    customer: customer.id,
    items: [{ price: priceId }],
    billing_cycle_anchor_config: { day_of_month: 1, hour: 0, minute: 0, second: 0 },
  });

  // Renewal invoices sit in draft for about an hour, so step past it.
  await advanceTo(clock.id, at(2027, 2, 1, 2));
  await advanceTo(clock.id, at(2027, 3, 1, 2));

  const invoices = await stripe.invoices.list({ subscription: sub.id });
  for (const invoice of invoices.data) {
    console.log(invoice.billing_reason, invoice.status, invoice.amount_due,
      invoice.lines.data.map((l) => [l.period.start, l.period.end]));
  }
  return { clockId: clock.id, subscriptionId: sub.id };
}

A useful anchor test matrix covers mid-month sign-ups with and without proration, anchors on the 29th to 31st across leap and non-leap Februaries, upgrades and downgrades under each `proration_behavior`, a reset in each billing mode you run, trials ending before and after the anchor, a backdated migration and a plan change with an unpaid invoice. Write the expected dates and totals down first, then compare. Our Stripe integration guide covers sandboxes more broadly.

Which webhook events will you see, and what changes in customer.subscription.updated?

Creating an anchored subscription sends `customer.subscription.created` and, unless the partial period is free, `invoice.created`, `invoice.finalized` and `invoice.paid` for the prorated invoice. Every reset, plan change or trial change sends `customer.subscription.updated`, and each renewal on the anchor date sends the invoice events again.

Stripe sends `customer.subscription.updated` when a subscription starts or changes, including renewals, plan changes and discounts. For anchor work, re-read `billing_cycle_anchor`, each item's `current_period_start` and `current_period_end`, `trial_end` and `status`. Use `previous_attributes` for analytics and emails, but set state from the current subscription. Other events that matter for billing dates:

  • `invoice.upcoming` arrives a configurable number of days before a renewal. It is a good moment to add invoice items to that renewal.
  • `invoice.created` for a renewal opens the roughly one-hour draft window. If Stripe does not get a successful response to it, finalizing invoices with automatic collection can be delayed by up to 72 hours, so acknowledge it quickly.
  • `invoice.paid` with an active subscription is the signal to extend access to the new period; `invoice.payment_failed` starts your retry and grace logic.
  • `customer.subscription.trial_will_end` fires three days before a trial ends, which is also three days before an anchor set by that trial.

We cover event handling, ordering and access rules in detail in Stripe subscription webhooks: which events to handle, and the retry side, including how subscription billing retries and dunning work, in a separate guide. The general rules for verifying signatures, deduplicating events and coping with out-of-order delivery are in our webhook best practices.

Common billing_cycle_anchor mistakes

Most anchor bugs come from local-time arithmetic, from not choosing a `proration_behavior` on purpose, and from code written for one API version or billing mode running against another. These are the ones we see most often in a Stripe billing integration:

  1. Computing month-end dates yourself. Use `billing_cycle_anchor_config` with `day_of_month: 31` instead of building the last day of next month in code.
  2. Leaving the time fields out of billing_cycle_anchor_config. The anchor inherits the creation time of day, so renewals are scattered across the day instead of at the boundary you meant.
  3. Building timestamps in server local time. The anchor is UTC. A container in one region and a laptop in another will produce different anchors from the same code.
  4. Sending the string "now" after upgrading to 2026-09-30.endive or later. The update parameter is now an object with `type`. Upgrade the code and the webhook endpoint versions together.
  5. Assuming a reset always invoices. In flexible billing mode, a reset to now needs `always_invoice` to bill immediately.
  6. Resetting with proration_behavior: none. The customer pays again for days already paid. Stripe warns this can overcharge.
  7. Forgetting that interval changes move the date. In classic mode, monthly to yearly resets the anchor and charges immediately, even with `none`; no credit is given for unused time in that case.
  8. Prorating against an unpaid invoice. The customer is credited for time they did not pay for. Disable prorations and void the old invoice.
  9. Previewing without proration_date. The amount the customer approved is not the amount they are charged, because time moved on between the two calls.

From here, this article is about Cevrynt

How Cevrynt approaches Stripe Billing dates and prorations

Cevrynt, Inc. is a Stripe partner in the Stripe Partner Ecosystem. The partnership belongs to the company; the certifications belong to the people who hold them. Arindam Pradhan, our founder and engineering lead, is a Stripe Certified Professional (Implementation Architect). Anupam Pradhan and Subhadip Giri, a full stack developer, are each Stripe Certified Professionals (Payments Architect).

On billing-date work we start by writing down the rules: which day and time zone renewals use, what the first invoice should contain, what happens on upgrades, downgrades and interval changes, and which API version and billing mode each subscription runs on. We then build the subscription and plan-change code in your repository and Stripe account, and run a test-clock matrix covering month-end dates, trials, resets and plan changes in Stripe test mode before anything reaches customers.

Every Stripe Billing project is scoped and priced in writing after a technical review. You can read how we work on our Stripe Billing service page, see the wider Stripe services overview, or tell us what your renewal dates and prorations should do.