Updating subscription contract skips next billingCycle

Current time is 2025-11-19T17:00:00Z

Let’s say I create a simple subscription contract using subscriptionContractAtomicCreate:

"subscriptionContract": {
  "id": "gid://shopify/SubscriptionContract/45075956005",
  "status": "PAUSED",
  "createdAt": "2025-11-19T17:00:00Z",
  "nextBillingDate": "2025-11-20T08:00:00Z",
  "billingPolicy": {
    "interval": "MONTH",
    "intervalCount": 1,
    "minCycles": null,
    "maxCycles": null
  }
}

If I query subscription billing cycles, I get:

"subscriptionBillingCycles": {
    "nodes": [
      {
        "cycleIndex": 1,
        "cycleStartAt": "2025-11-19T17:00:00Z",
        "cycleEndAt": "2025-11-20T08:00:00Z",
        "billingAttemptExpectedDate": "2025-11-20T08:00:00Z",
        "skipped": false,
        "status": "UNBILLED"
      },
      {
        "cycleIndex": 2,
        "cycleStartAt": "2025-11-20T08:00:01Z",
        "cycleEndAt": "2025-12-20T08:00:00Z",
        "billingAttemptExpectedDate": "2025-12-20T08:00:00Z",
        "skipped": false,
        "status": "UNBILLED"
      },
      {
        "cycleIndex": 3,
        "cycleStartAt": "2025-12-20T08:00:01Z",
        "cycleEndAt": "2026-01-20T08:00:00Z",
        "billingAttemptExpectedDate": "2026-01-20T08:00:00Z",
        "skipped": false,
        "status": "UNBILLED"
      },
      ...
    ]
  }

So far everything looks good and works as expected.

The problem arises if I try to update this subscription contract.

Let’s say I want to change the billingPolicy.maxCycles

I create subscription contract draft:

"subscriptionContractUpdate": {
  "draft": {
    "id": "gid://shopify/SubscriptionDraft/580998136101",
    "nextBillingDate": "2025-11-20T08:00:00Z",
    "billingCycle": null,
    "billingPolicy": {
      "interval": "MONTH",
      "intervalCount": 1,
      "minCycles": null,
      "maxCycles": null
    },
    "concatenatedBillingCycles": {
      "nodes": []
    }
  },
  "userErrors": []
}

And then I run a simple subscriptionDraftUpdate mutation for the draft:

"subscriptionDraftUpdate": {
  "draft": {
    "id": "gid://shopify/SubscriptionDraft/580998267173",
    "nextBillingDate": "2025-11-20T08:00:00Z",
    "billingCycle": null,
    "billingPolicy": {
      "interval": "MONTH",
      "minCycles": 2,
      "maxCycles": 10
    },
    "concatenatedBillingCycles": {
      "nodes": []
    }
  },
  "userErrors": []
}

Once I commit the changes using subscriptionDraftCommit:

"subscriptionDraftCommit": {
  "contract": {
    "id": "gid://shopify/SubscriptionContract/45075956005",
    "nextBillingDate": "2025-11-20T08:00:00Z",
    "orders": {
      "nodes": []
    }
  },
  "userErrors": []
}

When I query the billing cycles again I can see that the next billing cycle (cycleIndex: 1) billingAttemptExpectedDate jumps to December - the billingCycle for November is skipped:

"subscriptionBillingCycles": {
  "nodes": [
    {
      "cycleIndex": 1,
      "cycleStartAt": "2025-11-19T17:00:00Z",
      "cycleEndAt": "2025-12-19T17:00:00Z",
      "billingAttemptExpectedDate": "2025-12-19T17:00:00Z",
      "skipped": false,
      "status": "UNBILLED"
    },
    {
      "cycleIndex": 2,
      "cycleStartAt": "2025-12-19T17:00:01Z",
      "cycleEndAt": "2026-01-19T17:00:00Z",
      "billingAttemptExpectedDate": "2026-01-19T17:00:00Z",
      "skipped": false,
      "status": "UNBILLED"
    },
    {
      "cycleIndex": 3,
      "cycleStartAt": "2026-01-19T17:00:01Z",
      "cycleEndAt": "2026-02-19T17:00:00Z",
      "billingAttemptExpectedDate": "2026-02-19T17:00:00Z",
      "skipped": false,
      "status": "UNBILLED"
    }
  ]
}

Hi @simposk,

This looks to be expected behaviour documented in our Shopify.dev docs, where the billing cycles will get recalculated when the billing or delivery policies are updated on the subscription contract.

Billing and delivery policies change

When the billing and delivery policies change for the source subscription contract, billing cycles adjust as follows:

  • Current billing cycle: Adjusts its end date based on the new interval and anchor configuration
  • Future billing cycles: Follow the new cadence starting from the adjusted current cycle’s end date
  • Past billing cycles: Remain unchanged

In this case, you’re editing the Billing Policy cycles, minCycles and maxCycles, which is causing the current billing cycle to be updated and adjusted based on the billing policies interval, which is set at MONTH.

So since the original billing cycle was created manually, had an interval of MONTH, with a manually created nextBillingDate to be 1 day after the cycle started. Once you edited the billing policy it automatically recalculated the cycle so that the nextBillingDate is following the actual interval set.


Additionally, I tried to replicate this behaviour on my test store as well, and I haven’t been able to, can you provide more details on the initial API call you are using to create the subscription contract. When I create a subscription with the subscriptionContractAtomicCreate mutation, with nextBillingDate set 1 or 2 days in the future, then query the billing cycles afterwards, it is automatically updating the date on the billing cycle without editing the contract.

Could you possibly share the full subscriptionContractAtomicCreate mutation that you are using to create the billing cycles with the billingAttemptExpectedDate set 1 day in the future, specifically you can share the following so we have the full context on the API call that lead to this:

  • The full plain text HTTP Request, including URL, Body, and Headers (no access tokens)
  • The full plain text HTTP Response, including Body and Headers

Here’s some screenshots showing my replication where it is setting the billingAttemptExpectedDate in the actually billing cycle based on the billing policy interval.