Billing overview

Manage subscriptions, invoices, and payment collection for recurring revenue.
View as Markdown

How billing works

PaymentKit handles recurring billing through three connected concepts: customers hold payment methods, subscriptions define billing schedules, and invoices track amounts owed.

When you create a subscription, PaymentKit:

  1. Generates an invoice for the first billing period
  2. Attempts payment using the customer’s default payment method
  3. Schedules the next billing cycle automatically
  4. Handles failures with smart retries and recovery emails

Subscription states

Every subscription exists in one of seven states. PaymentKit transitions subscriptions between states based on payment outcomes and scheduled events.

StateDescription
IncompleteSubscription created but awaiting initial payment.
ScheduledSubscription scheduled for a future start date.
TrialingFree trial period is active. No charges until the trial ends.
ActiveNormal billing cycle. Invoices generated and payments collected on schedule.
Past duePayment failed. The dunning system retries collection automatically.
PausedBilling temporarily suspended. No invoices generated while paused.
CancelledSubscription terminated. No further billing. This is a terminal state.

Create a subscription

Start billing a customer by creating a subscription with one or more prices.

  1. Go to Billing > Subscriptions
  2. Click Create Subscription
  3. Select a customer
  4. Add prices with quantities
  5. Set billing interval and start date
  6. Click Create

See Create a subscription for trial periods, discounts, and fixed-term options.

Pause a subscription

Temporarily stop billing without cancelling. The customer retains access through the paid period, and billing resumes automatically or on request.

Pause immediately

$curl -X POST https://api.paymentkit.com/api/{account_id}/subscriptions/{subscription_id}/pause \
>-H "Authorization: Bearer sk_live_..." \
>-H "Content-Type: application/json" \
>-d '{
> "pause_behavior": "pause_immediately"
>}'

Pause at period end

Let the customer use the service through the paid period, then pause:

$curl -X POST https://api.paymentkit.com/api/{account_id}/subscriptions/{subscription_id}/pause \
>-H "Authorization: Bearer sk_live_..." \
>-H "Content-Type: application/json" \
>-d '{
> "pause_behavior": "pause_at_end"
>}'

Auto-resume after a duration

Pause for a set number of billing cycles:

$curl -X POST https://api.paymentkit.com/api/{account_id}/subscriptions/{subscription_id}/pause \
>-H "Authorization: Bearer sk_live_..." \
>-H "Content-Type: application/json" \
>-d '{
> "pause_behavior": "pause_immediately",
> "pause_for_cycles": 2
>}'

Or resume on a specific date:

$curl -X POST https://api.paymentkit.com/api/{account_id}/subscriptions/{subscription_id}/pause \
>-H "Authorization: Bearer sk_live_..." \
>-H "Content-Type: application/json" \
>-d '{
> "pause_behavior": "pause_at_end",
> "resumption_date": "2026-05-01T00:00:00Z"
>}'
ParameterDescription
pause_behaviorpause_immediately or pause_at_end
pause_for_cyclesNumber of billing cycles to skip before auto-resume
resumption_dateExplicit datetime for auto-resume. Mutually exclusive with pause_for_cycles.

See Pause a subscription for resume behavior and proration details.

Resume a paused subscription

Reactivate a paused subscription immediately:

  1. Go to Billing > Subscriptions
  2. Select the paused subscription
  3. Click Resume subscription

When resumed:

  • A prorated invoice is created for the remaining time in the current period
  • Payment is attempted on the prorated invoice
  • The billing period advances to the next cycle

Cancel a subscription

End a subscription with control over timing and refunds.

Cancel immediately

$curl -X POST https://api.paymentkit.com/api/{account_id}/subscriptions/{subscription_id}/cancel \
>-H "Authorization: Bearer sk_live_..." \
>-H "Content-Type: application/json" \
>-d '{
> "refund_option": "prorated"
>}'

Cancel at period end

Let the customer use the service through the paid period:

$curl -X PATCH https://api.paymentkit.com/api/{account_id}/subscriptions/{subscription_id} \
>-H "Authorization: Bearer sk_live_..." \
>-H "Content-Type: application/json" \
>-d '{
> "cancel_at_period_end": true
>}'

Cancel on a specific date

Schedule cancellation for a future date:

$curl -X POST https://api.paymentkit.com/api/{account_id}/subscriptions/{subscription_id}/schedule-cancellation \
>-H "Authorization: Bearer sk_live_..." \
>-H "Content-Type: application/json" \
>-d '{
> "cancel_at": "2026-06-01T00:00:00Z",
> "refund_option": "none"
>}'

Refund options

OptionBehavior
noneCancel with no refund (default)
fullRefund the full paid amount for the current period
proratedRefund unused portion calculated by remaining seconds
cancel_unpaidVoid open invoices instead of refunding

Undo a scheduled cancellation

Remove a pending cancellation before it takes effect:

$# Clear cancel_at_period_end
$curl -X PATCH https://api.paymentkit.com/api/{account_id}/subscriptions/{subscription_id} \
>-H "Authorization: Bearer sk_live_..." \
>-H "Content-Type: application/json" \
>-d '{
> "cancel_at_period_end": false
>}'
$
$# Clear scheduled cancel_at date
$curl -X DELETE https://api.paymentkit.com/api/{account_id}/subscriptions/{subscription_id}/scheduled-cancellation \
>-H "Authorization: Bearer sk_live_..."

See Cancel a subscription for preview mode and refund calculation details.

Update a subscription

Change subscription fields

Update metadata, payment method, or other settings:

