CronEngine docs

Schedule with confidence.
Know exactly what runs.

The practical guide to schedules, HTTP requests, response handling, alerts, troubleshooting, and plan capacity. No account is required to read it.

CronEngine schedule documentation illustration

Timezone aware

Schedules follow local calendar time

Fixed outbound IP

45.9.188.216 for allowlisting

Useful run history

Status, timing, errors, and response

Durable alerts

Delivery retries are handled separately

01 · Quickstart

Your first job in five minutes

You need a publicly reachable HTTP or HTTPS URL. CronEngine runs independently of your browser, so your computer can be off after the job is saved.

  1. 1

    Create the job

    Open Dashboard → New cron job. Give it a useful name and paste the endpoint URL.

  2. 2

    Choose the request

    Select GET for a simple trigger, or POST/PUT/PATCH when you need a raw request body. Add authentication or headers if required.

  3. 3

    Set the schedule

    Use the visual builder, a five-field expression such as 0 8 * * 1-5, or a macro such as @daily. The schedule uses your account timezone.

  4. 4

    Test and activate

    Use Test request to verify connectivity and success rules. Save the job as active, then inspect Run history after it fires.

Good first test: schedule a GET endpoint with @hourly, leave the default success rule and overlap protection enabled, and store responses only on failure.

02 · Feature guide

What you get after creating an account

These product tools are available on every plan, including Free. Plans change capacity, history retention, timeout length, channel count, and how frequently a schedule can run.

Build and control

Create jobs without guesswork

  • Visual schedule builder and raw cron
    Use guided fields, macros, or a five-field expression with live validation and an immediate reserved-run estimate.
  • Test request and manual runs
    Check the current form before saving, or trigger an existing job from the dashboard without waiting for its schedule.
  • Pause, resume, edit, and delete
    Temporarily stop a job without losing its request, schedule, organization, or alert settings.
  • Import and export
    Fill the form from a cURL command, a CronEngine JSON export, or a crontab. Copy or download a JSON snapshot for backups and moving job settings.

Monitor and debug

See what happened and what comes next

  • Operational dashboard
    Scan active and paused jobs, next runs, last outcomes, HTTP status, and scheduled-run usage.
  • Timezone-aware Agenda
    Browse upcoming executions by local day and time, with an option to include paused jobs.
  • Detailed run history
    Inspect success, status, duration, error explanations, and saved response content in pretty or raw form.
  • Execution charts and success rate
    Compare duration across recent runs and spot failures or performance changes visually.

Organize and operate

Keep a growing job list manageable

  • Nested maps
    Group jobs by environment, project, client, or workflow, with root maps and one level of sub-maps.
  • Colored tags
    Label jobs by priority, owner, environment, or task type for faster scanning.
  • Bulk actions
    Select jobs to pause or delete together, and bulk-manage maps and tags.
  • Fast dashboard actions
    Copy an endpoint URL, open history, edit, test, pause, resume, or delete from the job row.
No credit card on Free Maps and tags on every plan Charts and history on every plan Alerts and digests on every plan

03 · Schedules

Cron syntax and local time

CronEngine accepts a five-field expression. Names are not accepted in jobs created in the UI; use the numeric ranges below. Imported crontabs may normalize month and weekday names, and Sunday is stored as 0.

FieldAllowedExamples
Minute0-59*/5 every 5 minutes
Hour0-238,17 at 08:00 and 17:00
Day1-31 or LL last day of the month
Month1-121,7 January and July
Weekday0-61-5 Monday through Friday

Each field supports *, a value, comma-separated values, ranges, and steps: N, N,M, N-M, */S, N/S, or N-M/S. The special L is valid only by itself in the day field.

Examplesmin hour day month weekday
*/15 * * * *Every 15 minutes
0 8 * * 1-5Weekdays at 08:00
30 6 1 * *First of each month at 06:30
0 23 L * *Last day of each month at 23:00
0 9 15 * 1Only when the 15th is a Monday

