Skip to main content
If you already sell monthly subscriptions with Stripe, you can put Ledger on top of them. Each customer keeps their subscription, card and renewal date. Ledger grants their plan’s credits for the current period now, and the credits for every later period when Stripe renews.

Connect your Stripe account

Ledger has to work in the Stripe account your subscriptions live in. On the dashboard’s Developer page, connect that account’s secret key: sk_test_… in Sandbox, sk_live_… in Live. Ledger creates a webhook endpoint in your account; check that it shows as healthy, since every renewal’s credits arrive through it. Try the whole migration in Sandbox first, with test-mode copies of a few customers.

Run one command

From your app’s repo, with a Sandbox API key in LEDGER_API_KEY (or saved with ledger login):
It reads your Stripe account (nothing changes yet) and:
  1. Proposes a catalog. One paid plan for each monthly USD Price your customers are on, named after its Product and priced like it. It asks each new plan’s monthly credits, and a feature with its credit cost if your ledger.catalog.json has none. A plan already in your config keeps its credits.
  2. Shows a report. Each plan, each customer it will import with their next renewal date, and each subscription it will skip, with the reason (see what can be imported).
  3. Asks before changing anything. Then it writes the plans to ledger.catalog.json, publishes the catalog, and imports each customer, one progress line each.
  4. Ends with a summary. Imported, already imported, skipped by reason, failed, and the range of next renewals.
Safe to rerun. Every step is idempotent. Progress and your answers are saved in .ledger/migrate-stripe.sandbox.json (gitignored), so after a failure or an outage you run the same command again: it asks nothing, skips the customers already imported, and retries the rest. It exits 1 when any customer failed. The command carries no usage over: each customer starts the current period with the plan’s full credits. To carry over what they’ve used, see Other ways.

Other ways

ledger migrate stripe is the way to migrate. If you need to script it yourself, for example to carry over usage with startingBalance, the command is built on calls you can make directly: Preview a Stripe migration (client.previewStripeMigration()) returns the same report as JSON, then for each customer create them, link their Stripe customer and import their subscription onto a plan with the same monthly price. Every call is safe to repeat.The onboarding skill runs the command for you when it finds Stripe subscriptions in your app. Connecting Stripe with OAuth and migrating in one click from the dashboard is planned next, on the same calls.

Customers on a free plan

Customers on a free plan don’t need an import. Create them and attach the free plan. You can still link their Stripe customer, so a later upgrade uses the card they already saved.

What changes in Stripe

Ledger only changes two things in your Stripe account:
  • Metadata. The customer and subscription get ledger_organization_id and ledger_env, and the customer also gets ledger_customer_id. Your own metadata keys stay as they are.
  • The subscription’s Price. Its item moves from your Price to the Ledger plan’s Price, which has the same amount, with no proration. Nothing is charged, no invoice is created (a trialing subscription gets a $0 one), and the renewal date stays the same. From the next renewal on, the invoice line shows the Ledger plan’s name.
Ledger needs the subscription on its own Price because it grants credits only for invoices it can verify against the plan. Your old Price stays in your account, unused by the imported subscription.

Which subscriptions can be imported

Ledger imports active subscriptions, subscriptions set to cancel at the period end (the plan ends when Stripe ends the subscription), and trialing subscriptions (the trial becomes the plan’s first period, with its credits). It refuses, without changing anything, a subscription it couldn’t bill correctly, and says why. The command reports these as skipped: Fix the cause in Stripe and run the command again; it picks up the newly importable customers.

Live

Migration works the same way in Live, with your Live API key and sk_live_ connection: ledger migrate stripe --live. It doesn’t need Ledger’s approval for live charges, because it starts no charge: your customers keep paying what they already paid, on the same dates. A later upgrade through Ledger is a new charge, and does need it. See Import a Stripe subscription and Link a Stripe customer for every field and error.