Skip to main content
Evaly provides powerful scheduling capabilities that let you automate test lifecycle management. You can schedule tests to start and end at specific times, pause and resume tests, extend durations, and choose between two different duration modes.

Scheduling Modes

Evaly supports two ways to control test timing:

Schedule-Based Duration

Set explicit start and end times for your test. The test automatically activates and finishes at the scheduled times.
// Example: Publishing a test with scheduling
publishTest({
  testId,
  startOption: "schedule",
  scheduledStartAt: 1735689600000, // Jan 1, 2025, 8:00 AM
  scheduledEndAt: 1735693200000    // Jan 1, 2025, 9:00 AM
});

Section-Based Duration

Set individual durations for each test section. The test window must be long enough to accommodate the total section duration.
When using section-based durations, all sections must have a duration set before publishing. The system validates that total section duration doesn’t exceed the availability window.

Publishing Options

Immediate Publishing

Publish a test to start immediately:
1

Check Active Tests Limit

The system verifies you haven’t exceeded your plan’s concurrent active tests limit.
// Free plan: 1 active test
// Pro plan: 5 active tests
// Enterprise: Unlimited
2

Publish Immediately

Set startOption to "now" to make the test available right away.
await publishTest({
  testId,
  startOption: "now"
});

Scheduled Publishing

Schedule a test to start in the future:
1

Set Start Time

Specify when the test should become available to participants.
2

Set End Time (Optional)

Optionally set when the test should automatically finish.
3

Overlap Validation

The system checks if your scheduled test overlaps with other active tests and validates against your plan’s concurrent tests limit.
If scheduling conflicts with too many tests, you’ll see:
This schedule conflicts with 2 other test(s), exceeding your 
limit of 1 simultaneous active tests.

Conflicting tests:
• Mathematics Final Exam
• Physics Midterm

Automated Job Scheduling

Evaly uses Convex’s scheduler to manage test lifecycle:

Activation Jobs

When you schedule a test, an activation job is created:
// Stored in test.activationJobId
const activationJobId = await ctx.scheduler.runAt(
  scheduledStartAt,
  internal.internal.test.activateTest,
  { testId }
);

Finish Jobs

If an end time is set, a finish job is scheduled:
// Stored in test.finishJobId
const finishJobId = await ctx.scheduler.runAt(
  scheduledEndAt,
  internal.internal.test.finishTest,
  { testId }
);
Jobs are automatically cancelled when:
  • You unpublish a test
  • You reschedule a test
  • You manually stop a test
This prevents duplicate or orphaned jobs from executing.

Managing Active Tests

Pausing Tests

Temporarily freeze a test while preserving participant progress:
await pauseTest({ testId });
What happens:
  • Test is marked with pausedAt timestamp
  • Finish job is cancelled (will be rescheduled on resume)
  • All participant timers freeze
  • Participants cannot continue until resumed

Resuming Tests

Restart a paused test and adjust the schedule:
await resumeTest({ testId });
What happens:
  • Pause duration is calculated and stored in totalPausedDuration
  • If there was a scheduled end time, it’s extended by the pause duration
  • Finish job is rescheduled with the new end time
  • Participants can continue from where they left off
Pause duration example:
  • Test scheduled to end at 10:00 AM
  • Paused at 9:30 AM
  • Resumed at 9:45 AM (15-minute pause)
  • New end time: 10:15 AM

Extending Test Time

Add more time to an active test:
await extendTestTime({
  testId,
  additionalMinutes: 30 // Add 30 minutes from current end time
});
await extendTestTime({
  testId,
  newEndTime: 1735693200000 // Set explicit new end timestamp
});
  • Extension is logged in the activity log
  • Finish job is cancelled and rescheduled
  • If test is paused, the finish job remains cancelled until resume

Stopping Tests

Manually end a test before its scheduled finish time:
await stopTest({ 
  testId,
  reason: "Technical difficulties" // Optional
});
What happens:
  • Test is unpublished (isPublished = false)
  • All scheduled jobs are cancelled
  • Schedule times are cleared
  • If test has started, it’s marked as finished with the reason
  • Activity is logged for audit trail

Updating Test Schedules

Change the end time of an active test:
await updateTestSchedule({
  testId,
  scheduledEndAt: 1735696800000 // New end time
});
Validation:When using section-based durations, the system validates that total section duration fits within the new test window:
Cannot update schedule: Total section duration (90 min) exceeds 
new test window (60 min). Please adjust section durations or 
extend the end time.

Duration Mode Toggle

Switch between schedule-based and section-based duration:
await toggleUseSectionDurations({
  testId,
  useSectionDurations: true
});
Important: When turning OFF section durations, all section duration values are automatically cleared.

Test Status Logic

Evaly determines test status based on scheduling fields:
const now = Date.now();
let status = "draft";

if (test.finishedAt || (test.scheduledEndAt && now > test.scheduledEndAt)) {
  status = "finished";
} else if (test.isPublished && test.scheduledStartAt && test.scheduledStartAt > now) {
  status = "scheduled";
} else if (test.isPublished && (!test.scheduledStartAt || test.scheduledStartAt <= now)) {
  status = "active";
}
Status Breakdown:
  • Draft: Not published yet
  • Scheduled: Published but start time is in the future
  • Active: Published and currently available
  • Finished: Manually finished or past end time

Validation Rules

1

No Past Scheduling

Cannot schedule test to start in the past
Cannot schedule test to end in the past
2

End After Start

End time must be after start time
3

Section Duration Validation

When useSectionDurations is true:
- All sections must have duration > 0
- Total section duration ≤ test window duration
4

Active Tests Limit

Free: 1 concurrent active test
Pro: 5 concurrent active tests
Enterprise: Unlimited

Best Practices

  1. Always set end times for scheduled tests to prevent tests from running indefinitely
  2. Use section-based durations when you want precise per-section time limits
  3. Schedule tests in advance to avoid last-minute rush and ensure participants can plan
  4. Monitor overlap warnings to avoid exceeding your plan’s active tests limit
  5. Test pause/resume before using in production to understand the behavior
  6. Add buffer time to your test window when using section-based durations

Build docs developers (and LLMs) love