Skip to content
V
For developers
Architecture, conventions, the core and security
Getting started / Scheduled jobs

Scheduled jobs#

Everything the system does on its own. The list is generated from the code, so it names exactly the jobs the repository really contains.

How a job is called#

GET /cron/<module>/<presenter>/<action>?token=<cron.token>
Thing How it works
Authorisation the request must carry a ?token= matching the cron.token setting (group 1)
Response always JSON — {"status":"ok",…}, {"status":"error",…} or {"status":"skipped"}
Concurrency a second instance of the same job ends as skipped, it does not wait

Without a token it returns 403 — and an empty token lets NOBODY in

The check compares the expected and the supplied value; when cron.token is missing from the settings, not even an empty request gets through. On a fresh server it is the first thing to fill in — otherwise nothing runs at all and nothing anywhere complains.

The concurrency lock is fail-open

Until the lock table exists, skipped is never returned and jobs may overlap. That is deliberate — a missing table must not stop operations — but it means "I have never seen skipped" is no proof that the lock works.

How to tell a job actually ran

By its response, not by the fact that "nothing crashed". {"status":"ok"} is also returned by a job that did nothing at all — the counts are in the other keys of the response.

The scheduler — one address instead of fifty#

There are around seventy jobs. Entering them one by one at the hosting is forgotten with every new job, so the scheduler calls them: the hosting calls a single address

GET /cron/system/scheduler/run?token=<cron.token>        (every 5 minutes)

and the scheduler uses the cms_system_cron_jobs table to decide which jobs are due, then calls them one by one over HTTP on its own address with the same token — no job is rewritten. The administrator manages it in the administration (Settings → Cron, see Task scheduler).

