Scheduled Runs
Automate test execution on a recurring basis using cron expressions. Configure schedules for entire projects or individual suites, and manage data retention to keep your disk usage under control.
Overview
Scheduled runs let you execute your tests automatically on a recurring basis without manual intervention. Each schedule is tied to a project and optionally to a specific test suite. When a schedule fires, it runs all project tests (or the suite's tests) with a concurrency of 2, records the results, and updates the schedule's status tracking fields.
Schedules are managed through the REST API and persisted in the SQLite database. On server startup, all enabled schedules are reloaded via loadAllSchedules(), ensuring that your automation survives server restarts.
Schedules persist across server restarts. All enabled schedules are automatically reloaded when the server starts up via loadAllSchedules(). If you stop the server for an extended period, any schedules that were due to fire during that time will not retroactively execute — they will simply resume on the next scheduled time after the server starts.
Creating a Schedule
To create a schedule, send a POST request to the schedules endpoint with the following fields:
| Field | Type | Required | Description |
|---|---|---|---|
projectId |
string | Yes | The ID of the project whose tests to run. |
name |
string | Yes | A descriptive name for the schedule (1-100 characters). |
cronExpression |
string | Yes | A valid cron expression defining the schedule frequency. |
suiteId |
string | No | Optional suite ID. If provided, only that suite's tests will run. If omitted, all project tests run. |
enabled |
boolean | No | Whether the schedule is active. Defaults to true. |
{
"projectId": "proj_abc123",
"name": "Nightly Regression Suite",
"cronExpression": "0 0 * * *",
"suiteId": "suite_xyz789",
"enabled": true
}
The server validates the cron expression using the node-cron library. If the expression is invalid, a 400 error is returned. If the schedule is enabled, it is immediately activated via scheduleJob().
Cron Expression Format
Cron expressions follow the standard five-field format:
┌───────────── minute (0-59)
│ ┌───────────── hour (0-23)
│ │ ┌───────────── day of month (1-31)
│ │ │ ┌───────────── month (1-12)
│ │ │ │ ┌───────────── day of week (0-7, where 0 and 7 are Sunday)
│ │ │ │ │
* * * * *
Common Presets
Here are some commonly used cron expressions:
| Expression | Description |
|---|---|
0 * * * * |
Every hour, on the hour |
0 0 * * * |
Every day at midnight |
0 9 * * 1-5 |
Every weekday at 9:00 AM |
*/30 * * * * |
Every 30 minutes |
0 6,18 * * * |
Twice daily at 6:00 AM and 6:00 PM |
0 0 * * 0 |
Every Sunday at midnight |
0 0 1 * * |
First day of every month at midnight |
Schedule Behavior
When a schedule fires:
- If a suiteId is set, only the tests in that suite are executed.
- If no suiteId is set, all tests in the project are executed.
- Tests run with a concurrency of 2, meaning up to 2 tests execute in parallel.
- Each test execution creates a run record, exactly as if you had manually triggered the run.
- Results contribute to the analytics dashboard (pass rate, trends, flaky detection).
Schedule Status Tracking
Each schedule record tracks its execution history through three status fields:
| Field | Type | Description |
|---|---|---|
lastRunAt |
string (ISO timestamp) or null | The timestamp of the most recent execution. Null if never run. |
lastRunStatus |
string or null | The result of the most recent execution ("passed" or "failed"). Null if never run. |
nextRunAt |
string (ISO timestamp) or null | The calculated timestamp of the next scheduled execution. |
These fields allow you to monitor schedule health at a glance and detect any schedules that may be consistently failing.
Enable / Disable Toggle
Schedules can be enabled or disabled at any time by updating the enabled field. When a schedule is disabled, its cron job is unregistered via unscheduleJob() and it will not fire until re-enabled.
{
"enabled": false
}
When re-enabled, the schedule is immediately re-registered with the cron scheduler. You can also update the name, cronExpression, and suiteId in the same request.
Managing Schedules
The full set of schedule management endpoints:
| Endpoint | Method | Description |
|---|---|---|
/api/projects/:projectId/schedules |
GET | List all schedules for a project, ordered by creation date. |
/api/schedules |
POST | Create a new schedule. Activates immediately if enabled. |
/api/schedules/:id |
PUT | Update a schedule's name, cron expression, suiteId, or enabled status. |
/api/schedules/:id |
DELETE | Delete a schedule. The cron job is unregistered and the record is removed. |
Data Cleanup & Retention
As tests run automatically on a recurring basis, run records, screenshots, videos, and diff images accumulate over time. QA Studio includes an automatic cleanup system to manage disk usage.
Retention Configuration
The retention period is controlled by the RETENTION_DAYS environment variable:
| Variable | Default | Description |
|---|---|---|
RETENTION_DAYS |
30 | Number of days to keep run data. Set to 0 to disable automatic cleanup. |
Automatic Cleanup
The cleanup system runs automatically in two ways:
- On server startup — A deferred cleanup runs 5 seconds after the server starts, removing any data older than the retention period.
- Daily at 3:00 AM — A cron job (
0 3 * * *) runs the cleanup every day viastartCleanupSchedule().
The cleanup process removes:
- Old run records — Deleted from the
runstable. - Old suite run records — Deleted from the
suite_runstable. - Screenshot files — Deleted from disk for expired runs.
- Video files — Deleted from disk for expired runs.
- Diff image files — Actual and diff images for expired runs are removed. Baseline images referenced by diffs are preserved.
Manual Cleanup
You can trigger a cleanup on demand at any time using the admin endpoint:
POST /api/admin/cleanup
This runs the same cleanup logic as the automatic schedule, using the current RETENTION_DAYS value. The response confirms success:
{
"success": true
}
Use the POST /api/admin/cleanup endpoint to manually free disk space when needed. This is especially useful after bulk test runs or when you have lowered the RETENTION_DAYS value and want the change to take effect immediately.
Example Workflow
A typical scheduling workflow looks like this:
Create a Test Suite
Group your most important tests into a test suite. For example, create a "Smoke Tests" suite with your critical path tests.
Create a Schedule
Create a schedule targeting your suite. For nightly regression testing, use 0 0 * * * (midnight daily). For continuous monitoring, use 0 * * * * (every hour).
Monitor Results
Check the Analytics dashboard to monitor trends. The scheduled run results feed into the same analytics as manual runs — you will see trends, pass rates, and flaky test detection all reflect the automated runs.
Manage Retention
Configure RETENTION_DAYS based on your disk space and data needs. 30 days is the default and works well for most projects. Reduce it if disk space is limited; increase it if you need longer historical data for trend analysis.