Skip to main content

Packages & Add-ons (Admin)

Audience: Ops · Super-admins · Billing/Finance · Support · Where in app: /admin/packages, /admin/addons, /admin/agency-packages, /admin/agency-addons, /admin/agency-invites · Access: Super-admin only (verify — admin-portal routes behind the admin layout).

Packages are the subscription plans a company can buy; per-package feature flags and the AI-credit allotment are what gate what each tenant can actually do. Add-ons are extras stacked on top of a subscription (more AI credits, more users, extra agents). The agency variants are the equivalent catalog for white-label agencies: agency packages/add-ons size what an agency resells, and agency invites bundle starter packages + credits for onboarding new agencies. This page documents every field, because editing them changes billing and entitlements.

What it does

  • Packages (/admin/packages) — create/edit/delete the company-facing plans: pricing, trial terms, limits, AI credits, and the full per-package feature matrix.
  • Add-ons (/admin/addons) — create/edit/delete subscription extras, typed ai_credits / additional_users / outbound_agent / inbound_agent, billed one-time or recurring; optionally tailored to a single agency.
  • Agency Packages (/admin/agency-packages) — plans sold to agencies (priced by sub_accounts).
  • Agency Add-ons (/admin/agency-addons) — sub-account add-ons for agencies.
  • Agency Invites (/admin/agency-invites) — onboarding bundles: starter AI credits + a set of agency packages/add-ons + optional trial.

How it works

Frontend repos under frontend/src/repositories/admin/ (packages.js, addons.js, agency-invites.js, …) call the Node backend. Packages map to Package (collection packages), add-ons to Addon (addons), and the agency variants to AgencyPackage (agency_packages), AgencyAddon (agency_addons), AgencyInvite (agency_invites). Backend controllers: AgencyPackageController, AgencyAddonController, AgencyInviteController (+ the package/addon controllers).

Pricing rows hold Stripe ids: packages carry monthly_stripe_id / yearly_stripe_id; add-ons and agency entities carry stripe_product_id / stripe_price_id (stripe_id on Addon). The list/edit views are the source of truth that downstream subscription, entitlement, and AI-credit logic reads from.

Visibility quirk (packages): the list query sends v=y for private and v=n for public (getPackages in packages.js), mapping to the package visibility enum.

Screens & fields

Packages — /admin/packages (list), …/create, …/:id/edit

Source: packages/List.vue, packages/Edit.vuefrontend/src/repositories/admin/packages.js (/package routes). Backing model backend/app/models/PackageModel.js.

Plan / pricing / limits (top-level package fields):

FieldType / defaultNotes
title, sub_titlestringplan name
visibilityprivate / public (default public)controls who sees the plan
base_pricenumber, default 0
currencystring, default NZD
description, package_detailsstring
agency_idObjectId → agenciesset = agency-scoped plan
prices[]{name, amount, type: amount|percentage, country}per-country pricing rows
onetime_charges[]{name, amount}
annual_discountnumber, default 0
ai_creditsnumber, default 1000monthly AI-credit allotment for the plan
number_of_websitesnumber, default 1
default_users / max_users / additional_user_pricenumbersseat sizing; max_users 0 = unlimited
monthly_visitorsnumber, default 100
visitor_datanumber, default 1000visitor insights allotment
recordingnumber, default 1recordings (GB)
call_durationnumber, default 0minutes
free_trialyes / no (default no)
trial_limitnumber, default 1trial days
trial_without_cardbool, default false
expiry_daysnumber, default 7
statusactive / inactive (default active)
mobile_app / desktop_app / affiliate_programbool
popularbool"most popular" badge
monthly_stripe_id / yearly_stripe_idstringStripe price ids
user_emails[]string[]
Pricing-card style fieldsbg_color, *_txt_color, button colors, free_trial_button_text, buy_now_button_text, buy_now_button_visiblilitycosmetic for the public pricing widget

Feature flags (features.*) — the entitlement matrix. Numeric flags use -1 to mean unlimited. Booleans default to plan-tier values:

FlagDefaultMeaning
live_sessions, recorded_sessionstrue
audio_calls, video_callstruewith audio_call_duration_per_call / video_call_duration_per_call (-1 = unlimited)
call_forwarding, call_transfer, call_recordingtrue
chat_transfer, ai_chatbot, ai_conversationstrue
chat_auto_triggers5
ai_message_credits_per_month-1
auto_connect-1
multi_widget_styles, widget_styling, high_intent_widget, teamstrue
lead_intelligence20quota (was 0/-1); lead_intelligence_allowed (true) gates access; extended_lead_intelligence (false)
live_session_views-1
custom_integrations, dedicated_account_manager, priority_supportfalse
call_visitor_audio, call_visitor_videofalse
webhooksfalse
inbound_call_agent, outbound_call_agenttruewith number_of_inbound_agents (2) / number_of_outbound_agents (2) — were 1/0
number_of_whatsapp_agents2max WhatsApp agents (was 0); read into whatsapp_agents_allowed; must stay declared — schema is strict
whitelabel_widgetfalse

