Quartz.NETQuartz.NET
Home
Features
Blog
Discussions
NuGet
GitHub
Home
Features
Blog
Discussions
NuGet
GitHub
  • Getting Started

    • Overview
    • Quartz 4 Quick Start
    • Tutorial
      • Using Quartz
      • Jobs And Triggers
      • More About Jobs & JobDetails
      • Job Data
      • More About Triggers
      • Querying Jobs and Triggers
      • Simple Triggers
      • Cron Triggers
      • RecurrenceTrigger
      • Time and TimeProvider
      • Trigger and Job Listeners
      • Scheduler Listeners
      • Job Execution Middleware
      • Job Stores
      • Configuration, Resource Usage and Building a Scheduler
      • Building a Scheduler Without a Host
      • Clustering
      • Execution Groups
      • Node Affinity (Preferred Node)
      • Testing
      • Compile-Time Checks
      • Declaring Jobs with Attributes
      • Delegate Jobs
    • Configuration Reference
    • JSON Configuration
    • Cron Expression Reference
    • Multi-Tenancy
    • Comparison
    • Frequently Asked Questions
    • Best Practices
    • Before You Go Live
    • Operating a Cluster
    • Log Events
    • Tenancy Patterns
    • Database Schema
    • Database Schema Changes
    • Migration Guide
    • Troubleshooting
    • API Documentation
  • How To's
    • One-Off Job
    • Rescheduling Jobs
    • Backfill
    • Retrying Failed Jobs
    • Pausing with a Reason
    • Job Continuations
    • Overlap Policy
    • Progress and Execution Logs
    • Job Outcomes
    • Multiple Triggers
    • Job Template
    • Running Quartz under Aspire
    • Quartz.NET with Wolverine
    • Coming from Hangfire
    • Coming from TickerQ
    • Embedding Quartz in a Library
    • Running under an External Leader Election
    • Publishing Trimmed and Native AOT
    • Extending Quartz: what is open, what is closed, and how to ask
    • A Job Store of Your Own
    • A Driver Delegate for a New Database
    • Persisting a Custom Trigger Type
    • A Lock Handler of Your Own
  • Packages

    • Quartz Core Additions

      • Jobs
      • Serialization (System.Text.Json)
      • JSON Serialization
      • Plugins
    • Integrations

      • Aspire Integration
      • ASP.NET Core Integration
      • HTTP API
      • HTTP Client
      • Dashboard
      • Hosted Services Integration
      • Microsoft DI Integration
      • Multiple Schedulers with Microsoft DI
      • Observability
      • Redis Lock Handler
      • TimeZoneConverter Integration
      • Weasel Schema Management
    • 3rd Party Plugins for Quartz
  • Quartz 3.x

    • Getting Started

      • Quartz 3 Quick Start
      • Tutorial
        • Using Quartz
        • Library Overview
        • Jobs And Triggers
        • More About Jobs
        • More About Triggers
        • Execution Groups
        • Node Affinity (Preferred Node)
        • Simple Triggers
        • Cron Triggers
        • RecurrenceTrigger
        • Trigger and Job Listeners
        • Scheduler Listeners
        • Job Stores
        • Tuning the Scheduler
        • Configuration, Resource Usage and SchedulerFactory
        • Advanced (Enterprise) Features
      • Configuration Reference
      • JSON Configuration
      • Multi-Tenancy
      • Frequently Asked Questions
      • Best Practices
      • Tenancy Patterns
      • Troubleshooting
      • API Documentation
      • Database Schema
      • Database Schema Changes
      • Migration Guide
      • Miscellaneous Features
    • How To's

      • One-Off Job
      • Multiple Triggers
      • Job Template
      • Using the CronTrigger
      • Rescheduling Jobs
    • Packages

      • Quartz Core Additions

        • Dashboard
        • Jobs
        • Serialization (System.Text.Json)
        • Serialization (Newtonsoft Json.NET)
        • Plugins
      • Integrations

        • ASP.NET Core Integration
        • Hosted Services Integration
        • Microsoft DI Integration
        • Multiple Schedulers with Microsoft DI
        • OpenTelemetry Integration
        • OpenTracing Integration
        • Redis Lock Handler
        • TimeZoneConverter Integration
      • 3rd Party Plugins for Quartz
  • Old Releases

    • Quartz 2.x
      • Quartz 2 Quick Start
      • Tutorial
        • Lesson 1: Using Quartz
        • Lesson 2: Jobs And Triggers
        • Lesson 3: More About Jobs & JobDetails
        • Lesson 4: More About Triggers
        • Lesson 5: SimpleTrigger
        • Lesson 6: CronTrigger
        • Lesson 7: TriggerListeners and JobListeners
        • Lesson 8: SchedulerListeners
        • Lesson 9: JobStores
        • Lesson 10: Configuration, Resource Usage and SchedulerFactory
        • Lesson 11: Advanced (Enterprise) Features
        • Lesson 12: Miscellaneous Features of Quartz
        • CronTrigger Tutorial
      • Configuration Reference
      • Migration Guide
      • API Documentation
    • Quartz 1.x
      • Tutorial
        • Lesson 1: Using Quartz
        • Lesson 2: Jobs And Triggers
        • Lesson 3: More About Jobs & JobDetails
        • Lesson 4: More About Triggers
        • Lesson 5: SimpleTrigger
        • Lesson 6: CronTrigger
        • Lesson 7: TriggerListeners and JobListeners
        • Lesson 8: SchedulerListeners
        • Lesson 9: JobStores
        • Lesson 10: Configuration, Resource Usage and SchedulerFactory
        • Lesson 11: Advanced (Enterprise) Features
        • Lesson 12: Miscellaneous Features of Quartz
      • API Documentation
  • License

