Skip to main content

Subscriptions

Audience: Sales / Management / Agency-success / Support · Where in app: /agency/subscription-management (overview, plans, addons, history, payment-method) and /agency/subscriptions (per-client list/detail) · Plan availability: Agency tier; payment actions gated by agency.subscription_payments

There are two billing relationships in the agency portal, and this page covers both:

  1. The agency → Knock Knock. The agency subscribes to an agency package (AgencyPackageModel) which grants a number of sub-account slots. Stripe bills the agency owner. This is the "Subscription Management" unified screen.
  2. The client → the agency. Each client company has its own SubscriptionModel pointing at an agency-defined client package. The agency controls these; the client can't self-serve. This is the per-client "Subscriptions" list/detail.

What it does

  • Subscription Management (unified, tabbed): Overview, Plans, Add-ons, Payment History, Payment Method.
  • Subscribe / change the agency package, with immediate upgrades and deferred downgrades.
  • Buy agency add-ons (extra sub-accounts), one-time or recurring.
  • View payment history / invoices and manage the payment method.
  • Per-client subscriptions list with company + status filters, and a detail view.
  • Cancel / reactivate the agency subscription and individual client subscriptions.

How it works

Agency subscription (AgencySubscriptionModel)

One agency subscription row per agency holds package_id (current paid plan), optional new_package_id (a scheduled downgrade), stripe_id, status (active / inactive / canceled), expiry_date, is_trial, and canceled_at / cancel_reason. Subscribing or upgrading creates/updates the Stripe subscription immediately and refreshes total_sub_accounts.

Deferred downgrade is the key behavior: when the agency changes to a cheaper plan (changePackage, POST /agencies/change-package), the change is not applied immediately. Instead:

  • Stripe schedules the price change at period end (scheduleSubscriptionPriceChangeAtPeriodEnd).
  • Locally, new_package_id is set; package_id stays on the plan the agency already paid for.
  • The renewal webhook applies the downgrade (swaps package_id, recalculates sub-account counts) when the cycle rolls over.
  • The agency keeps current capacity until then, and gets a "Your plan will change to X on " response + a downgrade email.

Upgrades (more expensive plan) are charged immediately via Stripe updateSubscription (or, during trial, the trial is canceled and a fresh paid subscription starts).

Per-client subscription (SubscriptionModel)

Each sub-account's subscription is created when the agency provisions/assigns a package. Its expiry_date is derived from the agency's own subscription expiry; remaining_ai_credits comes from the client package. The agency cancels/reactivates these via the company endpoints (see Managing Companies).

