Modulify

Scheduled jobs

Call one of your own URLs on a schedule and read what it returned.

On this page

A cron calls one URL on a schedule. Modulify runs it from its own servers, records the response, and keeps a history you can read afterwards.

Crons belong to a project. Any member of the workspace that owns the project can create, edit and run them.

Before you begin

The request URL has to be public and use https. A cron cannot reach localhost or an address on a private network, so an endpoint that only exists on your machine will not work.

Crons keep firing whether or not the site is published. If you have never published this project, the Crons tab shows a banner saying so: "Crons run on our servers, so they keep firing whether or not this site is published. A cron that calls this site will fail until you publish it."

If you want the request to carry an API key, add it in the Secrets tab first. A cron can point a header at a site secret, but it cannot create one.

Open the Crons tab

Click More in the tab strip, then Crons. The address ends in /crons.

Three tabs sit under the heading:

  • Crons, the list of jobs on this site
  • Runs, the history of what each one did
  • Settings, export and the danger zone

An empty site shows No crons with the line "Schedule a URL call to run automatically." and a Create cron button. Once you have at least one, an Add cron button appears at the top right of the list instead.

A site can hold 10 crons. At the limit the Add cron button is disabled and a warning explains that you have to remove one to add another.

Create a cron

Click Create cron or Add cron. The form asks for five things.

Name is what the list and the run history call this job. It is limited to 64 characters. The placeholder is Nightly cleanup.

Runs is the schedule. See the next section.

Method is one of POST, GET, PUT, PATCH or DELETE. It defaults to POST.

Request URL is the endpoint to call, up to 2048 characters. The placeholder is https://mysite.com/api/cleanup.

Headers and the body are optional, and are covered below.

The submit button reads Create cron. If it is disabled, hover it: the tooltip names the one thing that is blocking you, such as "Give this cron a name." or "Enter a valid https request URL."

Choose a schedule

The Runs dropdown offers five kinds:

Runs What you pick Example
Every hour A minute Every hour at
Every day An hour and a minute Every day at 09
Every week One or more weekdays, plus a time Mon, Thu at 07
Every month A day of the month, plus a time Day 1 at 00
Custom (cron) A five field cron expression 0 9 * * 1-5

Hours are offered on the hour, from 00:00 to 23:00. Minutes are offered in five minute steps. The monthly picker offers Day 1 to Day 28, so a schedule never falls on a date that some months do not have.

Weekdays are chips you toggle: Mon, Tue, Wed, Thu, Fri, Sat, Sun. Picking none is rejected when you save.

Custom cron expressions

The Custom (cron) kind takes a standard five field expression: minute, hour, day of month, month, day of week. Ranges (1-5), lists (0,30), steps (*/15) and month or weekday names (JAN, MON) all work. Day of week accepts 0 to 7, where both 0 and 7 mean Sunday.

The shortcuts @hourly, @daily, @midnight, @weekly, @monthly, @yearly and @annually are accepted and expand to the matching expression. Anything else starting with @ is rejected.

The five minute floor

A cron runs at most once every five minutes. A schedule that would fire more often than that is refused when you save, with the message "A cron can run at most once every 5 minutes. Space the runs further apart." So */5 * * * * is fine and */2 * * * * is not.

Next runs and the timezone

Under the pickers, the Next runs box lists the next five times this schedule will fire, refreshed as you edit. If the schedule cannot be read, the reason appears there in red instead.

Below the list, "Times shown in" names the timezone, and the timezone itself is a searchable dropdown. The schedule is stored with that timezone, so a daily 09

job keeps firing at 09
local time across a daylight saving change. New crons start on your browser's timezone.

Send headers and a body

The Headers section starts empty with the line "No headers. Add one to send an API key or a content type." Use the plus button to add a row. A step can carry 20 headers.

Each row has a name, a source and a value. The source is either Value, which you type, or Site secret, which points at a key from the Secrets tab. If the site has no secrets yet, the picker reads No secrets yet and the form tells you to add one in the Secrets tab first.

Header values are encrypted. Once saved, a typed value is never shown again: the row displays four dots, followed by the last four characters if what you entered was at least eight characters long. Leave the field blank when editing and the stored value is kept.

Header names are checked as you save. The message reads "Use only letters, numbers and - . _ with no spaces!" Transport headers such as Host, Content-Length, Connection and anything starting with proxy- are rejected outright.