Comparison

Five .NET libraries schedule background work. Every cell below was read from the named project's own documentation or source at a pinned version, and every cell that is not Quartz's links its source. Rows where Quartz.NET is the more expensive answer say so; Where Quartz.NET costs more than it is worth collects them.

Read as of 29 September 2026. If a cell no longer matches its link, the link is right — please open an issue.

  • "None": the project's documentation has no such concept, and its source at the pinned version has none. You can usually still write the code yourself.
  • "—": the row does not apply to that library.
LibraryVersion readPinned at
Quartz.NET4.4this repository
Hangfire1.8.25tag v1.8.25
TickerQ10.4.0commit c6ed1e7d, named by the 10.4.0 packages' SourceLink; there is no v10.4.0 tag
Wolverine6.41.0tag V6.41.0
Coravel6.0.2commit 88ea3e89, named by the 6.0.2 nuspec; the last tag is 4.0.3

What each one is

  • Quartz.NET is a scheduler. A trigger is a stored object with a schedule, time zone, misfire instruction, calendar and priority, and the scheduler fires it once, on time, on one node of however many are running.
  • Hangfire is a background job processor first: a queue with a durable state machine (enqueued, processing, succeeded, failed) and a dashboard built around it. A recurring job is a cron string that enqueues into it.
  • TickerQ is a source-generated dispatcher: an attribute on a method, a Roslyn generator for the registration, no reflection on the dispatch path. It has a one-shot ticker and a cron ticker.
  • Wolverine is a message bus with cron since 6.34. opts.Schedules.ScheduleRecurring publishes a message on a schedule, using the bus's delivery, durability and replay. It is not a scheduler; Quartz.NET with Wolverine covers which of the two should own a schedule.
  • Coravel is an in-process convenience layer for ASP.NET Core: a scheduler, a queue, a cache and an event dispatcher, all in memory, in one small package.

Declaring a job

Quartz.NET 4.4Hangfire 1.8.25TickerQ 10.4.0Wolverine 6.41Coravel 6.0.2
The unit of worka class implementing IJob or IJob<TInput>, or a lambdaan expression tree naming a methoda method with [TickerFunction("name")]a message type and its handlera class implementing IInvocable
Scheduling itAddJob<T> + AddTrigger<T>, ScheduleJob<TJob, TInput>(input, delay), or ScheduleJob(name, lambda, trigger)BackgroundJob.Enqueue, .Schedule, RecurringJob.AddOrUpdate(id, …, cron)by function name, new TimeTickerEntity { Function = "send-welcome" }, or by type after MapTicker<T>()opts.Schedules.ScheduleRecurring<T>("0 2 * * *")scheduler.Schedule<T>().EveryTenMinutes()
Registrationbuilder.AddQuartz() + builder.AddQuartzHostedService()services.AddHangfire(…) + services.AddHangfireServer()builder.Services.AddTickerQ(); + app.UseTickerQ();inside UseWolverineservices.AddScheduler() + app.Services.UseScheduler(…)
Compiler checksjob type; payload type for IJob<TInput>; a cron literal or const (QZ0001); a [JobTimeout] argumentthe method call; the recurring id is a stringfunction name, signature and cron literal (TQ003)the message type; a bad cron throws at the registration linethe invocable type; cron parsed at run time
Where the schedule livesthe job store; changed at run time by any nodethe storage; changed at run timethe persistence provider; changed at run time or from the dashboardcode, at UseWolverinecode, at UseScheduler; not persisted