Supported macros

@hourly0 * * * * @daily0 0 * * * @midnight0 0 * * * @weekly0 0 * * 0 @monthly0 0 1 * * @yearly0 0 1 1 * @annually0 0 1 1 *

Macros are case-insensitive and stored in lowercase. @reboot is not supported because dispatcher restarts are not a reliable per-job schedule.

Important: day-of-month and weekday use AND

When both fields are restricted, CronEngine requires both to match. Some Unix cron implementations use OR instead. In CronEngine, 0 9 15 * 1 runs at 09:00 only when the 15th of the month is a Monday, not on every Monday plus every 15th.

Timezone behavior

Schedules use the timezone in Account settings, not the server timezone. @daily means local midnight. Changing your timezone recalculates upcoming executions for all active jobs.

Example: 0 9 * * * in Europe/Brussels stays at 09:00 Brussels time as the UTC offset changes.

DST behavior

During the spring clock jump, a nonexistent local time is skipped. During the autumn rollback, a repeated matching local time runs once in each real occurrence.

Month lengths, leap years, and calendar boundaries are resolved from the real local calendar.

When a clock is the wrong answer

Two questions a cron expression cannot answer. Both live behind the same switch on the job form, next to Repeats, and neither changes anything about a job you already have.

Once, at a moment you pick

Choose Once and a date and time in your account timezone. It fires at that moment, then completes: it will not run again, it does not reserve any of your monthly runs, and it stops occupying an active-job slot the moment it has run. One-off requests draw on their own monthly allowance instead. If its moment passes while the job is paused, it stays paused rather than firing late.

After another job has finished

Choose After a job, pick the job to follow, and say which ending should start it: only after it succeeds, only after it fails, or either way. The follower has no clock of its own. It is armed when the job it follows finishes an execution, which means after every retry that execution made, on the verdict the execution ended with, not on whichever attempt happened to be last.

It costs what the job it follows costs

A follower runs whenever the job above it runs, so it reserves the same number of monthly runs and uses an active-job slot. Following an hourly job costs another hourly job. Change the schedule at the top of a chain and everything below it is repriced with it.

Chains are lines, not webs

A job follows one other job, and up to five jobs can follow the same one. A chain can be at most five jobs long, and a link that would loop back on itself is refused when you save. Only a repeating job, or a job that already follows one, can be followed: a one-off has already spent its moment.

Failures do not cascade quietly

A follower set to run only after success simply does not run when the job above it fails, and nothing is recorded for it. Each job in a chain keeps its own timeout, retries, overlap protection, notifications and history. Delete a job that others follow and they are paused, so nothing is left waiting on something that no longer exists.

04 · HTTP requests

What CronEngine sends

Your endpoint must be publicly reachable. HTTP works, but HTTPS is strongly recommended whenever a URL, header, or body contains sensitive data.

Methods

GET, POST, PUT, PATCH, DELETE, and HEAD are supported.

Custom headers

Enter one Name: value header per line, up to 8,000 bytes total.

Request bodies

POST, PUT, and PATCH send a raw body of up to 10,000 bytes.

Content types

Choose JSON, form-urlencoded, or plain text for body methods.

Authentication

No auth, Bearer token, or HTTP Basic username and password.

Network safety

Private, local, metadata, reserved, blocked hosts, and blocked IPs are rejected.

Example POST request
POST /hooks/nightly HTTP/1.1
Host: api.example.com
User-Agent: CronEngine-Dispatcher/2
Authorization: Bearer your-dedicated-token
Content-Type: application/json
Accept: application/json
X-Environment: production

{"source":"cronengine","task":"nightly-sync"}

Managed headers

CronEngine manages Authorization through the auth fields and manages Content-Type through its selector. Connection, content length, host, proxy, transfer-encoding, trailer, upgrade, and user-agent headers cannot be overridden.

Credential storage

