Subscriptions (add-on)
One customer's billing record, and the actions that pause, extend, cancel or restart it.
One customer's billing record. Reads need the view permission; anything that moves money or ends access needs manage. See Subscriptions.
List subscriptions
/api/v1/kahunacart/subscriptions
A page of subscriptions, each with its status, plan, customer, billing mode, payment provider, current period, next billing date and frozen amount. The response also carries a count per status, the full status vocabulary and the base currency. q matches an email address or a prefix of the subscription's own link hash, which are the two things a support conversation ever starts with. Filters this endpoint does not recognise are dropped rather than passed through, and per_page is capped at 200 with the figure actually used reported back.
Parameters
| Name | Type | Description |
|---|---|---|
| q optional | string | Match on the customer's email address or on a prefix of the subscription's link hash |
| status optional | string |
incomplete is a checkout whose first payment never arrived, past_due is inside the grace period with an invoice outstanding and still counts as live, expired is grace exhausted and is not resumable
One of:
incomplete, trialing, active, past_due, paused, cancelled, expired |
| billing_mode optional | string |
provider means the payment platform runs the calendar and charges a stored card; invoice means this store runs the calendar and emails a pay link each period
One of:
provider, invoice |
| provider optional | string | Payment provider slug, for example stripe, polar or offline |
| plan_id optional | integer | Only subscriptions on this plan |
| customer_id optional | integer | Only this customer's subscriptions |
| sort optional | string |
A column to sort every matching subscription by before the page is cut; leave out for newest first
One of:
customer, plan, status, billing, amount, next |
| dir optional | string |
One of:
asc, descDefault:
asc |
| page optional | integer |
At least 1.
Default:
1 |
| per_page optional | integer |
At least 1, at most 200.
Default:
25 |
Get a subscription
/api/v1/kahunacart/subscriptions/{id}
One subscription in full: the row with its status, frozen amount, current period, next billing date, billing mode, provider and failed-attempt count; the plan behind it; every order it has produced, which are ordinary KahunaCart orders with numbers, receipts and invoices of their own; and the whole timeline. Read the timeline rather than reasoning from the status, because a status says where a subscription is and only the timeline says how it got there, which is what answers why a card was charged twice in March. Read this before acting on a subscription, since which actions are legal depends on the status it is in now.
Parameters
| Name | Type | Description |
|---|---|---|
| id required | integer | Subscription id |
Subscription metrics
/api/v1/kahunacart/subscriptions/metrics
The store's recurring-revenue figures right now: monthly and annual recurring revenue per currency, the counts of active, trialing, past-due and paused subscriptions, and how many plans exist. MRR is every live subscription's amount restated monthly, using 12 months, 52 weeks or 365 days to the year, computed in integer minor units with one rounding at the end. Only active and past_due count toward it, because both are money the store is contracted to receive; trials are reported separately since nothing has been contracted yet. Currencies are kept apart and never added together, with the store's own as the headline.
Cancel a subscription
/api/v1/kahunacart/subscriptions/{id}/cancel
End a subscription, at the end of the period already paid for or immediately. at_period_end defaults to true and that default is the important half: a customer who cancels in week one of a month they have paid for keeps the month, and cancelling now throws that paid period away, so it has to be asked for by name. At period end is legal from active, trialing, past_due and paused, and a subscription that has already ended is refused with 422; cancelling now is legal from anything not already cancelled or expired, an abandoned incomplete checkout included. On a provider-mode subscription the platform is told first and the store's own row changes either way, so read mirror on the response: a state of failed means the agreement Stripe or Polar still holds will charge the customer again next month whatever this store says, and somebody has to finish the job at the provider. There is no undo for cancelling now; a scheduled cancellation is undone with uncancel_subscription.
Parameters
| Name | Type | Description |
|---|---|---|
| id required | integer | Subscription id |
| at_period_end optional | boolean |
True keeps the period already paid for and stops the renewal; false ends access now and loses the remainder of that period
Default:
true |
| reason optional | string | Recorded on the timeline the customer can read, and carried on the cancellation event |
Undo a scheduled cancellation
/api/v1/kahunacart/subscriptions/{id}/uncancel
Clear a cancellation scheduled for the end of the period, which is what a retention call ends in. Refused with 422 when nothing was scheduled, rather than quietly succeeding, because telling a customer they are still subscribed when nobody had cancelled makes them doubt every other message. Also refused on a subscription that has already ended. The store's record always changes, since this only restores something the store took away; the platform is asked to resume the agreement as well, and mirror on the response says whether it agreed, could not, or had nothing to be told.
Parameters
| Name | Type | Description |
|---|---|---|
| id required | integer | Subscription id |
Pause billing
/api/v1/kahunacart/subscriptions/{id}/pause
Stop the calendar without ending the subscription. Nothing renews once it is paused and it can be resumed later. Legal only from active and trialing; anything else, a cancelled or expired subscription included, is refused with 422 before the payment platform is asked anything. This is the one action that does not go ahead locally when the platform will not have it: a pause the platform did not accept would stop access while the card went on being charged, so a mirror state of unsupported or failed leaves the row untouched and the subscription still running. Most platforms schedule a pause rather than applying it, because the customer has already paid for the period they are in — Polar and Stripe both do. Where the platform reports the agreement as still live, the response's subscription carries pause_at_period_end: true and pauses_at (the epoch it stops), the status stays active, and the subscription goes on billing until then; the status moves to paused when the platform's own event says billing has stopped. Call resume_subscription to undo either kind. Invoice-mode subscriptions have no platform to ask and pause immediately.
Parameters
| Name | Type | Description |
|---|---|---|
| id required | integer | Subscription id |
| reason optional | string | Recorded on the timeline the customer can read |
Resume billing
/api/v1/kahunacart/subscriptions/{id}/resume
Start the calendar again on a paused subscription, returning it to active — or call off a pause that was scheduled and has not taken effect, which is the same call at the platform and leaves the subscription running as it was. Legal from paused, and from a live subscription carrying pause_at_period_end; anything else is refused with 422, and an expired subscription can never be resumed at all, because grace ran out and the customer has to subscribe again. Unlike pausing, this always goes ahead in the store's own record even where the platform did not confirm it, since it only ever restores something the store took away. Read mirror for whether the platform agreed.
Parameters
| Name | Type | Description |
|---|---|---|
| id required | integer | Subscription id |
Extend the current period
/api/v1/kahunacart/subscriptions/{id}/extend
Push the current period out by a number of days without billing for them, moving the next billing date with it. A negative number pulls the period in instead. Nothing is charged and no order is created, so this is the store giving something away: use it for an outage, a support case that dragged on, or a mistake being corrected. The note is required and is written into the event at the moment it happens, because it appears on the customer's own timeline and somebody will ask six months later who authorised it. Zero days, more than 3650 days in either direction, and a subscription that has already ended are each refused with 422. Nothing is mirrored to the payment platform, so on a provider-mode subscription the platform's own charge date does not move.
Parameters
| Name | Type | Description |
|---|---|---|
| id required | integer | Subscription id |
| days required | integer | Days to move the period end by; negative pulls it in, and zero is refused |
| note required | string | Why this was given, recorded on the timeline the customer can read |
Add a note to a subscription
/api/v1/kahunacart/subscriptions/{id}/note
Write a free-text note onto a subscription's timeline. Changes nothing about the billing, the period or the status; it records something, in the one place both the merchant and the customer read. Trimmed and stored to 2000 characters, and an empty note is refused with 422.
Parameters
| Name | Type | Description |
|---|---|---|
| id required | integer | Subscription id |
| note required | string | Visible on the timeline the customer can read |
Resend a renewal pay link
/api/v1/kahunacart/subscriptions/{id}/resend-pay-link
Queue the pay-link email again for the renewal invoice a customer already owes, and record the resend on the timeline. It is the same message the billing calendar sends, so a resend and a reminder are the same email. Invoice mode only: a provider-mode subscription is billed by its platform and has no pay link, which is a 422. It does not build an invoice, because a resend that quietly raised one would let a support call bring a billing period forward by two weeks without anybody deciding to, so a subscription with nothing outstanding, or whose renewal was cancelled or has expired past its grace period, is also a 422. Renewal orders are built by the billing sweep, which runs off the store's scheduler, so on a store whose scheduler is not running there will never be an invoice to resend. Refused with 422 on an installation with no job queue.
Parameters
| Name | Type | Description |
|---|---|---|
| id required | integer | Subscription id |
extend refuses a zero-day extension, one over the cap, or one with no note, each with a 422 saying which. resend-pay-link is invoice mode only and answers 422 on a provider-billed subscription, because there is no pay link to send. A refusal from the state machine carries its reason as a sentence and a machine code in the X-KahunaCart-Error header.