Before 4.2 Quartz parsed cron only at run time, and TickerQ's generator won this row. Now an analyzer inside Quartz.nupkg parses a cron literal or const with the run-time parser and fails the build on an error (Compile-Time Checks), and [QuartzJob] and [CronTrigger] declare a job and its schedule on the class for a source generator to register. An expression built at run time is still checked at run time, with CronExpressionBuilder and the "when does this fire" helper.

Coravel has a method per cadence: EveryTenMinutes(), DailyAt(3, 0), Weekday(). Quartz has one member per idea, and writes the schedule inline: t.WithCronSchedule(cron => cron.Every(TimeSpan.FromMinutes(10))), cron.AtTime(new TimeOnly(3, 0)), cron.OnWeekdays(). See Building CronTriggers.

Trigger kinds and cron grammar

Quartz.NET 4.4Hangfire 1.8.25TickerQ 10.4.0Wolverine 6.41Coravel 6.0.2
Schedule kindsfive: cron, simple, calendar-interval, daily-time-interval, RFC 5545 recurrencefire-and-forget, delayed, recurring, continuation; batches on the paid tierTimeTicker (one-shot) and CronTicker (recurring)scheduled, and recurring on cronfluent intervals, or cron
Cron parserQuartz's ownCronos 0.11.1, internalized into Hangfire.CoreNCrontab 3.3.0Cronoshand-written
Fieldssix, or seven with a year; five with CronFormat.Unixfive, or six with leading secondssix; five is expanded with 0 secondsfive, or six with leading secondsfive
Beyond * , - /L, W, #, H, wrapping rangesL, W, # (Cronos)nonethe Cronos grammarnone
Finest cadenceno floor; seconds mean secondsSchedulePollingInterval, 15 s by default; recurring jobs are documented as minute-basedMinPollingInterval, 1 s by default5 s; quicker is refused1 s for a fluent interval; cron only on the minute
Tie between two due at oncePriority on the triggernone (requested since 2022); SQL Server drains queues in alphanumeric orderTickerTaskPriority per function: dispatch orderthe receiving endpoint decidesnone

A firing the process was down for

Quartz.NET 4.4Hangfire 1.8.25TickerQ 10.4.0Wolverine 6.41Coravel 6.0.2
The policya misfire instruction per trigger: skip, fire one catch-up, or fire every missed one; and a backfill of a past range you chooseMisfireHandlingMode: Relaxed (default, one job), Strict (one per missed occurrence), Ignorable (none)none: the fallback sweep runs an overdue row late; SkipStaleCronOccurrencesOnStartup() drops stale occurrences, off by defaultno back-fill; the one pre-scheduled occurrence fires, the rest is lostnothing; the tick catch-up is seeded at process start, so it covers a stalled timer, not a restart
Chosen pertriggerrecurring job———

Calendars and time zones

Quartz.NET 4.4Hangfire 1.8.25TickerQ 10.4.0Wolverine 6.41Coravel 6.0.2
Dates it must not fire onsix calendars: HolidayCalendar, CronCalendar, DailyCalendar, WeeklyCalendar, AnnualCalendar, MonthlyCalendarnonenonenonenone
Time zoneper trigger, InTimeZoneRecurringJobOptions.TimeZone, UTC by defaultone SchedulerTimeZone, machine-local by defaultper schedule, UTC by default.Zoned(TimeZoneInfo) per schedule, UTC by default

Elsewhere, "not on public holidays" is a check in the job body.

Running once when several nodes are up