Job tokens, Basic credentials, custom header values, and request bodies are encrypted with AES-256-GCM before they are stored, under a key held outside the database and kept out of database backups. Workers decrypt each value only when they send your request. Authentication secrets are write-only: once saved they are never shown again, and you replace rather than read them. Still use unique, least-privilege credentials that can be rotated or revoked.

Variables

The URL, the request headers and the request body can all contain {{...}} placeholders. CronEngine fills them in at the moment the request goes out, which is why {{now}} is the time of the actual call and {{execution.attempt}} counts up across retries.

Variable Value Example
{{job.id}} This job's numeric id 57
{{job.name}} The job name you gave it Nightly report
{{execution.id}} Id of this run, identical across its retries exe_290aa7905d54b2220d5430fe650f2e79
{{execution.attempt}} Attempt number, 1 on the first try 1
{{execution.scheduled_at}} When this run was due (UTC) 2026-09-10T22:00:00Z
{{now}} Current time in UTC 2026-09-10T22:00:15Z
{{now.unix}} Current time as a Unix timestamp 1789077615
{{now.date}} Current date in your timezone 2026-09-11
{{now.time}} Current time of day in your timezone 00:00:15
{{now.local}} Current time in your timezone 2026-09-11T00:00:15+02:00
Written like this
https://api.example.com/reports/{{now.date}}?run={{execution.id}}

X-Run-Id: {{execution.id}}
X-Attempt: {{execution.attempt}}

{"job":"{{job.name}}","due":"{{execution.scheduled_at}}","sent":"{{now}}"}

Values are encoded for where they land

A value in a URL is percent-encoded, a value in a JSON body is escaped as a JSON string, one in a form body is form-encoded, and one in a header loses anything that could start a new line. A job named Bob"s job therefore cannot break your JSON, and a variable can never introduce a slash or a colon that would move the request to a different path or host. For the same reason, variables are not allowed in the hostname.

Anything else is left alone

Only the names in the table are replaced. If your body already contains {{customer_id}} for a template engine further down your own pipeline, it is sent exactly as written. The same is true of a typo: {{job.nmae}} arrives verbatim rather than turning into an empty string, so the mistake is visible in your logs. Test request renders variables the same way the scheduler does, so you can see the finished request before saving.

05 · Responses

Success, redirects, and saved content

Default success rule

Any HTTP 2xx or 3xx response is successful.

A network error, DNS failure, refused connection, TLS failure, or timeout is always a failure because no response was received. You can customize accepted ranges, add exact codes, and optionally require that the captured body contains or does not contain case-sensitive text. Status and body conditions can use AND or OR.

Redirects

Scheduled executions follow at most four redirect hops (five requests in the chain, including the original). Every destination is checked against the outbound safety rules. Test request and Run now do not follow redirects.

10 KB capture limit

CronEngine reads the first 10 KiB (10,240 bytes) of a response. Larger responses receive a [truncated] marker. Body success checks use this captured portion.

Plan timeout

The request stops at your plan’s timeout, from 20 seconds on Free through 600 seconds on Enterprise. A timeout is recorded as a failure.

Save modeWhat is stored in run history
AlwaysThe captured response for successful and failed runs.
Only on failureThe captured response only when the configured success criteria fail.
NeverNo response body. Status, duration, success, and error details are still recorded.

The save mode controls history storage, not whether CronEngine captures the first 10 KB while evaluating body-based success rules.

06 · Execution

Skips are not failures

Overlap skip

Expected control flow

Overlap protection is enabled by default. If the previous execution is still within its lease when the next occurrence becomes due, that occurrence is consumed and skipped.

  • No failed history row
  • No health-state change
  • No failure notification
Failed run

An attempted execution failed

The request was attempted and encountered a network error or did not satisfy the job’s success criteria.

  • Recorded in run history
  • Updates consecutive-failure health
  • May create a notification event

Automatic retries

Retries are off by default on every job, and every job you already have keeps behaving exactly as it does today. Turn them on per job under Execution behavior, from zero up to your plan's ceiling. Leave them at zero when calling your endpoint twice would do the work twice.