$curl -X PATCH https://api.paymentkit.com/api/{account_id}/subscriptions/{subscription_id} \
>-H "Authorization: Bearer sk_live_..." \
>-H "Content-Type: application/json" \
>-d '{
> "description": "Enterprise plan",
> "default_payment_method_id": "pm_newcard456"
>}'
FieldDescription
descriptionFree-text description
default_payment_method_idPayment method for future collections
cancel_at_period_endSchedule cancellation at period end
trial_endExtend or shorten the trial period
coupon_idAttach a coupon (pass "" to remove)
promotion_codeApply a promotion code
collection_methodcharge_automatically or send_invoice
net_dPayment terms in days

Update subscription items

Add, remove, or change items with proration control:

$curl -X PATCH https://api.paymentkit.com/api/{account_id}/subscriptions/{subscription_id}/items \
>-H "Authorization: Bearer sk_live_..." \
>-H "Content-Type: application/json" \
>-d '{
> "items": [
> {"id": "si_existing123", "quantity": 10},
> {"price_id": "price_addon_storage", "quantity": 1},
> {"id": "si_old_addon", "deleted": true}
> ],
> "proration_behavior": "always_invoice"
>}'
OperationPayload
Add item{"price_id": "price_xxx", "quantity": 2}
Update quantity{"id": "si_xxx", "quantity": 10}
Swap price{"id": "si_xxx", "price_id": "price_yyy"}
Remove item{"id": "si_xxx", "deleted": true}

Proration behavior

BehaviorDescription
always_invoiceCreate invoice immediately and attempt payment
create_prorationsAdd prorated items to the next renewal invoice (default)
noneNo proration; changes apply at next renewal

See Update a subscription for upgrade/downgrade examples.

Reschedule billing

Change when the next invoice is generated:

$curl -X POST https://api.paymentkit.com/api/{account_id}/subscriptions/{subscription_id}/reschedule-billing \
>-H "Authorization: Bearer sk_live_..." \
>-H "Content-Type: application/json" \
>-d '{
> "next_billing_date": "2026-03-15T00:00:00Z",
> "create_proration": true
>}'

Use is_preview: true to see the proration amount without applying changes.

Trial periods

Offer a free trial before the first charge:

$curl -X POST https://api.paymentkit.com/api/{account_id}/subscriptions \
>-H "Authorization: Bearer sk_live_..." \
>-H "Content-Type: application/json" \
>-d '{
> "customer_id": "cus_abc123",
> "currency": "USD",
> "billing_interval": "month",
> "trial_start": "2026-02-01T00:00:00Z",
> "trial_end": "2026-02-15T00:00:00Z",
> "items": [{"price_id": "price_monthly_pro", "quantity": 1}]
>}'

During the trial:

  • Subscription is in Trialing state
  • A $0 invoice is generated for the trial period
  • Three days before trial ends, subscription.trial_will_end event fires
  • At trial end, the subscription activates and billing begins

Fixed-term subscriptions

Create subscriptions that automatically end after a set number of billing cycles:

$curl -X POST https://api.paymentkit.com/api/{account_id}/subscriptions \
>-H "Authorization: Bearer sk_live_..." \
>-H "Content-Type: application/json" \
>-d '{
> "customer_id": "cus_abc123",
> "currency": "USD",
> "billing_interval": "month",
> "total_billing_cycles": 12,
> "items": [{"price_id": "price_monthly_pro", "quantity": 1}]
>}'

The subscription cancels automatically after 12 billing cycles complete.

Payment collection

Control how invoices are paid:

MethodBehavior
charge_automaticallyPaymentKit charges the customer’s default payment method at each renewal
send_invoicePaymentKit generates an invoice and waits for the customer to pay manually

Set net_d to control when invoices are due. A value of 0 means due immediately. A value of 30 gives the customer 30 days after invoice finalization.

Failed payments and dunning

When a payment fails, PaymentKit automatically:

  1. Transitions the subscription to Past due
  2. Retries payment on a smart schedule
  3. Sends dunning emails with a link to update payment method
  4. Applies your configured end behavior if all retries fail

Dunning end behavior

Configure what happens when all retry attempts are exhausted:

BehaviorSubscriptionInvoice
cancel_and_uncollectibleCancelledMarked uncollectible
cancel_and_openCancelledKept open
past_due_and_uncollectibleStays past dueMarked uncollectible
past_due_and_openStays past dueKept open

See Automatic retries & emails for retry schedules and email configuration.

Preview upcoming invoice

See what the next invoice will look like before changes take effect:

$curl -X GET https://api.paymentkit.com/api/{account_id}/subscriptions/{subscription_id}/preview \
>-H "Authorization: Bearer sk_live_..."

The response includes line items, totals, and billing dates.

Webhook events

PaymentKit emits events at each subscription state change:

EventTrigger
subscription.createdSubscription created
subscription.updatedSubscription fields changed
subscription.pausedSubscription paused
subscription.resumedPaused subscription resumed
subscription.cancelledSubscription cancelled
subscription.trial_will_endTrial ends in 3 days
invoice.createdInvoice generated
invoice.paidPayment succeeded
invoice.payment_failedPayment failed

See Webhooks for setup and event handling.

Invoice states

Invoices track payment collection status:

StateDescription
DraftBeing prepared, not yet finalized
OpenFinalized and awaiting payment
PaidPayment successfully collected
Past duePayment overdue, in collection
UncollectibleMarked as uncollectible after failed recovery
VoidCancelled, no longer valid

See Create an invoice and Custom invoices for manual invoice management.