The Edit form surfaces the common subset (AI Credits, Number of Websites, Lead Intelligence allowed/number, Default/Max Users, Webhooks, Inbound/Outbound Call Agent + counts, Whitelabel Widget, Monthly Visitors, Visitor Data, Recordings GB, Call Duration, trial terms, status, visibility, expiry).

Add-ons — /admin/addons (list), …/create, …/:id/edit

Source: addons/List.vue, addons/Edit.vuefrontend/src/repositories/admin/addons.js (/addons). Backing model AddonModel.js.

FieldType / defaultNotes
title, sub_title, descriptionrequired strings
typerequiredUI options: ai_credits / additional_users / outbound_agent / inbound_agent (see ADDON_TYPES)
quantitynumber, default 1e.g. credits or seats granted
billing_typeone_time / recurring (default one_time)recurring attaches to the Stripe subscription and rebills; one_time is a single charge
stripe_idrequiredStripe product id
stripe_price_idstring
agency_idObjectId → agencies, default nullset ("Tailored for Agency") = only that agency sees it; null = global add-on
sort_ordernumber, default 0
statusactive / inactive (required)

Agency Packages — /admin/agency-packages

Model AgencyPackageModel.js (agency_packages). Plans for agencies, priced by sub-accounts.

FieldNotes
name, descriptionrequired
amountrequired number
currencydefault usd
sub_accountsrequired — how many sub-accounts the plan grants
trialbool, default false
stripe_product_id / stripe_price_idstring
statusactive / inactive (default active)
package_typeprivate / public (default private)
sort_orderdefault 1

Agency Add-ons — /admin/agency-addons

Model AgencyAddonModel.js (agency_addons).

FieldNotes
name, descriptionrequired
amountrequired
currencydefault usd
typeenum sub_account (default)
billing_typeone_time / recurring (default recurring)
visibilityprivate / public (default public)
statusactive / inactive (default active)
sort_orderdefault 1

Agency Invites — /admin/agency-invites

Source: frontend/src/repositories/admin/agency-invites.js (/agency-invites). Model AgencyInviteModel.js (agency_invites). Onboarding bundle for new agencies.

FieldNotes
name, descriptionrequired
initial_ai_creditsdefault 0 — starter AI credits granted on accept
agency_packages[]refs → agency_packages bundled into the invite
agency_addons[]refs → agency_addons
give_trialbool, default false
trial_durationnumber, default 0
sort_orderdefault 0
statusactive / inactive (default active)

Behaviors & edge cases

  • -1 = unlimited for numeric feature flags (durations, quotas, agent counts). Setting a finite number imposes a cap; 0 typically means none allowed.
  • Defaults changed (2026-07): new packages/companies get inbound and outbound calling on by default with caps 2 outbound / 2 inbound / 2 WhatsApp agents (were 1/1/0), and lead intelligence enabled at 20 lookups (was off/0). SubscriptionHelper mirrors the same fallbacks (?? 2, ?? 20) when a package omits the field.
  • AI credits live in two places: ai_credits on the package (recurring monthly allotment), quantity on an ai_credits add-on (top-up), and initial_ai_credits on an agency invite (starter grant). Editing the package value changes ongoing entitlement.
  • Agency scoping: a package with agency_id set, or an add-on with agency_id set ("Tailored for Agency"), is only visible to that agency. Null = global/visible to everyone.
  • Billing type matters for Stripe: recurring add-ons/agency-add-ons attach to the subscription and rebill each cycle; one_time is a single immediate charge. Agency add-ons default to recurring; platform add-ons default to one_time.
  • Currencies differ by catalog: packages default NZD; add-ons/agency entities default usd. Mismatches surface in revenue grouping — confirm currency on create.
  • Packages visibility filter is inverted in the query (v=y = private, v=n = public) — a UI/API mapping detail, not a data field.
  • Stripe ids are not auto-created here — the fields hold ids that must correspond to real Stripe products/prices for checkout to work.

Technical implementation

  • Models: backend/app/models/PackageModel.js, AddonModel.js, AgencyPackageModel.js, AgencyAddonModel.js, AgencyInviteModel.js.
  • Controllers: backend/app/controllers/AgencyPackageController.js, AgencyAddonController.js, AgencyInviteController.js (+ package/addon controllers); routes agencyPackageRoutes.js, agencyAddonRoutes.js, agencyInviteRoutes.js.
  • Frontend: repos in frontend/src/repositories/admin/ (packages.js, addons.js, agency-invites.js); views under frontend/src/views/admin/{packages,addons,agency-packages,agency-addons,agency-invites}/.