Only failures that a second try could fix

Timeouts, refused or reset connections, and statuses that mean "come back later": 408, 425, 429, 500, 502, 503, 504 and Cloudflare's 52x. A 404, a 401, a hostname that does not resolve, or a response that simply failed your success criteria is a settled answer, so it is left alone.

Backoff, and one run either way

Waits grow between attempts, 15s, 30s, 60s, then 2 minutes, each jittered. Every attempt belongs to one execution: it costs one scheduled run, keeps one slot of history, and only notifies you once the run is really over. A run that fails and then succeeds on its retry is a success, and never emails you.

Your endpoint can tell attempts apart

Every attempt carries the same X-CronEngine-Execution-ID and an increasing X-CronEngine-Attempt, so a receiver that needs exactly-once behaviour can deduplicate on the execution id.

Notification delivery retries separately

Once a run creates an alert event, delivery has its own durable outbox. Transient email/provider/network failures and webhook HTTP 408, 425, 429, or 5xx responses are retried with exponential backoff, up to five attempts by default. That retries the alert, not the job.

Before you turn retries on

  • Your endpoint may do the same work twice, seconds apart. A retry is the identical request sent again: a charge taken, an email sent, a row inserted can all happen a second time, and CronEngine cannot undo it. If the work is not safe to repeat, either leave retries at zero or deduplicate on the X-CronEngine-Execution-ID header, which is identical across every attempt of one run.
  • Retries do not extend your request timeout. Each attempt is still cut off at your plan's timeout; a job that needs longer than that needs a bigger plan, not retries. Retrying a request that timed out simply tries again from zero.
  • A failing run stays busy for longer. Attempts plus the waits between them add up: on Business, 3 retries of a 300-second job can keep one occurrence going for roughly 22 minutes before it is finally recorded as failed. With Prevent overlapping runs on, occurrences due inside that window are skipped, so a frequently scheduled job runs noticeably less often while its endpoint is down. The job form shows the worst case for the setting you pick.

Free-plan safety: a Free job is automatically paused after 20 consecutive failed executions and a one-time account email is queued. A success resets the count; manually resuming the job starts a fresh count.

07 · Notifications

Choose when and where alerts arrive

TriggerBehavior
NoneNo per-run alert events.
Every failureCreates an alert for each failed execution.
State changeAlerts on the first failure, then again on recovery.
Failure thresholdAlerts when consecutive failures reach your threshold, then on recovery.
Every runAlerts for successful and failed runs; available on paid plans.

Channels include email, generic signed webhooks, Slack, Discord, and Telegram. Channel counts and daily delivery caps depend on the plan and are enforced independently per channel type. Weekly and monthly account digests are separate from per-job alerts.

Generic webhook payload

What CronEngine sends to your endpoint

Generic webhooks receive an HTTPS POST with JSON. The envelope is versioned, contains a stable event ID, and includes the job and run snapshot that caused the notification.

application/json version 1
{
  "version": "1",
  "id": "evt_1842",
  "type": "job.failed",
  "created_at": "2026-07-25T08:09:10Z",
  "job": {
    "id": 7,
    "name": "Nightly invoice sync",
    "url": "https://example.com/tasks/invoices",
    "history_url": "https://cronengine.com/jobs/history?id=7"
  },
  "run": {
    "id": 9124,
    "success": false,
    "http_status": 503,
    "duration_ms": 812,
    "error": "The endpoint returned HTTP 503.",
    "fired_at": "2026-07-25T08:09:10Z"
  }
}

Event types

job.failed
A failure alert or configured threshold was reached.
job.recovered
The first success after an open failure incident.
job.run.succeeded
A successful every-run notification.
job.run.failed
A failed every-run notification.

Delivery contract

  • Delivery is at least once, so the same event can arrive more than once.
  • Return any 2xx status to acknowledge the event.
  • Store id and ignore duplicates with the same ID.
  • Network errors, 408, 425, 429, and 5xx responses retry. Other non-2xx responses fail permanently.
  • http_status is null when no HTTP response was received.
  • Notification redirects are not followed.

