Feature

Scheduled scans
on a real, audit-grade cadence

Set external, authenticated, and code scans to run daily, weekly, biweekly, or monthly. The platform records every schedule with a target, a frequency band, a deterministic next run time, and a plan-aware quota check, then writes the resulting scan to the workspace activity log so the cadence is auditable from the moment it starts.

No credit card required. Free plan available forever.

Scheduled scans as an operating discipline, not a checkbox

Internal security teams, AppSec teams, vulnerability management teams, cloud security teams, and security engineering teams all carry the same recurring problem: how often is each asset actually scanned, by which scanner stack, against which credential, on which branch, and where is the evidence the cadence held. The honest answer for most teams is a mix of ad-hoc runs, half-remembered calendar invites, a CI pipeline somebody set up two years ago, and a spreadsheet that nobody trusts. The cadence is real on paper and missing in evidence.

SecPortal answers that problem with scheduled scans as a first-class record. Every recurring scan lives on the scan_schedules table with a target, a schedule_type discriminator that picks the scanner stack, a frequency band that maps deterministically to a cron expression, a next_run_at timestamp the platform advances after every cycle, and a created_by actor that chains through to the resulting scan execution and the activity log. The cadence is auditable from the moment the schedule is created.

Three schedule types covering the workspace scanner stack

The schedule_type field is a strict discriminator. A schedule is exactly one of external, authenticated, or code, and the validation rules, plan checks, and execution path each type takes differ accordingly.

External scan schedule

Recurring perimeter scan against a verified domain in the workspace.

What it runs. Runs the full external scanner stack on the cadence the schedule defines. The target is a verified domain owned by the workspace, the scan_type field selects full or quick, and the cron schedule advances next_run_at after each execution. External schedules require a matching verified_domains record, so a schedule cannot be aimed at a target the workspace has not proved it owns.

Plan required. Continuous monitoring on the workspace plan; schedule count is capped by the same plan tier that authorises continuous monitoring.

Authenticated scan schedule

Recurring authenticated DAST against a verified domain using a stored credential.

What it runs. Runs the authenticated module set including auth path discovery, headers, tech fingerprint, SQLi, XSS, JWT, IDOR, path traversal, sensitive data, HTTP methods, error handling, session, SSRF, broken access, CSRF, and command injection on the cadence the schedule defines. The schedule is bound to a credential_id from the encrypted credential vault and the credential domain is checked against the schedule target at create time, so a schedule cannot run with a credential issued for a different domain.

Plan required. Continuous monitoring plus authenticated scanning on the workspace plan; both flags are re-checked on every cron execution and the schedule is auto-disabled if either is removed.

Code scan schedule

Recurring SAST and SCA against a connected repository on its default branch.

What it runs. Runs sast_analysis, sca_analysis, or both as selected by the scan_type field on the cadence the schedule defines. The schedule is bound to a repo_id from the connected_repos table, the branch is the default branch of the repo, and the trigger is recorded as schedule so the resulting code_scan_executions row can be filtered from manual runs and webhook runs in the dashboard.

Plan required. Continuous monitoring plus SAST or SCA on the workspace plan; both feature flags are re-checked on every cron execution and the schedule is auto-disabled if either is removed.

Four frequency bands, deterministically mapped to cron

The schedule frequency is one of four named bands, and each band maps to a fixed cron expression in the API. The mapping is deterministic so two schedules with the same frequency advance their next_run_at on the same boundary, which keeps the operating rhythm predictable across the team and across the asset inventory.

FrequencyCron expressionWhen it runsRight use
Daily0 0 * * *Runs at 00:00 UTC every day.Highest-value targets that change daily: production login flows, payment pages, externally exposed admin consoles, and assets the team is actively remediating.
Weekly0 0 * * 1Runs at 00:00 UTC every Monday.The default cadence for most assets. Catches deployments and posture drift within a business week without burning the monthly scan quota.
Biweekly0 0 1,15 * *Runs at 00:00 UTC on the 1st and 15th of every month.Balanced cadence for stable assets that still need regular regression coverage but do not warrant a weekly run.
Monthly0 0 1 * *Runs at 00:00 UTC on the 1st of every month.Baseline cadence for low-change assets, archived systems still in scope, and quarterly trend reads against the same scanner version.

