Plan-based limits and quotas
enforced in code, not in policy
Three plan tiers (Starter, Pro, Team), seven numeric limit fields, seventeen feature flags, and a uniform can-this-action helper in front of every quota-sensitive write. Workspaces stay inside the tier they paid for because the gate runs at the API rather than relying on a salesperson to remember the contract.
No credit card required. Free plan available forever.
Plan-based limits and quotas, enforced in code rather than in policy
Enterprise security teams, AppSec leads, vulnerability management programme owners, GRC teams, and CISOs all share the same plan-evaluation question when they read a security platform RFP response: which plan fits the security programme, and what happens when the team grows past the next ceiling. SecPortal answers that question with a small, well-typed set of plan-feature and plan-limit fields, a handful of can-this-action helpers that read the plan on every privileged write, and a uniform 403 response that the dashboard and the API both understand. Limits are not a sales conversation. They are a set of numbers in code and a gate that rejects writes when the ceiling is hit.
The model carries three tiers (Starter, Pro, Team), seven numeric limit fields per tier covering clients, team seats, engagements, findings per engagement, total findings, storage, and activity log retention, and seventeen plan-feature fields covering verified domains, scan budgets, code scan budgets, repository connections, AI reports, MFA enforcement, continuous monitoring, attack surface discovery, subdomain scanning, authenticated scanning, custom subdomain branding, branding removal, scan cooldown, and more. Every field is defined in code and read by a deterministic helper, and the helper runs in front of the API every time the workspace tries to add a client, connect a repo, upload a document, kick off a scan, or invite a member.
Three plan tiers, ordered by capacity
Starter
Free tier for individuals and very small teams evaluating the platform.
Capacity. Holds the lowest numeric limits and the smallest feature surface in the product. 3 clients, 1 team seat, 2 engagements per client, 10 findings per engagement, 20 findings total across the workspace, 1 GB of storage, and 30 days of activity log retention. Active scanning is on with 2 scans per month and a 24-hour cooldown between scans. 1 verified domain via DNS TXT only. AI reports are available with a starting one-time credit allowance rather than a monthly quota. Authenticated scanning, code scanning, continuous monitoring, attack surface discovery, subdomain scanning, branding removal, and custom subdomain are all off. The Starter tier is the right shape for a single operator running a few light evaluations, not for a team trying to standardise their security testing programme.
Right reader. Solo security engineers, freelance pentesters running occasional client work, evaluators kicking the tyres before a production decision.
Pro
The general-purpose tier for growing security teams and service providers.
Capacity. 100 clients, 2 team seats with $29/seat/month add-ons available, 100 engagements per client, 500 findings per engagement, 10,000 findings total across the workspace, 25 GB of storage, 90 days of activity log retention. 50 scans per month with no cooldown, 5 verified domains across DNS TXT, file upload, and meta tag methods. 20 code scans per month, 5 connected repositories. 25 AI reports per month. Authenticated scanning on. Subdomain scanning on. Attack surface discovery on. Custom subdomain and branding removal on. Workspace MFA enforcement required. Continuous monitoring stays off at this tier. Pro is the shape that most growing internal AppSec teams, vulnerability management teams, and consulting practices land on after Starter.
Right reader. Internal AppSec leads, vulnerability management operators running ongoing scanning, security consultancies serving a small portfolio of clients, MSSPs running standardised assessments.
Team
The enterprise tier for security organisations running at scale.
Capacity. 500 clients, 5 team seats with $29/seat/month add-ons available, 200 engagements per client, 1,000 findings per engagement, 50,000 findings total across the workspace, 100 GB of storage, 365 days of activity log retention. 100 scans per month with no cooldown, 10 verified domains across DNS TXT, file upload, and meta tag methods. 100 code scans per month, 25 connected repositories. 75 AI reports per month. Authenticated scanning on. Subdomain scanning on. Attack surface discovery on. Continuous monitoring on. Custom subdomain and branding removal on. Workspace MFA enforcement required. Activity retention at 365 days matches the most common ISO 27001, SOC 2 Type II, and PCI DSS evidence windows. Team is the shape that internal security organisations, multi-team enterprise programmes, and larger consulting firms land on when one workspace serves several lines of business.
Right reader. CISOs and security directors carrying a multi-team programme, enterprise security operations leaders running continuous testing and compliance evidence work, security service providers serving large client portfolios.
Plan capacity matrix at a glance
The matrix below maps every plan-limit and plan-feature field to the three tiers. Every cell is sourced from the same plan definition file the API gate reads, so the table on this page and the gate in production never diverge.
| Dimension | Starter | Pro | Team |
|---|---|---|---|
| Clients (organisations or business units) | 3 | 100 | 500 |
| Team seats included in base price | 1 | 2 | 5 |
| Extra team seat add-on price | Not available | $29 / seat / mo | $29 / seat / mo |
| Engagements per client | 2 | 100 | 200 |
| Findings per engagement | 10 | 500 | 1,000 |
| Total findings across the workspace | 20 | 10,000 | 50,000 |
| Workspace storage | 1 GB | 25 GB | 100 GB |
| Extra storage add-on price | $1 / 10 GB / mo | $1 / 10 GB / mo | $1 / 10 GB / mo |
| Activity log retention | 30 days | 90 days | 365 days |
| Verified domains | 1 | 5 | 10 |
| Verification methods allowed | DNS TXT only | DNS TXT, file upload, meta tag | DNS TXT, file upload, meta tag |
| External scans per month | 2 | 50 | 100 |
| Scan cooldown between runs | 24 hours | None | None |
| Subdomain scanning | Off | On | On |
| Authenticated scanning | Off | On | On |
| Code scans per month (SAST and SCA) | 0 | 20 | 100 |
| Connected repositories for code scanning | 0 | 5 | 25 |
| AI reports per month | 0 monthly + signup credit | 25 | 75 |
| Continuous monitoring | Off | Off | On |
| Attack surface discovery | Off | On | On |
| Custom subdomain branding | Off | On | On |
| Branding removal on client portal | Off | On | On |
| Workspace MFA enforcement required | Optional | Required | Required |
| Stripe Connect transaction fee on invoicing | 4% | 3.5% | 2% |
The can-this-action helpers that gate every quota
Every numeric ceiling and every plan-feature flag has a matching helper that reads the plan, compares the current usage to the limit, and returns a LimitCheck. Every helper is called from the API route in front of the database write, so quota enforcement is a code gate rather than a policy promise.
canAddClient(planId, currentClientCount)
New client record creation against the maxClients ceiling.
Reads the plan from the workspace, compares the current client count to plan.limits.maxClients, returns a LimitCheck object. Returns 3 for Starter, 100 for Pro, 500 for Team. The Add Client API route returns 403 with the limit, current usage, and plan name when the ceiling is hit, so the failure response is the same shape the upgrade prompt uses on the dashboard.
canAddTeamMember(planId, currentMemberCount, extraSeats)
Team invite limit, including paid extra seat add-ons.
Compares the current consultant count to plan.limits.maxTeamMembers plus any extraSeats the workspace has bought through Stripe. Extra seats are a $29/seat/month add-on on Pro and Team. Starter has 1 seat with no extra-seat add-on path.
canAddEngagement(planId, currentEngagementCount)
Engagement scaffolding inside a client against the per-client ceiling.
Compares the current engagement count for the client to plan.limits.maxEngagementsPerClient. Returns 2 for Starter, 100 for Pro, 200 for Team. The Create Engagement API route returns 403 with the limit before the engagement record is written.
canAddFinding(planId, currentFindingCount)
Per-engagement findings ceiling, used by manual create and bulk import.
Compares the count of findings on the current engagement to plan.limits.maxFindingsPerEngagement. Returns 10 for Starter, 500 for Pro, 1,000 for Team. The bulk import pipeline checks this ceiling per engagement before any rows are written so a partial import never leaves the engagement in a half-imported state.
canAddFindingTotal(planId, totalItemsCreated)
Workspace-wide finding ceiling across every engagement combined.
Compares the total finding rows ever created in the workspace to plan.limits.maxTotalFindings. Returns 20 for Starter, 10,000 for Pro, 50,000 for Team. This is the workspace-wide ceiling that decides whether the team can keep importing and scanning at the current tier.
canUploadDocument(planId, currentStorageBytes, newFileBytes, extraStorageGb)
Per-upload storage ceiling against the included storage plus any paid extra storage.
Sums the current workspace storage and the proposed upload, compares to plan.limits.maxStorageBytes plus extraStorageGb * 1 GB. Returns a LimitCheck with the byte budget remaining. Starter is 1 GB. Pro is 25 GB. Team is 100 GB. Extra storage is sold as $1 per 10 GB per month and stacks on the included ceiling.
canRunScan(planId, scansThisMonth)
External scan ceiling per month plus the active scanning feature flag.
Combines plan.features.activeScanning with the count of scans this month versus plan.features.maxScansPerMonth. Returns false if active scanning is off on the plan, or if the monthly scan budget has been spent. Starter 2, Pro 50, Team 100. The scheduling worker rechecks this gate every cycle so a plan downgrade auto-disables the schedule that no longer fits.
canRunAuthenticatedScan(planId)
Whether the plan tier carries the authenticated scanning feature at all.
Plan-feature flag rather than a numeric quota. Returns false on Starter, true on Pro and Team. The authenticated scan API route refuses the run with a 403 before any credential is read out of the encrypted store, so a Starter workspace cannot accidentally trigger an authenticated scan even if the credential record exists.
canRunCodeScan(planId, codeScansThisMonth)
Code scan (SAST and SCA) ceiling per month plus the SAST or SCA feature flag.
Returns true only when (sastScanning OR scaScanning) is on for the plan and codeScansThisMonth is below plan.features.maxCodeScansPerMonth. Starter 0, Pro 20, Team 100. The cron worker that executes scheduled code scans re-evaluates this every cycle so a plan downgrade auto-stops the schedule.
canConnectRepo(planId, currentRepoCount)
Connected repository ceiling against the maxConnectedRepos plan feature.
Compares the current connected repo count to plan.features.maxConnectedRepos. Starter 0, Pro 5, Team 25. The repository connect flow refuses to bind a new repo when the ceiling is hit, so the plan cap is enforced before any OAuth callback writes a token.
canAddVerifiedDomain(planId, currentDomainCount)
Verified domain ceiling against the maxVerifiedDomains plan feature.
Compares the current verified domain count to plan.features.maxVerifiedDomains. Starter 1, Pro 5, Team 10. The verified domain create API route refuses a new domain when the ceiling is hit so the scanner cannot be aimed at a target the workspace has not proved it owns.
canUseContinuousMonitoring(planId)
Whether the plan tier carries continuous monitoring as a feature.
Plan-feature flag. Returns true only on Team. Continuous monitoring is the trend-tracking and regression-detection loop on top of scheduled scans, and the gate runs in front of the API surface so a workspace cannot opt in to a feature its plan does not include.
canUseAttackSurfaceDiscovery(planId)
Whether the plan tier carries attack surface discovery as a feature.
Plan-feature flag. Returns false on Starter, true on Pro and Team. The discovery API route refuses the run before any external probe is sent.
getTransactionFeePercent(planId)
Stripe Connect transaction fee applied to invoices the workspace sends through the platform.
Returns 4 for Starter, 3.5 for Pro, 2 for Team. The fee is collected by Stripe Connect at settlement time on each charge that runs through the workspace; the fee number does not change between billing cycles unless the plan tier changes.
How usage is counted before the gate decides
The gate is only as honest as the count it reads. SecPortal computes the workspace footprint with tightly scoped Supabase queries and Postgres RPCs rather than holding the usage in application memory, so the count cannot drift between the helper and the underlying tables.
getWorkspaceUsage
Counts of clients, consultant-role team members, and the workspace storage byte total in one parallel call. The storage total is computed by a Postgres RPC get_workspace_storage_bytes that sums the file_sizes column in the workspace storage table.
getEngagementFindingCount
Exact count of findings on a single engagement. Used by the per-engagement ceiling check before any new finding row is written.
getVerifiedDomainCount
Exact count of verified domains in the workspace. Used by canAddVerifiedDomain when a new domain is being added.
getScansThisMonth
Count of external and authenticated scans the workspace has run since the start of the current month. Returned by a Postgres RPC get_workspace_scans_this_month so the count is computed in the database rather than pulled into memory.
getCodeScansThisMonth
Count of code scan executions the workspace has run since the start of the current month, excluding cancelled runs. The count is computed by a tightly scoped Supabase query against code_scan_executions.
getConnectedRepoCount
Exact count of connected repositories in the workspace. Used by canConnectRepo before the OAuth callback writes a token.
Where the quota gate runs in the stack
The quota gate is enforced in the same way the role gate is enforced: in code, at the API, with a uniform 403 shape on the failure path. The dashboard only hides the upgrade-blocked controls as a courtesy on top of the real enforcement layer.
- Every quota-sensitive API route imports the matching can-this-action helper and refuses the write with a uniform 403 before the database is touched
- Helpers return a LimitCheck shape with allowed, currentUsage, limit, and planName so the dashboard and the API share one error contract for upgrade prompts
- The cron worker that runs scheduled scans rechecks plan eligibility on every cycle, so a plan downgrade auto-disables the schedule that no longer fits
- Feature flags compose with workspace-level RBAC so a member with manage_repos still cannot connect a sixth repo on a Pro workspace once the ceiling is hit
- Activity retention is enforced by a daily cleanup cron that prunes activity rows older than plan.limits.activityRetentionDays so the retention window in the UI matches the retention window in the database
- Storage and per-engagement finding counts are computed by Postgres queries and RPCs rather than maintained in application memory, so the count cannot drift between the gate and the underlying tables
Procurement questions answered without a sales call
Enterprise procurement and security leadership both arrive at the same set of plan questions, and the answers are easier to read off a page than to chase through a sales cycle. The eight items below cover the questions that show up on almost every plan decision and almost every RFP response.
How many scans does the plan include per month?
Starter 2, Pro 50, Team 100 external scans per month. Pro and Team include authenticated scanning, Starter does not. Pro and Team additionally allow code scans, 20 per month on Pro and 100 per month on Team.
How many team members are included in the base price?
Starter 1, Pro 2, Team 5. Pro and Team allow extra seats at $29 per seat per month, sold as a Stripe add-on. Starter cannot add extra seats.
How much storage is included?
Starter 1 GB, Pro 25 GB, Team 100 GB. Extra storage is sold at $1 per 10 GB per month and stacks on the included ceiling, on every tier.
How long is the activity log retained?
Starter 30 days, Pro 90 days, Team 365 days. The 365-day retention on Team matches the most common ISO 27001, SOC 2 Type II, and PCI DSS audit-evidence windows.
Which plans include continuous monitoring?
Team only. Continuous monitoring is the trend-tracking and regression-detection loop on top of scheduled scans. The gate is enforced in code, not by feature toggles in the dashboard.
Which plans include attack surface discovery?
Pro and Team. Starter has it off. The discovery API route refuses the run with a 403 before any external probe is sent.
Is workspace MFA enforcement included?
Yes on Pro and Team, optional on Starter. The Pro and Team workspaces enforce a session promotion to AAL2 before any privileged action can run.
How are quotas enforced when the plan is downgraded?
Numeric ceilings are evaluated on every write, so a workspace at 60 connected repos on Team that downgrades to Pro stops being able to connect new repos until the existing count drops below 5. Feature flags like continuous monitoring auto-disable on downgrade: the cron worker that executes scheduled scans rechecks the plan every cycle and disables any schedule that no longer fits.
How the plan model composes with the rest of the platform
The plan model is one layer in the platform security and operations model. It decides capacity. Other layers decide who can act, whether the action is recorded, and which tenant the action belongs to. The composition is deliberate, and the plan ceiling never replaces a security control.
The role-based access control layer answers whether the actor is allowed to take the action at all. The plan gate runs after the role gate is satisfied: a member with manage_repos still cannot connect a sixth repository on a Pro workspace because the connect helper refuses to write past the ceiling. The two gates run together rather than one replacing the other.
Sensitive surfaces compose the plan gate with their own security primitives. Authenticated scanning carries both a plan-feature flag (off on Starter, on on Pro and Team) and a per-month budget through the canRunScan helper. Code scanning carries a per-month budget and a connected-repository ceiling, both enforced before any Semgrep run starts. Continuous monitoring is a Team-tier feature flag rather than a numeric quota, and the scheduling worker auto-disables continuous schedules on a downgrade.
The workspace activity log retention window is itself a plan limit (30 days on Starter, 90 on Pro, 365 on Team), and a daily cleanup cron prunes rows older than the plan window so the retention promise on the marketing page and the retention behaviour in the database match. Scheduled scans recheck the plan gate every execution cycle, so a downgrade automatically stops any schedule that no longer fits the new ceiling without an operator intervention.
The tenant boundary the plan model is read under is the workspace, resolved by the tenant subdomain isolation middleware. Plan limits are workspace-scoped, so a workspace with a Team plan cannot leak capacity into a sibling workspace on a different tier, and a Starter workspace cannot borrow a sibling workspace's monthly scan budget.
Plan quotas sit alongside a separate short-window request-shaping gate. The API rate limiting primitive caps the rate of calls into each privileged surface (per IP on authentication, per user on workspace writes, per IP on the public scanner tools), and composes with the plan-based monthly ceiling: a Pro-tier workspace can hit the per-user scan rate cap inside a month it still has 30 scans of plan budget, or hit the monthly plan ceiling inside a 15-minute window it still has rate budget for, whichever comes first.
Sizing the security programme against the plan ceilings
The right plan for a security programme is the plan whose ceilings sit comfortably above the team's current footprint and below the next tier's price line. The five questions below are the practical sizing pass a security leader can run with their head of security operations before a procurement decision, and each one maps to a numeric ceiling defined in the plan model.
- How many distinct clients, business units, or product groups does the workspace need to track. This maps directly to maxClients (3 Starter, 100 Pro, 500 Team).
- How many people from the security team need write access to the workspace. The base included seat counts are 1, 2, and 5; extra seats are $29 per seat per month on Pro and Team and stack on the base count.
- How many findings does the team expect across all engagements this year. The Pro tier supports 10,000 total findings, the Team tier supports 50,000. Per-engagement ceilings (500 on Pro, 1,000 on Team) cover the typical scope of a single assessment.
- How many scans does the team run per month, split between external, authenticated, and code scans. The monthly ceilings are 2 external on Starter, 50 external and 20 code on Pro, 100 external and 100 code on Team.
- How much storage does the team need for evidence, scanner output, AI-generated reports, and uploaded documents. 25 GB on Pro and 100 GB on Team cover most programmes, and extra storage is sold at $1 per 10 GB per month on every tier.
- Does the audit-evidence window need 365 days of activity log retention. If yes, Team is the right shape; Pro retains 90 days and Starter retains 30.
The plan tier itself is set on the workspace from the pricing page, where the Stripe-managed checkout writes the plan back to the workspace and the helpers on this page pick it up on the next request.
For the workflow that operationalises plan capacity alongside the security testing programme, see the security testing programme management use case. For the audience pages that frame plan selection in the right buyer language, see the internal security teams page, GRC and compliance teams page, and CISO page; each one frames the plan-selection question in the language the respective buyer uses.
Honest scope: SecPortal does not currently ship enterprise SSO, SAML, SCIM provisioning, custom contract pricing through the dashboard, automated procurement workflow integration, on-premise deployment, dedicated tenant infrastructure, or volume discounts beyond the three published plans. Plans are read off the workspace record, gated in code, and managed through Stripe billing.
Know exactly which plan fits the security programme
Every limit and feature flag is defined in code and gated in the API. Read the plan tier, sum the workspace footprint, and the answer to which tier the team needs is a deterministic comparison rather than a sales conversation.
No credit card required. Free plan available forever.