Agency Portal — Overview
Audience: Sales / Agency-success / Management (engineers for the technical section) · Where in app: Agency Portal (separate nav once an agency exists) · Plan availability: Enterprise / agency tier (the agency portal is its own subscription)
The agency portal turns Knock Knock into a reseller-friendly platform. An agency manages many client companies ("sub-accounts") under its own brand, sets its own pricing via custom packages, and bills its clients while paying Knock Knock for the underlying platform.
What it does
- One owner manages all sub-accounts from a single dashboard.
- Sell at any markup with agency packages and agency add-ons (custom pricing/currency).
- Create client accounts directly (no client self-signup needed) — including from a GoHighLevel sub-account.
- White-label the widget, portal (custom domain), emails, the Nox assistant name, and the desktop app.
- Track per-sub-account revenue and churn; manage agency staff with scoped permissions.
How it works
An AgencyModel row (collection agencies) is owned by a user and holds the agency's brand, sub-account counts, and references to its packages/add-ons. The agency subscribes to an agency package (AgencyPackageModel) which grants a number of sub-account slots; Stripe bills the agency. The agency then creates client companies, each linked back via companies.agency_id. Each sub-account can have its own company subscription, but the company admin can't change its own plan — they see a "Contact agency" gate. When the agency's subscription renews, sub-account allowances and AI credits are refreshed across all its companies.
Configuration & options
Agency model (AgencyModel, collection agencies)
| Field | Default | What |
|---|---|---|
user_id | — | Agency owner |
name | '' | Agency name |
logo_file | null | Branding logo (FileModel ref) |
initial_ai_credits | 1500 | Pooled AI credits across sub-accounts unless customized |
total_sub_accounts / available_sub_accounts | 0 | Slot counts (granted vs. remaining) |
agency_packages[] / agency_addons[] | — | Refs to the agency's SKUs |
theme_colors | teal palette | primary, secondary, accent, sidebar_bg, sidebar_text, header_bg, header_text |
nox_name | null | White-label display name for the Nox assistant |
reply_email | null | Sender for automated agency emails |
go_high_level | — | GHL OAuth tokens + config (app_version defaults v1) |
trial / trial_duration / trial_activated_at / expiry | — | Agency trial state |
payment_issue | false | Flag for billing problems |
deleted_at | null | Soft-delete |
Agency packages (AgencyPackageModel, collection agency_packages)
The SKUs the agency sells to clients (custom title, description, features, limits, user count, AI credits, expiry):
| Field | Default | What |
|---|---|---|
name, description | — | Package identity |
amount | — | Custom price |
currency | usd | Any Stripe-supported currency |
sub_accounts | — | Sub-account slots granted by this plan |
trial | false | Trial package |
stripe_product_id / stripe_price_id | — | Stripe linkage |
package_type | private | private / public |
status | active | active / inactive |
Packages can also restrict access by email allow-list, set an expiry (for time-limited promos), and define user counts and AI-credit allotments (per the customer-facing Packages screen).
Agency add-ons (AgencyAddonModel, collection agency_addons)
| Field | Default | What |
|---|---|---|
name, description, amount | — | Add-on identity + price |
currency | usd | |
type | sub_account | Add-on category |
billing_type | recurring | one_time / recurring |
visibility | public | private / public |
status | active | active / inactive |
The most common add-on is extra sub-accounts. Add-ons can be bought for the agency itself or applied to a specific client company.
Agency subscription (AgencySubscriptionModel, collection agency_subscription)
agency_id, user_id, package_id, new_package_id (scheduled downgrade at cycle end), expiry_date, is_trial, stripe_id, status (active / inactive / canceled), canceled_at, cancel_reason. Renews monthly.
Agency staff
Staff are UserModel rows with user_type: 'agency_staff', linked via agency_id and a parent (the agency owner), carrying a permissions[] array (and optional role). Per the customer-facing permission set, staff permissions include:
| Staff permission | What it allows |
|---|---|
| Manage Companies | Create/edit/manage client companies |
| Subscription & Payments | The agency's own billing, invoices, payment methods |
| Manage Packages | Create/edit/delete the packages sold to clients |
| Agency Settings | Branding, white-label, agency-wide settings |
| (revenue/impersonation gates) | View revenue; impersonate ("Login as Owner") |
Staff can be imported from a client company (promoting a member to agency staff without removing them from the company). Routes: /agency/staff*. Status shows Active (logged in) or Never Logged In.
Agency portal screens
| Screen | What |
|---|---|
| Dashboard | KPIs across sub-accounts (revenue, hot leads, MRR) |
| Companies | List + manage all sub-accounts (create, edit, impersonate, add-ons) |
| Staff | Agency users + permissions |
| Packages | The agency's product catalog |
| Add-ons | Agency-defined add-ons |
| Subscription Info / Management | The agency's own platform subscription |
| White-Label Domains | Custom domains for portal/widget |
| Buy Sub-Account | One-click create-new-client flow |
| Invoices | Agency billing history |
| Settings | Agency branding, working hours, notifications, Nox name |
Sub-accounts (companies under an agency)
companies.agency_idpoints to the agency.companies.agency_self_account: truemarks the agency's own business company vs. a client sub-account.- Subscription bills the agency, not the company directly; the company admin sees a "Contact agency" gate on plan changes.
- Create a sub-account via Add Company (
POST /agencies/companies) or Add from GHL (POST /agencies/companies/ghl). - Login as Owner (impersonation) lets agency staff configure or support a client; a "Back to Agency" control returns them.
Behaviors & edge cases
- Upgrade vs. downgrade: agency upgrades apply immediately (prorated); downgrades are deferred to cycle end via
new_package_id. Same model as direct billing — see Plans & Subscriptions. - Revoking agency access does not auto-convert sub-accounts to direct billing — that's a manual conversion.
- Inherited defaults: sub-accounts inherit some agency defaults (working hours, branding) — admins should understand precedence.
- GHL is agency-aware: company creation, tagging on cancel/reactivate, and install version (
app_version) flow through GHL — check the CRM integration docs. - Payment-issue flag:
agencies.payment_issue/ per-company flags (/agencies/payment-issue,/agencies/dismiss-payment-issue) surface billing problems. - Agency deletion: soft-delete (
deleted_at) plus a hard-delete endpoint exist; the customer-facing "Danger Zone" full self-serve deletion is marked coming soon.
Plan & limits
- The agency portal is the agency/Enterprise tier — itself a paid subscription.
initial_ai_creditsdefault is 1500 (pooled). Per-package sub-account slots, pricing, and AI credits are agency-defined — verify specific numbers against the live data; do not quote fixed prices.
Technical implementation
- Models:
AgencyModel,AgencyPackageModel,AgencyAddonModel,AgencySubscriptionModel,AgencyDomainModel; pluscompanies.agency_id/agency_self_account/widget_whitelabel—/home/bilal/Projects/knock-knock-app/backend/app/models/. - Controllers/routes:
AgencyController(/agencies*— subscribe, change-package, buy-addon, create company, payment-issue, conversion-requests),AgencyStaffController(/agency/staff*),AgencyDomainController(/agency-domains*). - Frontend:
frontend/src/views/agency/(Dashboard, SubscriptionInfo, SubscriptionManagement, WhiteLabelDomains, Settings, BuySubAccount, Invoices) +components/agency/AgencyPermissionGate.vue. - Billing flows through Stripe + Xero — see Payments & Invoices. White-label detail: White-Label. CRM/GHL: /subsystems/crm-integration. Engineering: backend service.