Six fields that define the lifecycle of every schedule

The schedule row carries the fields the cron reads, the operator reads, and the audit reads. Each field is computed at a known point in the lifecycle so the row is never an opinion: it is the platform record of the cadence.

enabled

A boolean toggle on the schedule row. The execute-schedules cron only picks up schedules where enabled is true and next_run_at is at or before the current time, so pausing a schedule is one PATCH call away.

frequency

One of daily, weekly, biweekly, or monthly. Validated on create and on update; any other value is rejected with a 400 response.

cron_expression

A standard five-field cron string mapped deterministically from the frequency band. The mapping is fixed in the API so two schedules with the same frequency produce the same cron expression and the same next_run_at progression.

next_run_at

The deterministic next execution timestamp computed by getNextRunAt. Recalculated after every execution, after every frequency change, and after every error so the cron does not retry the same schedule every minute.

last_run_at

Set to the execution start timestamp after every successful run. The audit answer for "when did this asset last get scanned" is one column on one row.

created_by and created_at

The actor and timestamp the schedule was created with. The actor is reused as the initiated_by on every resulting scan execution so the audit chain from schedule to scan to finding is unbroken.

Validation, deduplication, and plan enforcement

Every privileged action on a schedule passes through the same gates as the equivalent ad-hoc scan, plus a set of dedup and plan checks that exist only on the schedule surface. The gates run at create time and the plan checks re-run on every cron cycle so a schedule cannot drift outside the rules silently.

  • The schedules API requires the initiate_scan permission, so only roles that can run an ad-hoc scan can create or modify a schedule
  • External and authenticated schedules require a matching verified_domains row in the workspace; a schedule cannot be aimed at a target the workspace has not proved it owns
  • Authenticated schedules require a credential from the encrypted credential vault and the credential domain is checked against the schedule target at create time
  • Code schedules require a connected_repos row in the workspace and a duplicate code schedule for the same repo is refused with a 409 response
  • External and authenticated schedules deduplicate against the same target plus schedule_type pair so the same domain cannot accumulate two external or two authenticated schedules
  • The continuous monitoring plan flag is re-checked on every cron execution; a schedule on a downgraded plan is auto-disabled rather than silently dropped
  • Monthly scan quotas are re-checked on every cron execution; a schedule that would exceed the quota advances its next_run_at without running so the next eligible cycle picks up the work
  • A per-cron-cycle ceiling of ten schedules per run prevents one busy cycle from overrunning the Vercel cron timeout

From schedule to finding: the audit chain

The cadence is only useful if the chain from the schedule that asked for the scan to the finding the scan produced is unbroken. The platform records the chain across four layers so every finding can be traced back to the schedule that triggered the scan that ran the module that detected it.

Schedule record

Every schedule lives on the scan_schedules table with workspace_id, target, schedule_type, frequency, cron_expression, next_run_at, last_run_at, enabled, created_by, and created_at. The row is the single source of truth for the cadence and the actor who set it.

Scan execution

Every run writes a scan_executions row (for external and authenticated) or a code_scan_executions row (for code) with initiated_by set to the schedule creator and trigger or scan_category recording how the scan started. The execution row carries the modules_completed list, the modules_total list, and the result summary so the audit can reconstruct what the scan actually did.

Scan job and module result

Every scan execution enqueues scan_jobs rows for each module the scanner stack ran. Module results land back on the execution and the findings table is the durable record of what was detected. The relationship from schedule to findings is one continuous chain of foreign keys.

Activity log

Privileged schedule actions and the resulting scan runs land on the workspace activity log with the actor, the timestamp, and the inputs. Internal audit, ISO 27001 surveillance, and SOC 2 access reviews read the same activity feed the operator reads.

