# create_site_cron

Source: https://modulify.ai/docs/mcp/scheduled-jobs/create-site-cron

Creates a scheduled job on a site that calls a URL on a repeating schedule.

- Title: Create a scheduled job
- Scope: `config:write`
- Access: Makes changes

A site holds at most 10 jobs, and the call is refused once that is reached. Runs cannot be closer together than 5 minutes. The job starts firing on its schedule straight away unless `enabled` is false.

A schedule takes a `Kind` and only the fields that kind uses. `hourly` needs `Minute`, `daily` needs `Hour` and `Minute`, `weekly` needs `DaysOfWeek`, `Hour` and `Minute`, `monthly` needs `DayOfMonth`, `Hour` and `Minute`, and `cron` needs a five field `Expression`. An optional `Timezone` takes an IANA zone name such as `Europe/Berlin`, and without one the schedule runs in UTC. The tool tells the client to check the schedule with `preview_cron_schedule` before committing it, because a job that fires more often than intended calls somebody else's URL every time.

Every header needs a `Name` and a `Source`. A `Name` takes letters, numbers and `-` `.` `_` only, with no spaces, and transport headers such as `Host`, `Content-Length` and `Connection`, and any name starting with `proxy-`, are refused. `literal` stores its `Value` encrypted at rest and reads it back masked, while `secret` stores a reference to the environment variable named in `Secret`, which is where a credential belongs, so the tool tells the client to put credentials there rather than as literal text.

Private and loopback addresses are refused as the `Url`, and a `Body` sent with `GET` or `HEAD` is dropped rather than refused. A `{{secrets.MY_KEY}}` token is expanded in the `Body` only: it is refused in the `Url` and means nothing in a header. A header or token that names a variable the site does not have is refused, so set the variable first with `set_secret` or in the Secrets tab. See [Scheduled jobs](https://modulify.ai/docs/automations/scheduled-jobs).

## Inputs

| Input | Type | Required | Description |
| --- | --- | --- | --- |
| `projectId` | string | Yes | The site id. |
| `schedule` | object | Yes | When the job runs, in UTC unless `Timezone` names a zone. Its fields are `Kind` (`hourly`, `daily`, `weekly`, `monthly` or `cron`), `Minute` (0 to 59), `Hour` (0 to 23), `DaysOfWeek` (a list of integers from 0 to 6, Sunday is 0), `DayOfMonth` (1 to 31), `Expression` (a five field cron expression, used only when `Kind` is `cron`, where the `@hourly`, `@daily`, `@weekly`, `@monthly` and `@yearly` shorthands are accepted too) and `Timezone` (an IANA zone name such as `Europe/Berlin`). Fill only the fields the `Kind` uses. |
| `steps` | array of objects | Yes | What the job does, as exactly one HTTP request. Its fields are `Type` (always `http.request`), `Name` (a label cut to 64 characters, which becomes the job name when `name` is empty), `Url` (the absolute `https` URL to call, at most 2,048 characters and publicly reachable), `Method` (`GET`, `HEAD`, `POST`, `PUT`, `PATCH` or `DELETE`, defaulting to `POST`), `Headers` (up to 20, each with `Name`, `Source`, `Value` and `Secret`) and `Body` (up to 32,000 characters). `Source` is `literal`, with the value in `Value`, or `secret`, with an environment variable name in `Secret`. A header named `Host`, `Content-Length`, `Transfer-Encoding`, `Connection`, `Keep-Alive`, `Upgrade`, `TE`, `Trailer` or `Expect`, or starting with `Proxy-`, is refused, and so is the same name twice. |
| `name` | string | No | What to call the job, cut to 64 characters. Defaults to the name of its step. |
| `enabled` | boolean | No | False to create the job switched off. Defaults to true, so it starts firing on its schedule straight away. |