Quartz.NET 4.4Hangfire 1.8.25TickerQ 10.4.0Wolverine 6.41Coravel 6.0.2
How one node winsa row lock at trigger acquisition, before the job runsa distributed lock around the recurring enqueue; the first server to dequeue runs ita conditional UPDATE's affected-row count, with no row lock, SKIP LOCKED or lock table; Lua scripts on Redisone SingularAgent per cluster, plus a deterministic deduplication idnothing: a process-local Timer, so every instance runs every schedule
A node dies mid-executioncheck-in detects it; a job that requests recovery re-runsremoved after ServerTimeout (5 minutes); its jobs are requeuedthe fallback sweep takes rows with a stale lease; Redis has a dead-node scriptthe agent moves to another node—
Pinning work to one nodePreferredNode on the triggerqueue names per serverNodeIdentifier names the lease holder; it does not routethe agent's assignment—

Quartz fires each trigger once, because acquisition takes the lock before the job runs. A firing is at most once unless recovery is requested, which makes it at least once. No library here promises exactly once; Hangfire's best practices ask for re-entrant methods because it retries, and its throttling page says a mutex does not prevent simultaneous execution of one job. Write jobs so a second run is harmless — Best Practices.

Concurrency control

Quartz.NET 4.4Hangfire 1.8.25TickerQ 10.4.0Wolverine 6.41Coravel 6.0.2
Overall parallelismMaxConcurrency, ten by defaultWorkerCount, min(ProcessorCount × 5, 20)MaxConcurrency, Environment.ProcessorCountthe receiving endpoint'snone documented
One job not overlapping itself[DisallowConcurrentExecution]: cluster-wide with a persistent store, enforced by the store, no waiting; or a per-trigger overlap policy: skip, buffer one, or cancel the previous firingDisableConcurrentExecution(timeoutSeconds): a distributed lock that waits, then throws DistributedLockTimeoutExceptionmaxConcurrency on [TickerFunction], a per-process SemaphoreSlimas for any messagePreventOverlapping, an in-memory mutex
Capping a category of workexecution groups, per node or across every node sharing the store; one limit per tenant by prefixHangfire.Throttling, Business tier; best-effort, not for hundreds of jobs on one semaphoreper function and per process only——

Only Quartz counts a limit across nodes for free: "this tenant gets eight threads, however many nodes are up" is counted by the store, not per process.

Retries and failure

Quartz.NET 4.4Hangfire 1.8.25TickerQ 10.4.0Wolverine 6.41Coravel 6.0.2
Retry on failureRetryPolicy on the trigger: Fixed, Exponential, Explicit; opt-in; also on the job type or as a scheduler defaultAutomaticRetryAttribute on every job by default: ten attempts, (attempt − 1)⁴ + 15 + rand(30) × attempt seconds apartRetries and RetryIntervals (seconds) per tickerthe message's retry policies and dead-letter queuenone: OnError and a ScheduledEventFailed broadcast
What holds the waitthe job store: a new fire time on the trigger, which survives a restart and runs on any nodethe storage: the job waits in Scheduleda Task.Delay in the execution, holding the worker slot and the lease; lost with the processthe message store—
Jitteropt-in on Exponential: each wait × [1 − jitter, 1 + jitter]always, rand(30) × attempt secondsnonethe bus's policy—
Attempts run outback to the ordinary schedule, not an error state, or paused, with the error as the reason when you ask; reported by ITriggerListener.TriggerRetriesExhausted, a log event, a counter and a final history rowFailed, never expires; requeue from the dashboardrecorded as faileddead-letter queue—
Per-job timeout[JobTimeout], enforced once AddJobTimeout registers the middlewarenonenonethe message's ownnone

Hangfire retries by default; Quartz retries when the trigger, its job type or the scheduler names a policy, and jitters only when the policy says so. Quartz holds the wait in the store, so a node that dies during a backoff does not lose the retry.

Continuations and chaining

Quartz.NET 4.4Hangfire 1.8.25TickerQ 10.4.0Wolverine 6.41Coravel 6.0.2
Run B after AStartAfter(parentTriggerKey, condition), or ScheduleJob<TJob, TInput>(input, Continuation.After(…)) for a one-offBackgroundJob.ContinueJobWith(parentId, …)parent/child TimeTickersa handler publishes the next messagenone
Conditional on the outcomeContinuationCondition flags: OnSuccess (default), OnFailure, OnCancellation, OnVeto, OnAnyOutcomeJobContinuationOptions: OnAnyFinishedState, OnlyOnSucceededState (default), OnlyOnDeletedStateRunCondition: OnSuccess, OnFailure, OnCancelled, OnFailureOrCancelled, OnAnyCompletedStatus, InProgressthe handler decides—
Where the link is keptthe job store: a trigger in Awaiting, released or discarded in the parent's completion transaction on any node; shown in the dashboardthe storage; Awaiting in the dashboardthe persistence provider, on the child rowcode—
On a recurring scheduleyes, with any schedule; JobChainingJobListener links recurring jobs with the same conditionsyes: recurring jobs enqueue ordinary jobs, which can be continuedno, TimeTicker onlyyes—