Plan tiers and schedule availability

Scheduled scans live behind the continuous monitoring plan feature and the schedule-type-specific feature flags. The plan is checked at create time and re-checked on every cron execution so a downgrade auto-disables the schedule with an explicit reason rather than silently dropping the cadence. See the pricing page for the current monthly scan quotas and verified domain ceilings per tier.

Starter

Scheduled scans are not available. The Starter plan supports ad-hoc external scans only; continuous monitoring is unlocked on Pro and Team.

Pro

Continuous monitoring is on. The schedule count is capped to the same ceiling that gates verified domain count on the plan, so the schedule pool grows with the asset pool the workspace is allowed to monitor.

Team

Continuous monitoring is on, authenticated scanning is on, code scanning (SAST and SCA) is on. All three schedule types are available with the highest verified-domain ceiling and the highest monthly scan quota.

Why this matters for internal teams and enterprise buyers

Heads of vulnerability management, AppSec leads, CISOs, GRC owners, and security operations leaders evaluate scheduled scans against a different test than the one ad-hoc scanning passes. The test is whether the cadence survives a plan change, a team change, a quota cycle, and an audit window without manual reconciliation. The six properties below are the ones that matter for that test.

Cadence is the audit answer

ISO 27001 Annex A 8.8 and SOC 2 CC7.1 both ask the same question in different language: "How often is this asset scanned, and where is the evidence?" A schedule with last_run_at, next_run_at, and a scan history tied by initiated_by answers that question without a reconciliation. The cadence record and the evidence record are the same artefact.

No silent drift on plan downgrade

When the workspace plan changes, the execute-schedules cron re-checks continuousMonitoring, authenticatedScanning, sastScanning, and scaScanning on every execution. A schedule that no longer fits the plan is auto-disabled with an explicit error rather than silently dropped, so the next access review sees the disabled state and the reason it was disabled.

Quota is enforced, not surprised

Monthly scan quotas are re-checked on every cron execution. A schedule that would exceed the quota advances its next_run_at and skips the cycle with a logged error, so the team finds out at the next review rather than at the end-of-month invoice. There are no surprise overage charges because there is no overage path.

Owners change without losing the schedule

The created_by field records the actor who set the schedule, but the schedule keeps running when that actor changes team membership. The audit can trace back to who created it and who was the owner at the time, while the schedule itself continues to advance its next_run_at through team changes.

Deduplicated by design

External and authenticated schedules deduplicate against the same target plus schedule_type pair, and code schedules deduplicate against the same repo_id. Two engineers cannot accidentally double-schedule the same asset, which means two scans cannot fight for the same quota slot or produce two competing finding records on the same cycle.

Recoverable on every cycle

A failed cycle does not block the next cycle. The cron updates next_run_at even when the underlying scan errors out, so the schedule recovers on the next pass instead of stalling. Stale jobs in the worker queue are detected and recovered within the worker stale-job threshold so a stuck module does not freeze the whole schedule.

Operationalising scheduled scans on the workspace

Scheduled scans pay back the moment the cadence is documented and the first cycle runs without manual intervention. The checklist below is the operating shape the platform expects: it pairs the schedule with the verified domain or repo, the right frequency for the asset, and the runbook the owners on the other end read when the finding lands.

  • Pick the assets that need recurring coverage and confirm the verified domain or connected repo is already in the workspace
  • For authenticated schedules, confirm an encrypted credential is stored for the target domain and is rotated on the team policy
  • Pick the right frequency band per asset: daily for active high-value targets, weekly as the default, biweekly for stable assets, monthly for baseline
  • Create the schedule through the dashboard or the schedules API, naming the schedule_type, target, frequency, and credential or repo binding
  • Check the first next_run_at lands on the cadence the team expects and the activity log records the create action with the actor
  • Add the schedule to the team operating runbook so the cadence is known to the owners who read the findings on the other end
  • Review enabled schedules quarterly against the asset inventory and disable schedules for retired assets so the next quota cycle does not waste scans on dead targets
  • Use scan history and the scan comparison view to read the trend over time and catch regression between cycles

