# Self-serve subscription state https://api-docs.lumar.io/docs/graphql/self-serve-billing Accounts that pay by card manage their own subscription — starting one, changing plan, buying add-ons, cancelling and resubscribing. This page covers the two fields that say where such an account stands and what it may do next, so a billing screen never has to work either out from raw status text. ## Two fields, two questions | Question | Field | | ---------------------------------------------------------- | ------------------------------------------------------------------- | | Is this account provisioned, or does it need to pay? | `Account.selfServeSubscriptionState` | | Where does its subscription stand, and what can it change? | `Account.selfServeAccountSubscription` → `state` and `capabilities` | `selfServeSubscriptionState` covers accounts with no subscription at all — one that never completed checkout, or that is not self-serve in the first place. `selfServeAccountSubscription` is `null` for those, so start from `selfServeSubscriptionState` when you are deciding whether to show a billing screen at all, and read the subscription's own fields once you are on it. Neither says what the account is **entitled** to. That stays with the `*Available` flags on `Account.subscription` — a subscription can be perfectly healthy and still not include the product you are gating on. ```graphql query GetSelfServeBillingState($accountId: ObjectID!) { getAccount(id: $accountId) { id selfServeSubscriptionState selfServeAccountSubscription { state status cancelAtPeriodEnd currentPeriodEnd trialEnd planCode planName capabilities { canCancel canReactivate canResubscribe canUpdate } } } } ``` **Variables:** ```json { "accountId": "TjAwN0FjY291bnQxMjM0NQ" } ``` ## Subscription state `selfServeAccountSubscription.state` is one typed value covering the whole lifecycle. Branch on it rather than on `status`, which carries the payment provider's own vocabulary and is not enumerated in this schema. | State | Meaning | | ------------------- | --------------------------------------------------------------------------------------------------------------------- | | `Active` | Paid, running, not set to cancel. | | `Trialing` | In a trial that converts to paid at `trialEnd`. | | `Cancelling` | Still live, but set to end. The customer keeps everything until it does. | | `PaymentFailed` | Payment is outstanding. Usually still granting access while the card is retried — do not read it as a loss of access. | | `PaymentIncomplete` | The first payment was never completed, so the subscription has never granted anything. | | `Paused` | Paused on our side; only Lumar can lift it. | | `Ended` | Over for good, by cancellation or an expired first payment. | Where a subscription qualifies for more than one, the more urgent wins: a cancelling trial reports `Cancelling`, and an outstanding payment reports `PaymentFailed` whatever else is set. `state` is deliberately wider than `cancelAtPeriodEnd`, which is only true when the end date falls on the current period — the usual case, and the one where `currentPeriodEnd` is the date to show. A cancellation dated anywhere else still reports `Cancelling`, so read `cancelAtPeriodEnd` before presenting `currentPeriodEnd` as the day access stops. ## Capabilities `capabilities` reports which subscription mutations the subscription's current state allows. Each flag is taken from that mutation's own state precondition, so a screen can render the buttons it is given instead of deriving them from a status string. They are eligibility, not a guarantee of success: a call can still fail for a reason that has nothing to do with the subscription's state. Resubscribing is the one to watch — it creates a new subscription and cannot inherit the cancelled one's card, so an account with no default payment method gets [`ACCOUNT_SUBSCRIPTION_PAYMENT_METHOD_MISSING`](./error-codes.md) and needs the billing portal, even though `canResubscribe` was `true`. | Flag | Mutation it gates | | ---------------- | ------------------------------------------------------------------------------------------------------------ | | `canUpdate` | `updateSelfServeAccountSubscription` — plan, billing interval and add-ons all move through this one mutation | | `canCancel` | `cancelSelfServeAccountSubscription` | | `canReactivate` | `reactivateCancellingSelfServeAccountSubscription` — call off a pending cancellation | | `canResubscribe` | `reactivateCancelledSelfServeAccountSubscription` — start again after the subscription ended | Plan and add-on changes are refused while a cancellation is pending: `canReactivate` is the way back, and the others become true again once it is called off. `canResubscribe` is not simply "the subscription ended". Reactivation applies only to a cancelled subscription; one whose first payment expired instead has to go through checkout again, which `selfServeSubscriptionState` reports as `CheckoutRequired`. Read the capability rather than inferring it from `Ended`. ### A note on freshness `state` and `capabilities` describe the subscription as Lumar last recorded it, which is why they cost nothing to read. An account is meant to hold one subscription at a time, but two checkouts completing together can leave it with more, and there the recorded one may not be the one Stripe is billing. If a mutation a capability offered is refused, re-query rather than treating it as final — and start from `selfServeSubscriptionState`, which asks Stripe directly.