Skip to main content
POST
cURL
Imports a subscription you already bill in Stripe onto a paid Ledger plan. Link the customer’s Stripe customer first with Link a Stripe customer. The plan’s monthly price must equal the subscription’s. The full walkthrough is Migrate from Stripe Billing. Ledger moves the subscription’s item to the plan’s own Stripe Price, with no proration: nothing is charged now, and the renewal date and amount stay the same. It then grants the plan’s credits for Stripe’s current period and treats the subscription like one it sold: each paid renewal grants the next period’s credits, and upgrades and cancellations work through Ledger. Send startingBalance to carry over the credits the customer has left this period; it defaults to the plan’s monthly credits. An active subscription, one set to cancel at the period end, and a trialing one can be imported; a trial becomes the plan’s first period. Ledger refuses, without changing anything:
  • 400 subscription_past_due, subscription_incomplete, subscription_ended, subscription_not_billable or latest_invoice_unpaid: collect or void the open invoice in Stripe first, or don’t import an ended subscription.
  • 400 unsupported_subscription with a reason: something Ledger can’t bill yet: multiple_items, quantity, price_mismatch, not_monthly, metered_price, send_invoice, discount, schedule, pending_update, pause_collection, billing_thresholds, pending_invoice_items or customer_balance.
  • 409 stripe_customer_not_linked or stripe_customer_mismatch: link the subscription’s own Stripe customer first.
  • 409 multiple_subscriptions: the Stripe customer has another subscription that isn’t canceled.
  • 409 subscription_exists: the customer already has a paid plan or a pending Checkout. An active free plan is replaced by the import.
Ledger checks all of this before it changes Stripe. If something changes in Ledger while it switches the Price (a Checkout starts for the same customer, say), the refusal has a message: the subscription is already on the plan’s Price, with nothing charged, but not imported. Fix what it names and import again. Importing the same subscription onto the same plan again returns it with imported: false and changes nothing; onto another plan, it returns 409 subscription_already_imported. Like attach, the import needs a healthy signed webhook endpoint (502 stripe_not_configured or stripe_webhook_unhealthy), because the credits for every later renewal arrive by webhook. It works in Live and doesn’t need Ledger’s live-charge approval, since it starts no charge. The key needs the billing:write permission.

Authorizations

Authorization
string
header
required

Bearer authentication header of the form Bearer <token>, where <token> is your auth token.

Body

application/json
customerId
string
required

The Ledger customer. Link its Stripe customer first with POST /v1/customers/stripe-customer.

Required string length: 1 - 255
stripeSubscriptionId
string
required

The customer's existing subscription in your Stripe account.

Maximum string length: 255
planId
string
required

The paid Ledger plan it becomes. Its monthly price must equal the subscription's.

Required string length: 1 - 255
startingBalance
integer

The plan credits the customer has left this period, to carry over their usage. Defaults to the plan's monthly credits.

Required range: x >= 0

Response

The subscription is the customer's Ledger plan

subscriptionId
integer
required
planId
string
required
status
enum<string>
required
Available options:
active
currentPeriodStart
string<date-time>
required
currentPeriodEnd
string<date-time>
required
cancelAtPeriodEnd
boolean
required

The subscription ends at the period end in Stripe; Ledger ends the plan then.

grantedCredits
integer
required

Credits granted for the current period.

Required range: x >= 0
imported
boolean
required

False when the subscription had already been imported.