Headers on every generic webhook

X-CronEngine-Webhook-Version: 1
X-CronEngine-Event-Id: evt_1842
X-CronEngine-Event-Type: job.failed
X-CronEngine-Timestamp: 1784966950

Optional signature

When a signing secret is configured, CronEngine adds X-CronEngine-Signature: v1=<hex HMAC-SHA256>. Verify the HMAC over <timestamp>.<raw body> before decoding JSON, use a constant-time comparison, and reject stale timestamps.

Slack and Discord use their native incoming-webhook payloads. Telegram uses the official Bot API. Each includes the outcome, HTTP status, duration, error, history link, and stable event ID.

Full webhook reference

08 · Heartbeats

Watch the work we cannot run

A cron job is CronEngine calling your URL. A heartbeat is the mirror image: your script calls us when it finishes, and we raise the alarm when that call does not arrive. Use it for work that has to run on your own machine, where we cannot reach in to start it.

 Who callsWhat it notices
Cron jobWe call your URLA call of ours that failed
HeartbeatYour script calls usSilence

That second row is the point. If the machine is switched off, nothing fails and nothing is reported, so an outbound check sees a healthy nothing. A heartbeat notices that the ping stopped.

One line in your crontab

Create a monitor and you get a ping URL. Call it when your work finishes.

0 3 * * * /usr/local/bin/backup.sh && curl -fsS https://cronengine.com/ping/hb_…

The && matters: the ping only happens when the script succeeded. Anyone holding that URL can report that your work ran, so treat it like a password. Deleting the monitor is the only way to revoke one.

URLMeaning
/ping/<token>Finished, successfully. This is the one that moves the deadline.
/ping/<token>/startStarted. Gives you a duration, and catches a run that begins but never ends.
/ping/<token>/failFinished, and it knows it failed. Marks it down without waiting for the deadline.

Two ways to say when it is due

Every so often for "at least once a day". On a schedule for work that does not run around the clock: a cron expression in your account timezone, built with the same editor a job uses. 0 3 * * 1-5 expects a ping on weekday mornings and stays quiet all weekend instead of alarming every Saturday.

Grace time, and going down

Grace is how late a ping may be before we call it down, so a job that usually takes twenty minutes and sometimes takes two hours does not wake you. A monitor that has never been pinged stays Waiting and never alarms: creating one and not wiring it up yet is not a failure.

Heartbeats do not use your scheduled runs

An inbound ping costs no outbound request and no worker, so monitors have their own plan allowance and never draw on the monthly run reservation. Alerts go to your account email plus any channel you configure, and count against the same daily notification cap as your jobs.

08 · Troubleshooting

When a request fails

No response / network error

Confirm the hostname resolves publicly, the port is reachable, and the URL does not point to localhost, a private network, cloud metadata, a reserved range, or a blocked CronEngine address. Check DNS, TLS certificate validity, and your plan timeout.

HTTP 401 or 403

Check the Bearer token or Basic credentials. Confirm the credential is active and has permission for this endpoint. If a WAF or firewall is involved, allowlist the CronEngine outbound IP.

HTTP 404

Verify the full path and environment. Redirects are followed for scheduled jobs, but a redirect cannot rescue an incorrect final route.

HTTP 405

The endpoint rejected the selected method. Match GET, POST, PUT, PATCH, DELETE, or HEAD to what your server route accepts.

HTTP 415 or body parsing errors

Match the selected Content-Type to the raw body. For JSON, validate the JSON and use application/json. For form data, URL-encode the body.

HTTP 429

Your endpoint is rate-limiting scheduled executions. Reduce the cadence or change the endpoint limit. The job itself is not automatically retried.

HTTP 5xx

The request reached your application. Inspect its server logs using the run timestamp, then use the stored response and CronEngine error explanation for context.