The body editor is enabled for POST, PUT, PATCH and DELETE, and is limited to 32,000 characters. For GET the editor is disabled and the note reads "GET requests do not send a body. This body is removed when you save."

A body that starts with { or [ is checked as JSON, and the form blocks saving with "This does not look like valid JSON yet." until it parses. Anything else is sent as you typed it. Nothing sets a content type for you, so add a Content-Type header if the endpoint expects one.

Inside the body you can reference a secret as {{secrets.KEY}} and it is substituted at request time. The JSON check ignores those tokens. Secrets are not substituted in the URL: put them in a header instead.

{
    "task": "cleanup",
    "token": "{{secrets.CLEANUP_TOKEN}}"
}

What happens when a cron fires

One HTTP request goes out with your method, headers and body. It has 30 seconds to answer.

A status in the 200 to 299 range is a success. Anything else is a failure, and the response body is recorded with it, up to 8,000 characters. A successful run records the status and the duration but not the body. A request that never completes records "The request could not be completed. The host may be unreachable or blocked."

Up to five redirects are followed. A redirect to a different origin strips headers whose name mentions auth, cookie, token, secret or a key, and drops the body. If the body was dropped on the way, the run fails and tells you to point the cron at the final URL. A response larger than 32,000 bytes is not recorded.

Run, pause and edit

Each row in the list shows the method, the URL, the schedule in plain words, when the next run is due, and a green or red dot for how the last run went.

The switch on the right pauses and resumes the cron. Pausing clears its next run time, and resuming recomputes it from now and resets the failure count.

The three dots menu holds four actions:

  • Run now fires the cron immediately
  • Run history jumps to the Runs tab filtered to this cron
  • Edit opens the form inline in the row
  • Delete removes the cron and its run history, after a confirmation

Run now is recorded like any other run, but it does not move the schedule and it does not count towards the consecutive failure total. When it finishes, a toast reports what the endpoint answered and points you at Runs for the full response.

Read the run history

The Runs tab lists recent runs, newest first, with a Success or Failed badge, the cron name, how long ago it ran and how many milliseconds it took. A run started by Run now carries a Manual tag.

Click a row to expand it. You get the step name, the response status, the duration, the method and URL that were actually called, the error if there was one, and the recorded response body.

The URL shown in an expanded run has its query string removed, so a token passed as a query parameter is never written into the history.

Three dropdowns filter the list: by cron (All crons), by outcome (All runs, Successful, Failed, Manual runs) and by order (Newest first, Oldest first). Refresh re-reads the list, Load more pages through it, and the X clears the filters once any are set.

Hovering a row reveals a small trash icon that removes that single run from the history.

Runs are kept for 30 days.

When a cron switches itself off

A cron disables itself in three cases, and the reason appears in amber under its name in the list:

  • 20 consecutive failures. The reason reads "Disabled after 20 consecutive failures." A successful run resets the count to zero, so only an unbroken run of failures trips it.
  • The project was deleted. The reason reads "Disabled because this project was deleted."
  • The schedule can no longer be resolved, which carries the parser's own explanation.

Flipping the switch back on clears the reason and the failure count.

If a scheduled slot is missed, the cron does not fire repeatedly to catch up. It skips forward to the next slot in the future.

Export and clear

The Settings tab holds three things.

Export crons downloads every cron on the site as JSON, including its schedule, URL, method, header names and body. Header values and secret values are never included. The file is named after the project slug.

Clear run history empties the Runs tab and leaves the crons running.

Delete all crons permanently deletes every cron on the site along with its history. Both of the danger zone buttons are disabled unless your workspace role can delete projects, and both ask you to confirm first.

From an AI client over MCP

Everything on this page is reachable from a connected AI client: list_site_crons, create_site_cron, update_site_cron, delete_site_cron, delete_all_site_crons, toggle_site_cron, run_site_cron and export_site_crons for the jobs themselves, and list_site_cron_runs, delete_site_cron_run and clear_site_cron_runs for the history. preview_cron_schedule checks a schedule before it is saved.

Creating and changing jobs needs the config:write scope, which is unticked by default. See MCP tools.

Next

  • Webhooks go the other way: Modulify calls your endpoint when the site publishes.
  • Publish a site covers getting the site live so a cron pointed at it can reach it.
  • Tabs and visibility explains the More menu the Crons tab lives in.