Before 4.2 Quartz had only JobChainingJobListener: links were not persisted, the follow-up ran on the parent's node, and it ran even when the parent threw. Now the link is in the store, so a crash between the parent finishing and the release cannot lose it, and a continuation whose condition was not met is discarded. See Job Continuations.

Persistence

Quartz.NET 4.4Hangfire 1.8.25TickerQ 10.4.0Wolverine 6.41Coravel 6.0.2
Defaultin-memory RAMJobStorenone; a storage is requiredin-memorynone for the schedulein-memory only
Databases shipped by the projectSQL Server, PostgreSQL, MySQL, Oracle, SQLite, Firebird, through ADO.NETSQL Server and Hangfire.InMemory; Redis (Hangfire.Pro.Redis) is paidEF Core — SQL Server, PostgreSQL, SQLite in the docs, MySQL in the readme — plus RedisPostgreSQL, SQL Server, MySQL, Oracle, SQLite, for the message store—
Anything elseyour own store, or your own dialectcommunity packages (PostgreSQL, MongoDB, MySQL, SQLite), officially unsupportedyour own provider——
Creating the schemaProvisionSchema(), or the DDL yourself; or Weasel, through Quartz.Weasel, as Marten and Wolverine do, on every shipped database (MySQL, Oracle and Firebird from 4.4)automatic unless PrepareSchemaIfNecessary is offyour own EF Core migrationWolverine provisions its tables—

The dashboard, and what it allows before you configure anything

Quartz.NET 4.4Hangfire 1.8.25TickerQ 10.4.0Wolverine 6.41Coravel 6.0.2
In the boxQuartz.Dashboard, free, fourteen pagesin Hangfire.Core, freeTickerQ.Dashboard, freenone; CritterWatch is paidnone; Coravel Pro is separate
Who may reach it by defaultnobody: a mapping with neither RequireAuthorization nor AllowAnonymous, under a host with no fallback policy, fails at start-uplocal requests only; otherwise implement IDashboardAuthorizationFiltereveryone: AuthMode.None is the default——
Read-only modeyes, plus a job-type allow-list for scheduling through itIsReadOnlyFunc, off by defaultno——
Best atthe cluster: node check-ins, execution groups, misfires, an action log; pointed at a database, every scheduler in it without running them; a trigger edited in place or backfilled, filtered listings and bulk pause, resume and unschedulethe state machine: a page per state, one-click requeue or delete of failed jobs, singly or in bulklive SignalR monitoring, editing both ticker kinds, starting and stopping the host——

Hangfire's per-state lists are better for browsing failures by kind, and it requeues any failed job. Quartz's History page has a Failed after retries filter and a Run again button on each occurrence that gave up (When the policy gives up). Run again needs the job still stored: a non-durable job is deleted with its last trigger. From 4.4 the page also filters by result (Succeeded, Skipped, Failed, Cancelled) and by one job, and charts runs over time. The Jobs page shows each job's last run, last success and a failing ×N count from its run status.

Observability

Quartz.NET 4.4Hangfire 1.8.25TickerQ 10.4.0Wolverine 6.41Coravel 6.0.2
Tracestwo job spans, thirty-four store spans, Quartz activity sourcenone in the box; the OpenTelemetry community package is pre-releaseTickerQ.Instrumentation.OpenTelemetry: tickerq.job.execute.* spansthe bus's message spans, under a wolverine.recurring.occurrence span per occurrence tagged wolverine.schedule.namenone
Metricseleven instruments on the Quartz meternonenone; traces and ILogger events onlyyes, Wolverine's meternone
Health checkin the core package; from 4.4 it can report a job that stopped succeedingnonenoneWolverineFx.HealthChecks, separate packagenone
.NET AspireQuartz.Aspire: a connection name becomes a store with telemetry and health checknonenone—none

Trimming and native AOT