Response looks cut off

Run history and body matching use only the first 10 KiB. Put health or success markers near the start of the response and keep diagnostic payloads concise.

Expected run is missing

Check whether the job is active, whether DST skipped a nonexistent local time, and whether overlap protection skipped an occurrence while the prior execution lease was active.

Firewall allowlisting

Allow inbound requests from CronEngine

Add this single IPv4 address to the allowlist for the exact route and port CronEngine calls. Keep normal authentication enabled; an IP allowlist should be an additional check, not the only credential.

45.9.188.216

Scheduled job requests identify themselves with User-Agent: CronEngine-Dispatcher/2. The fixed IP is the reliable network allowlist value; user-agent strings can be imitated and should only help with logs or routing.

09 · Plan usage

Capacity is reserved up front

Scheduled runs are a reservation model, not a meter. Each active job reserves how often its expression is expected to fire in a fixed 30-day month. CronEngine checks the total when you create, edit, or resume a job. Actual executions are not counted afterward, so capacity never runs out midway through a month and nothing resets on the first.

Feature availability

Plan capacity at a glance

See current pricing
Plan Active jobs Reserved runs Fastest schedule Timeout History per job Retries per run One-off requests Alert channels
Free53,000Every 15 min20 sec5 runsNone5 / month1
Starter2510,000Every 5 min60 sec20 runsUp to 150 / month1
Pro5030,000Every 2 min150 sec30 runsUp to 2250 / month2
BusinessUnlimited90,000Every minute300 sec40 runsUp to 31,000 / month3
EnterpriseUnlimited300,000Every minute600 sec50 runsUp to 55,000 / month3

One-off requests are the one allowance that is counted rather than reserved, and they reset at the start of each month: a request that runs once is spent, so there is nothing to hold capacity for. They never draw on your scheduled runs, and a pending one does not use an active-job slot. Every plan includes failure, recovery, consecutive-failure threshold alerts, and weekly or monthly digests. Every-run notifications are available from Starter. Daily notification caps are 5, 20, 50, 100, and unlimited per channel type from Free through Enterprise. Retries are a ceiling, not a default: each job is created with retries off until you raise it, and a retried run still costs a single reserved run.

CadenceReservationMath
Every minute43,20060 × 24 × 30
Every 5 minutes8,64012 × 24 × 30
Every 15 minutes2,8804 × 24 × 30
Hourly72024 × 30
Daily301 × 30

Worked example · Starter

A mixed workload fits

Every 5 min8,640

Hourly720

Daily30

Reserved9,390

Starter limit10,000

Remaining610

Worked example · Free

One more hourly job does not fit

Every 15 min2,880

Proposed hourly720

Would reserve3,600

Free limit3,000

Over by600

Pause another active job, run the new job less often, or change plan. Paused jobs reserve no capacity.

Calendar-specific schedules are estimated across a canonical 365-day year and scaled to 30 days. This correctly charges schedules involving day 31, weekdays, L, and CronEngine’s day-plus-weekday AND behavior. Any valid rare schedule reserves at least one run.

Compare plan limits and timeouts

10 · Account settings

Controls you will find after sign-in

Profile and timezone

Update your display name and IANA timezone. A timezone change reschedules active jobs and controls the local dates used for digests and reports.

Notification email

Send alerts to a dedicated address. A new notification address must be verified before it becomes the active destination.

Email digests

Choose off, weekly, or monthly. Weekly summaries are prepared after 08:00 Monday; monthly summaries after 08:00 on the first, in your timezone. Empty digests are not sent.

Login security

Review active sessions and revoke other remembered or logged-in devices. Password changes and account access controls live under Security.

Billing

Paid accounts can open Stripe’s hosted portal to update payment details, review the subscription, and view billing history.

Archive account

Archiving pauses product use while preserving the account. Deletion and other account-management options are explained in Settings.

Ready to configure your account?

The documentation stays public; account controls require a signed-in, verified account.