Cancel subscription
Cancel an active subscription.
Source src/operations/cancelSubscription.ts#L38 · handleCancelSubscriptionsrc/operations/cancelSubscription.ts#L83 · assertMayCancelsrc/operations/cancelSubscription.ts#L109 · lifecycleMarker
Cancel subscription
cancelSubscription marks an active Subscription CANCELED,
so the background worker's renewal sweep stops billing
it. It moves no money.
You submit it with just the subscription id:
let outcome = await economy.submit({
kind: "cancelSubscription",
idempotencyKey: "idem_1",
actor: { kind: "user", userId: "usr_a" },
subscriptionId: "sub_abc",
});
// outcome.status === "committed"
Canceling forfeits the rest of the period already paid for — there is no refund, so there is nothing to post to the ledger.
Parameters
Every field below is required; cancelSubscription carries no optional payload.
| Field | Type | Default | Description |
|---|---|---|---|
idempotencyKey | string | — | Makes a retried request run at most once. |
actor | Principal | — | Who is asking. |
subscriptionId | string | — | The subscription to cancel. Non-blank. |
Returns
cancelSubscription returns an Outcome.
On success the status is committed. The outcome carries a placeholder
Transaction with empty legs, because the
cancel only flips the subscription's state and records no money moving.
A repeat of the same idempotencyKey returns duplicate. A subscription that is missing or
already CANCELED returns rejected with UNKNOWN_SUBSCRIPTION — see Reason codes.
Postings
None. A cancel is a status change only, so the placeholder transaction posts no double-entry legs and advances no account's hash chain.
Authorization
A user actor may cancel only their own
subscription. The handler loads the record and compares its owner to the actor's userId;
a mismatch throws AUTH.UNAUTHORIZED.
A system or operator actor may cancel anyone's subscription. cancelSubscription is
not a privileged-only operation.
Reason codes
cancelSubscription returns one RejectionCode
as a rejected outcome — a normal "no", not a thrown fault:
| Code | When |
|---|---|
UNKNOWN_SUBSCRIPTION | No subscription matches the id, or it is already CANCELED. |
ECONOMY_PAUSED | A maintenance window is in effect and the actor is a user. The decline carries resumesAt. |
A blank or whitespace-only subscriptionId throws OP.MALFORMED instead — that is malformed
client input, caught before the store is touched. A user actor canceling a subscription they
do not own throws AUTH.UNAUTHORIZED.
Preconditions and invariants
The ownership check runs only after the subscription is confirmed to exist and be cancelable.
So probing a missing or already-canceled id returns the same UNKNOWN_SUBSCRIPTION answer
regardless of caller, and never reveals whether the id exists.
Canceling is final for that record: a CANCELED subscription stays canceled, and a second
cancel of the same id returns UNKNOWN_SUBSCRIPTION rather than committing again. To bill the
same SKU and seller again, the user runs subscribe
to open a fresh subscription.