Skip to content

Powered by Grav CMS + Helios Theme

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

GET /api/v1/kahunacart/subscriptions Permission kahunacart.subscriptions.view

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, desc
Default: asc
page optional integer At least 1.
Default: 1
per_page optional integer At least 1, at most 200.
Default: 25

Get a subscription

GET /api/v1/kahunacart/subscriptions/{id} Permission kahunacart.subscriptions.view

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

GET /api/v1/kahunacart/subscriptions/metrics Permission kahunacart.subscriptions.view

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

POST /api/v1/kahunacart/subscriptions/{id}/cancel Permission kahunacart.subscriptions.manage

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

POST /api/v1/kahunacart/subscriptions/{id}/uncancel Permission kahunacart.subscriptions.manage

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

POST /api/v1/kahunacart/subscriptions/{id}/pause Permission kahunacart.subscriptions.manage

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

POST /api/v1/kahunacart/subscriptions/{id}/resume Permission kahunacart.subscriptions.manage

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

POST /api/v1/kahunacart/subscriptions/{id}/extend Permission kahunacart.subscriptions.manage

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

POST /api/v1/kahunacart/subscriptions/{id}/note Permission kahunacart.subscriptions.manage

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
POST /api/v1/kahunacart/subscriptions/{id}/resend-pay-link Permission kahunacart.subscriptions.manage

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.