Source: frontend/src/views/agency/SubscriptionManagement.vue + frontend/src/components/agency/subscription/*, frontend/src/views/agency/subscriptions/{List,Detail}.vue, and AgencyController.

Configuration & options

Subscription Management tabs

TabRoute nameComponentPurpose
Overviewagency-subscription-overviewSubscriptionOverview.vueCurrent plan, trial banner/progress, capacity
Plansagency-subscription-plansSubscriptionPlans.vueSubscribe / change agency package
Add-onsagency-subscription-addonsSubscriptionAddons.vueBuy agency add-ons
Payment Historyagency-subscription-historyPaymentHistory.vueInvoices
Payment Methodagency-subscription-paymentPaymentMethodManager.vueManage card

Legacy redirects: /agency/buy-sub-account → Plans, /agency/subscription → Overview, /agency/invoices → Payment History.

Agency subscription actions (repository → endpoint)

ActionRepository fnEndpointNotes
SubscribesubscribeAgency({ package_id, trial })POST /agencies/subscribeWrapped with Rewardful affiliate data
First-time price calcgetSubscribeCalculations(packageId)POST /agencies/subscribe-calculationsReturns { monthly_price, gst, total_due, currency }
Change planchangeAgencyPackage({ package_id })POST /agencies/change-packageImmediate upgrade / deferred downgrade
Change calc (proration)getPackageChangeCalculations(packageId)POST /agencies/package-change-calculationsProrated amounts
Buy agency add-onbuyAgencyAddon(addonId)POST /agencies/addonExtra sub-accounts (one-time or recurring)
Cancel agency subcancelAgencySubscription({ reason })POST /agencies/unsubscribeStores cancel_reason
ReactivatereactivateAgencySubscription(subId)POST /agencies/reactivate-subscription
Retry failed paymentretryAgencyPayment(subId)POST /subscription/stripe-payment-retry/:id
InvoicesgetInvoices()GET /agency/invoices
AI credit usagegetCreditUsage()GET /agency/credit-usagePooled credit history

All payment/plan actions require agency.subscription_payments (owner always passes); changePackage returns 403 otherwise.

Per-client subscriptions (subscriptions/List.vue, Detail.vue)

ControlRepository fnEndpoint
List (filter by company + status)getSubscriptions({ companyId, status, page, pageSize })GET /agency/subscriptions
DetailgetSubscriptionById(id)GET /agency/subscriptions/:id
Active sub by usergetActiveSubscriptionByUserId(userId)GET /subscription/active/:userId
Cancel client subcancelCompanySubscription(companyId)POST /agencies/companies/:id/cancel-subscription
Reactivate client subreactivateCompanySubscription(companyId)POST /agencies/companies/:id/reactivate-subscription

Status filter values: active, cancelled, expired (UI badge colors map active→green, cancelled→red, expired→gray).

Agency package model fields (AgencyPackageModel)

FieldNotes
name, descriptionPlan identity
amount, currency (default usd)Price the agency pays
sub_accountsSlot capacity granted
trialWhether the plan offers a trial
stripe_product_id, stripe_price_idStripe linkage
status (active/inactive), package_type (private/public), sort_orderCatalog control

Agency add-on model fields (AgencyAddonModel)

FieldNotes
amount, currency, type (sub_account)What the add-on grants/costs
billing_type (one_time / recurring, default recurring)One-time charge vs. rebills each cycle
visibility (private/public), status, sort_orderCatalog control

Behaviors & edge cases

  • Deferred downgrade keeps capacity. Until the cycle ends the agency stays on the paid plan; new_package_id flags the pending change. A downgrade is blocked during trial and blocked if it can't hold the in-use sub-accounts (returns a message telling the agency to remove sub-accounts first).
  • Upgrades are immediate; trial upgrades cancel the trial and start a fresh paid subscription.
  • Billing responsibility split. Stripe bills the agency for the agency package + add-ons. Clients are billed by the agency off-platform — client SubscriptionModel rows track entitlement/expiry, not a client Stripe charge.
  • Trial banner. Overview shows an amber "Trial Active" (with progress bar) or red "Trial Expired" state driven by subscription.is_trial + expiry_date.
  • Payment issues. agency.payment_issue flag and companyPaymentIssue / dismissCompanyPaymentIssue endpoints surface dunning state; cancel reasons persist on the subscription.
  • Calculations are tax-inclusive. subscribe-calculations returns GST so the displayed total matches the eventual invoice.

Plan & limits

  • Sub-account capacity = the agency package's sub_accounts + any sub-account add-ons (verify exact slot counts and prices per plan — none are hard-coded in this cluster).
  • Trial availability/duration depends on the package's trial flag and the agency's trial_duration (verify).

Technical implementation

  • Views: frontend/src/views/agency/SubscriptionManagement.vue, frontend/src/components/agency/subscription/*, frontend/src/views/agency/subscriptions/{List,Detail}.vue
  • Routes: frontend/src/router/agency.js (subscription-management children + agency-subscriptions, agency-subscription-detail)
  • Repository: frontend/src/repositories/agency/index.js
  • Controller: backend/app/controllers/AgencyController.js (subscribe, changePackage, unsubscribe, reactivateSubscription, buyAddon, subscribeCalculations, packageChangeCalculations) — see backend service
  • Models: AgencySubscriptionModel (incl. new_package_id for deferred downgrade), AgencyPackageModel, AgencyAddonModel, SubscriptionModel (per-client)