Skip to main content

Package Restrictions & Feature Flags

[pakete] [admin] [billing]

Per license package, you can define which products are visible and which functions are enabled. Granular control over your offering.

Who it's for​

Admins who want to offer multiple packages with different scopes. The names (e.g. "Basic", "Pro", "Enterprise") are your own package names that you define for your customers.

What is configurable​

Product visibility (whitelist / blacklist)​

Categories:

  • allowed_categories : whitelist: only these categories are visible. Empty = all.
  • excluded_categories : blacklist: these categories are excluded. Always wins (even against the whitelist).

Compatibilities:

  • allowed_compatibilities : whitelist: only products with at least one of these tags. Products without compat tags are always visible.
  • excluded_compatibilities : blacklist.

Hard limits (count)​

  • max_downloads_per_month : monthly download quota (reset on the 1st of the month UTC). 0 = unlimited.
  • max_printers : how many printers per customer. 0 = unlimited.
  • max_active_licenses : how many customers active at the same time on this package. 0 = unlimited. (Enforcement TBD : see the wiki note below)

Feature flags​

  • has_crm_access : CRM tab visible in the portal (placeholder, Phase 2)
  • has_api_access : reserved for a future programmatic API (not built yet : no API keys, no endpoints)
  • has_white_label : reserved for white-label options (e.g. removing the footer logo) (Phase 2)
  • has_priority_support : tickets get a "Priority" tag in the admin inbox
  • watermark_downloads : downloads get a watermark (not implemented yet)

Where it applies​

Portal: product list​

/dashboard/products filters based on:

  1. excluded_categories (out)
  2. allowed_categories (only whitelist visible)
  3. excluded_compatibilities (out)
  4. allowed_compatibilities (only if the item has compats)

Implementation: apps/portal/app/dashboard/products/page.tsx.

Portal: feed export​

Feed export applies the identical logic. What is not visible in the portal is not in the feed either.

Admin: product selection in the package​

Settings → Packages → Edit package → "Restrictions" tab shows a list of all categories + compats of the provider with whitelist/blacklist toggles.

Step by step : adjusting package restrictions​

1. Open the package​

Settings → Packages → select a package.

2. "Restrictions" tab​

  • Allowed categories: multi-select of all categories in your catalog.
  • Excluded categories: multi-select.
  • Allowed compatibilities: multi-select of all compat tags.
  • Excluded compatibilities: multi-select.

Leave empty = no restriction (= all enabled).

3. "Limits" tab​

  • max_downloads_per_month
  • max_printers
  • max_active_licenses

4. "Features" tab​

  • has_crm_access, has_api_access, has_white_label, has_priority_support, watermark_downloads (toggle per flag)

5. Save​

Takes effect immediately for all customers on this package: on the next portal visit they see the new selection.

Frequently asked questions​

What if I put a product into an excluded category AFTER the package was created? It disappears immediately from the portal of all customers on this package. Applies retroactively.

How do I show customers which limits they have? In the Account tab "My Account" there is a "Package usage" section with the current consumption (downloads this month, printer count). Implemented in apps/portal/app/dashboard/account/PackageUsageSection.tsx.

What happens when max_downloads_per_month is reached? Currently: the quota is shown in the portal, but the download trigger is not yet hard-blocked (enforcement TBD). The printer limit, on the other hand, is already active: /api/portal/printers POST returns 429 with { limit, used }.

Can I migrate packages? In the customer detail you can change a customer's package. The new restrictions take effect immediately.

What does show_in_join_preview mean? A separate flag (not in this category). It controls whether the package appears on the landing page as a pricing card or is distributed by invitation only.