Quartz.NET 4.4Hangfire 1.8.25TickerQ 10.4.0Wolverine 6.41Coravel 6.0.2
DeclaredIsAotCompatible, no IL3050 anywherenoIsAotCompatible on four of six libraries; not the EF Core provider or OpenTelemetry packageIsAotCompatibleno
Checkeda native canary is published and run on Windows, Linux and macOS on every pull requestnot supported yetan AOT sample publishes nativelytrim and AOT analyzers, both target frameworks—
What you must still doname or root job types so the trimmer sees them; the page lists the paths that still warn—pass a JsonSerializerContext via WithJsonContext for payloads——

Target frameworks

Quartz.NET 4.4Hangfire 1.8.25TickerQ 10.4.0Wolverine 6.41Coravel 6.0.2
Targetsnet10.0; the maintained 3.x line covers .NET Standard 2.0 and .NET Frameworknet451, net46, netstandard1.3, netstandard2.0net10.0; 8.x and 9.x lines target net8.0 and net9.0net9.0 and net10.0net6.0
Serializing what is storedSystem.Text.Json; Newtonsoft.Json as a second serializerNewtonsoft.Json onlySystem.Text.Json; a JsonSerializerContext when trimmed——

Licence and price

Quartz.NET 4.4Hangfire 1.8.25TickerQ 10.4.0Wolverine 6.41Coravel 6.0.2
LicenceApache-2.0LGPL v3, or commercialMIT OR Apache-2.0, with a contributor CLAMITMIT
Free tiereverythingOpen: SQL Server and in-memory storage, community supporteverythingeverything in Wolverinethe library
Paid tiersnoneStartup $500, Business $1,500, Enterprise $4,500 per organization per year; batches and Redis are Pro, throttling is Acenone; a commercial model proposed on 3 August 2026 keeps existing MIT and Apache releases freenone for Wolverine; CritterWatch and AI Skills are paidCoravel Pro: free for personal use, $299 a year commercial

Where Quartz.NET costs more than it is worth

  • The first five minutes are longer. TickerQ is two registration lines and an attribute; Coravel is two lines and a fluent chain. Quartz's shortest form is comparable, and a delegate job is one call with the job as a lambda. A recurring schedule on a database still adds a builder chain, a connection string, a driver package and a schema.
  • A schedule costs a row. With a persistent store, adding a schedule is an INSERT and a round trip, and each firing costs a few more. TickerQ's in-memory default and Coravel are a dictionary insert. For thousands of short-lived one-off firings, use one durable job per job type with a trigger per firing.
  • Retry and jitter are opt-in. Hangfire retries every job by default and spreads the attempts. Quartz retries nothing until a trigger, a job type ([RetryPolicy]) or the scheduler (UseDefaultRetryPolicy) names a policy, and waits exactly the policy's waits unless it names a jitter. One line opts a whole scheduler in.
  • There are no queues. Hangfire's [Queue], with servers subscribing to different sets, routes work. Execution groups bound how much of a category runs at once but do not choose the node, and PreferredNode pins rather than balances.
  • A message may belong to a bus. An application already running Wolverine with an outbox, wanting "publish X every weekday at 03:00", should use Wolverine's schedule rather than a second runtime and set of tables — Quartz.NET with Wolverine covers both directions.
  • .NET Framework is 3.x territory. Quartz 4.x is net10.0 only; Hangfire still ships net451, and is the only one of the five whose current line a .NET Framework application can take.

Choose:

  • Quartz.NET for stored schedules with calendars, time zones, misfire policies and priorities; a cluster that fires each trigger on one node; limits counted across nodes; retries held in the store.
  • Hangfire for a queue with a state-machine dashboard, retry on by default, queue-based routing, or .NET Framework on a current line.
  • TickerQ or Coravel for the shortest setup, or many short-lived in-memory firings.
  • Wolverine's own schedule when the occurrence is a message in an application already on Wolverine.

Coming from one of these

  • Coming from Hangfire — the API mapping, and the semantics that differ
  • Coming from TickerQ — the same, shorter
  • Quartz.NET with Wolverine — running both in one host, and deciding which owns a schedule
Help us by improving this page!
Last Updated: 10/6/26, 2:13 PM
Contributors: Marko Lahma, Claude Opus 5.5
Prev
Multi-Tenancy
Next
Frequently Asked Questions