On-call notes for the schedule that did not fire

Schedules are durable but not magic. A handful of failure shapes recur, and each one has a deterministic signature on the schedule row, the execute-schedules cron response, or the scan execution record. Reading the right field first is faster than starting from scratch.

  • When a schedule does not run on the expected cadence, check enabled, next_run_at, and the most recent execute-schedules cron output before assuming the cron itself failed
  • When a schedule auto-disables, the reason is recorded in the cron response: plan-feature loss, missing credential, missing repo, or quota exhaustion
  • When a schedule fires and the scan fails part-way, the schedule still advances next_run_at so the next cycle gets a clean attempt; the failed execution row carries the diagnostic the team needs
  • When the workspace plan changes, audit the schedule list against the new plan flags before assuming everything carries over; the cron will disable any schedule the plan no longer authorises
  • When two engineers try to schedule the same target, the second create call returns a 409 with the existing schedule reference so the duplicate is visible rather than silent

Where scheduled scans compose with the rest of the platform

Scheduled scans are one layer in the scanning model. The schedule answers when the scan runs and against which target; other features handle what the scanner stack covers, how the result is compared to the previous cycle, and how the finding lands on the engagement record.

For the higher-level posture story the cadence supports, see continuous security monitoring. The continuous monitoring page frames the trend tracking and regression detection the cadence enables; this page is the operating record of the cadence itself.

For the scanner stack the schedule runs, see the external scanning, authenticated scanning, and code scanning feature pages. Each one details the modules that run, the evidence captured, and the guardrails on the scan input. The schedule is the cadence wrapper; the scanner stack is the work the cadence triggers.

Sensitive surfaces compose the schedule with their own primitives. Encrypted credential storage is where authenticated schedules bind to a credential and the credential domain is checked against the schedule target. Repository connections is where code schedules bind to a repo and inherit the default branch. Verified domain management is where external and authenticated schedules confirm the target is in workspace scope before the cron picks them up.

For reading the result of the cadence cycle over cycle, see scan comparison and diff for the new, fixed, and unchanged finding view between two scan executions, and the activity log for the named-actor timestamped record of every schedule action and resulting scan.

For the role gate that controls who can create and modify schedules, see role-based access control. The initiate_scan permission is what authorises a member to create or modify a schedule, and the gate runs at the API layer before the schedule row is written.

For the team running the cadence

For the workflow that pairs scheduled scans with triage and remediation, see the external security assessment use case, the DevSecOps scanning use case for the code-scanning side of the cadence, and the scanner onboarding and coverage rollout use case for the first-90-days shape of getting the schedule list right.

For the broader operating posture the cadence supports, see the continuous threat exposure management cycle use case, which frames the discover-prioritise-mobilise-validate loop the recurring scan feeds into.

The audience pages that map most closely to the operating discipline of scheduled scans are the internal security teams page, the vulnerability management teams page, the AppSec teams page, and the security engineering teams page. Each one frames the cadence conversation in the language the respective owner uses against the operating model and the audit posture.

Honest scope

Scheduled scans run the SecPortal scanner stack on the workspace target. The platform does not push schedules to or pull from third-party scheduling systems, does not run on a per-second or per-minute cadence, does not provide enterprise SSO, SCIM, or SAML, does not integrate natively with Jira, ServiceNow, Slack, Teams, SIEM, SOAR, or GRC platforms, and does not promise audit certification on behalf of the customer. The cadence record is the platform record of when the scan ran; the audit conclusion is still drawn by the auditor reading that record.

Stop forgetting last quarter's scan

Create a schedule once, pick a cadence, and the platform runs the scan, advances the next run timestamp, enforces the quota, and writes the result to the activity log. The audit answer for "when did this asset last get scanned" is one query, not a spreadsheet reconcile.

No credit card required. Free plan available forever.