Thing How it works
Catalogue the #[CronJob(frequency: …, outbound: …, query: […])] attribute on a public action*() of a cron presenter — the source of truth; the docblock (URL:, Frekvence:) stays for this overview
A new job without the attribute the scheduler does not know it — CronJobCatalogTest fails for every action* lacking it. It is added to the table by itself on the scheduler's next call (no migration)
Frequency text every:N · daily:HH:MM · weekly:D@HH:MM · monthly:D@HH:MM · manual (CronFrequency); "once after deployment" and repair jobs = manual
outbound a required argument (no default): true = sends e-mails/SMS, issues documents, deletes/anonymises user data, calls payment gateways or notifies watchdogs. An outbound job starts disabled. When in doubt, true
query fixed call parameters (e.g. sitemaps ['save' => '1'] — without save they only return a preview)
Modules cms_system_cron_jobs.module_id from the path /cron/<module>/… (cms_system_modules); jobs of a module with activated = 0 are neither shown nor run, the system module is always on
Run 45 s budget, the longest-waiting first; an error or timeout = error state and the next try only at the next term; "skipped" (the job's lock) is retried on the next call. The response is stored shortened (≤ 500 characters) and the token never reaches it or any log
Concurrency two schedulers: the second ends skipped on the lock (cron.system.scheduler.run) like any cron
The scheduler is not a job SchedulerPresenter is left out of the catalogue (CronJobCatalog::EXCLUDED_PRESENTERS)

The scheduler calls jobs over HTTP on its own address

On a single-threaded php -S (the local dev server) such a call hangs and ends with a timeout — the server cannot serve a second request until the first one finishes. Verify with several workers: PHP_CLI_SERVER_WORKERS=4 php -S ….

Adding a new cron action?

  1. Add #[CronJob(frequency: '…', outbound: true|false)] above action*() (and query if needed).
  2. Add the job name to the translations admin.system.presenters.cronJob.jobs.<module>.<presenter>.<action> (cs/en/sk); without a translation the administration shows only the address.
  3. The rest (table, list, scheduling) happens by itself.

Deployment#

The Suggested interval column is the recommendation from the source code — build your crontab from it. For example:

*/1  * * * *  curl -s "https://site.com/cron/system/email-queue/send?token=TOKEN"
*/5  * * * *  curl -s "https://site.com/cron/store/reservation/expire?token=TOKEN"
15   3 * * *  curl -s "https://site.com/cron/blog/sitemap/article?token=TOKEN"

An unscheduled job NEVER runs — and nothing breaks

It raises no error. It merely stops doing what it was meant to: carts do not expire, invoices are not generated, data is not cleaned up. It usually surfaces only when something "has somehow grown". So walk the whole list below after an installation, not just the parts you happen to use.

Not everything is periodic

A few jobs are repair or one-off tasks — their interval says "by hand". They do not belong in the crontab; run them when needed.

The sitemap index goes last, after the other sitemaps

It assembles a directory of already generated files. Run first, it builds one from whatever the previous run left behind.

The jobs#

Shared#

Endpoint What it does Suggested interval
/cron/base/newsletter/dispatch Dispatches executing newsletters into the e-mail queue in batches ; the queue cron sends them every 5 minutes
/cron/base/rating/recalc Recalculates the rating thread aggregates as a safety net against drift periodically, e.g. once a day

Classifieds#

Endpoint What it does Suggested interval
/cron/bazaar/advert/notice-expiration Sends BZ_ADVERT_EXPIRATION_NOTICE to adverts expiring within N days daily, at night
/cron/bazaar/advert/delete-expired Silently marks deleted=1 on adverts expired for more than M days; no e-mail is sent daily
/cron/bazaar/advert/recount-prices Recalculates the denormalized price column of all adverts from the price parameter every 10-15 minutes
/cron/bazaar/category/recount Recalculates the denormalized advert_count of all categories every 10-15 minutes
/cron/bazaar/elastic/reindex Full reindex of adverts into a new index version with an atomic alias swap once a day, at night
/cron/bazaar/elastic/sync Incremental sync: indexes adverts inserted since the watermark and cleans up orphans every 10 minutes
/cron/bazaar/sitemap/category Sitemap of all active Bazaar categories with hreflang variants daily, at night
/cron/bazaar/sitemap/advert Sitemap of active adverts, split into files by ID ranges (size depends on the number of languages) daily, at night
/cron/bazaar/watchdog-email/send Modes 1 (immediate) and 3 (after the advert becomes active) continuously, e.g. every 5-10 minutes
/cron/bazaar/watchdog-email/digest Mode 2: daily digest once a day, e.g. at 23:00
/cron/bazaar/watchdog-email/purge-old Queue retention of the watchdog: deletes sent and canceled _watchdog_email_rel rows older than N days, plus e-mails (_watchdog_emails and texts) no row points to anymore daily

Blog#

Endpoint What it does Suggested interval
/cron/blog/elastic/reindex Full reindex of articles into a new index version with an atomic alias swap once a day, at night
/cron/blog/elastic/sync Incremental sync: indexes articles changed since the watermark and cleans up orphans every 10 minutes
/cron/blog/sitemap/article — daily, at night
/cron/blog/sitemap/event — daily, at night
/cron/blog/sitemap/news — daily, at night
/cron/blog/sitemap/article-category — daily, at night
/cron/blog/sitemap/event-category — daily, at night
/cron/blog/sitemap/news-category — daily, at night

Company catalogue#

Endpoint What it does Suggested interval
/cron/comcat/advert/notice-expiration Sends CC_ADVERT_EXPIRATION_NOTICE to company profiles expiring within N days daily, at night
/cron/comcat/advert/delete-expired Silently marks deleted=1 on company profiles expired for more than M days; no e-mail is sent daily
/cron/comcat/category/recount Recalculates the denormalized advert_count of all categories every 10-15 minutes
/cron/comcat/elastic/reindex Full reindex of company profiles (Comcat Advert) into a new index version with an atomic alias swap once a day, at night
/cron/comcat/elastic/sync Incremental sync: indexes company profiles inserted since the watermark and cleans up orphans every 10 minutes
/cron/comcat/sitemap/category Sitemap of all active Comcat categories with hreflang variants daily, at night
/cron/comcat/sitemap/advert Sitemap of active company profiles, split into files by ID ranges (size depends on the number of languages) daily, at night
/cron/comcat/watchdog-email/send Modes 1 (immediate) and 3 (after the advert becomes active) continuously, e.g. every 5-10 minutes
/cron/comcat/watchdog-email/digest Mode 2: daily digest once a day, e.g. at 23:00
/cron/comcat/watchdog-email/purge-old Queue retention of the watchdog: deletes sent and canceled _watchdog_email_rel rows older than N days, plus e-mails (_watchdog_emails and texts) no row points to anymore daily

Company#

Endpoint What it does Suggested interval
/cron/company/sitemap/service Sitemap of the company service detail pages (Front:Company:Service:detail) daily, at night (before the sitemap index at 04:30)

Discussion#

Endpoint What it does Suggested interval
/cron/discussion/elastic/reindex Full reindex of thread roots (level=1) into a new index version with an atomic alias swap once a day, at night
/cron/discussion/elastic/sync Incremental sync: indexes new threads since the watermark and cleans up orphans every 10 minutes
/cron/discussion/sitemap/message Sitemap of top-level (level=1) active messages, split by ID ranges (size depends on the number of languages) daily, at night
/cron/discussion/sitemap/category Sitemap of all Discussion categories daily, at night

E-shop#

Endpoint What it does Suggested interval
/cron/eshop/cart/cleanup Deletes abandoned carts whose retention period has expired daily, at night
/cron/eshop/complaint/expire Closes complaints whose resolution deadline has passed daily (deadlines are counted in days)
/cron/eshop/elastic/reindex Full reindex of sale cards into a new index version with an atomic alias swap once a day, at night
/cron/eshop/elastic/sync Incremental sync: indexes cards changed since the watermark and cleans up orphans every 10 minutes
/cron/eshop/feed/generate Generates XML feeds for product comparison sites hourly (or as the comparison site requires)
/cron/eshop/order/expire Cancels unpaid orders and generates missing invoices hourly
/cron/eshop/order/cancel-unpaid Cancels unpaid orders and generates missing invoices daily (the period is counted in days)
/cron/eshop/order/invoice Cancels unpaid orders and generates missing invoices every 15 minutes
/cron/eshop/price/recalc Activates planned prices and repairs the denormalized price column every 15 minutes (a longer interval delays activation of planned prices)
/cron/eshop/product-import/run Runs the oldest queued job every minute
/cron/eshop/sitemap/category Sitemap of all e-shop categories with hreflang variants daily, at night
/cron/eshop/sitemap/product Sitemap of visible products (sale cards), split into files by ID ranges (size depends on the number of languages) daily, at night

Invoicing#

Endpoint What it does Suggested interval
/cron/invoicer/payment/generate Issues invoices for paid payments according to the module rules often, e.g. every 15 minutes: invoices appear shortly after a payment status change, but outside
/cron/invoicer/schedule/generate Issues recurring invoices for schedules that are due daily

Warehouse#

Endpoint What it does Suggested interval
/cron/store/batch/expiry-alert Alerts about batches nearing expiration daily, in the morning
/cron/store/reservation/expire Releases stock reservations that have expired every 5 minutes
/cron/store/stock/recount Nightly stock recount: repairs reserved quantities, batches and card prices daily, at night
/cron/store/stock/low-stock-alert Alerts about cards below the minimum stock: one summary e-mail to stock.lowStock.email daily, in the morning

System#

Endpoint What it does Suggested interval
/cron/system/asset-thumbnail-catchup/generate Generates missing image variants (poster from the first frame) for video assets whose variants were never physically generated, typically because ffmpeg was missing or failed at upload time daily, at night
/cron/system/asset-thumbnail-catchup/backfill-meta Backfills Asset.meta (real variant sizes) for assets where it is missing (meta IS NULL) once after deployment, no longer needed afterwards
/cron/system/asset-variant-regenerate/default Regenerates missing variants of stored images, for all usages at once repeatedly after deployment until the response says remaining: 0; then daily at night is
/cron/system/auth-log/purge-old Periodic cleanup of the persistent audit log (cms_system_auth_log) daily; otherwise the table grows
/cron/system/dropzone-temp/clean Cleans dropzone temp folders (temp/dropzone): files and empty folders older than 24 h daily
/cron/system/elastic/status Elasticsearch cluster health plus document counts per index; a monitoring smoke check manually (state check)
/cron/system/elastic/watch Watches search availability and reports problems by e-mail every 15 minutes
/cron/system/email-queue/send Sends e-mails from the queue gradually every minute
/cron/system/email-queue/purge-old Cleans processed (sent or stopped) e-mails older than N days; newsletter rows stay daily
/cron/system/file-system/sanitize Sanitizes file and folder names in the uploads directory manually, after bulk file uploads (not periodic)
/cron/system/jwt-deny-list/cleanup-expired Cleans expired rows of cms_system_jwt_denylist (the DB-backed JtiDenyList) daily, at night
/cron/system/log/purge-old Retention of the log/ directory daily
/cron/system/notification/clean-orphans Detaches orphaned admin notifications from their target (the target record, such as an order, user or payment, no longer exists) daily
/cron/system/notification/purge-read Detaches orphaned admin notifications from their target (the target record, such as an order, user or payment, no longer exists) daily
/cron/system/page-tree/rebuild Regenerates the nested-set indexes (lft, rgt, level) of the page tree in cms_system_pages from the adjacency list (parent_id) manually, after a bulk change of the page tree (not periodic)
/cron/system/payment/activation-audit Payment maintenance jobs: the activation audit and the retention of the gateway notification history every 15 minutes
/cron/system/payment/purge-notifications Payment maintenance jobs: the activation audit and the retention of the gateway notification history every 15 minutes
/cron/system/rate-limit/purge-old Cleans expired rows of cms_system_rate_limit (the DB-backed RateLimit) daily, at night (a no-op with useRedis: true)
/cron/system/scheduler/run Cron scheduler: the only address the hosting has to call every 5 minutes
/cron/system/sitemap/generate Generates the XML sitemap of System pages with all language variants (hreflang) daily, at night
/cron/system/sitemap/index Generates the sitemap index linking all generated sitemap- .xml files in the directory daily, at night, after the other sitemaps
/cron/system/user/anonymize-deleted Account deletion: anonymizes the accounts whose grace period (account.deletion.graceDays, default 7 days) has passed daily
/cron/system/user-refresh-token/purge-old Periodic cleanup of expired refresh tokens (cms_system_user_refresh_tokens) daily

Cookie settings