# Run services on a schedule

Run services on an interval, at a specific time or on a cron expression.

The scheduler invokes your services on an interval, at a specific time or on a cron expression. Jobs are created in the Dashboard or from Python and no service code changes are needed for a service to become a scheduled one.

For a step-by-step introduction, see the [Python scheduler tutorial](https://zato.io/tutorials/python-scheduler.html).

## Invoke services on an interval {#invoke-services-on-an-interval}

To create a job, go to **Scheduler** > **Config** in the Dashboard, click **Create a new job** and fill in the form:

1. **Name:** Nightly Sync
2. **Service:** the service the scheduler invokes on each run
3. **Every:** how often to run it, e.g. every 30 minutes
4. Click **OK**

![New interval-based job](https://zatosource-production.b-cdn.net/docs/gfx/scheduler/job-create.webp?v=1791135894)

Dashboard menu: Scheduler > Config

The service is invoked each time the job fires, and any extra data provided in the job's definition is available to the service as text in `self.request.input`:

```python
# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class MyService(Service):

    def handle(self):
        self.logger.info('Message received: %s', self.request.input)
```

## Schedule one-time jobs in Python {#schedule-one-time-jobs-in-python}

To run a service once, at a specific time, call `self.schedule.onetime`. The example below schedules another service for a specific moment - the prefix parameter makes the job easy to find in the Dashboard and logs:

```python
# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class MyTarget(Service):

    def handle(self):
        self.logger.warn('I was invoked with %s', self.request.input)

class Scheduler(Service):

    def handle(self):

        # This can be used to make it easier to understand which scheduled job is which
        prefix = 'user@example.com'

        # This is what the target service is going to receive on input
        data = {
            'my.key': 'my.value'
        }

        # This is when the job should start, note that the time must be in UTC
        start_date = '2027-07-29 11:35:49'

        # This is the call that schedules the job
        self.schedule.onetime(self, MyTarget, prefix=prefix, start_date=start_date, data=data)
```

## Schedule interval-based and cron-style jobs in Python {#schedule-interval-based-and-cron-style-jobs-in-python}

To create interval-based or cron-style jobs from code, invoke the built-in `zato.scheduler.job.create` service:

```python
# stdlib
from datetime import datetime

# Zato
from zato.server.service import Service

class MyService(Service):

    def handle(self):

        #
        # We schedule a cron-style job here that will run once per hour (@hourly)
        #

        request = {
            'name': 'my-job-1',
            'is_active': True,
            'service': 'zato.ping',
            'job_type': 'cron_style',
            'start_date': datetime.utcnow().isoformat(), # Always in UTC
            'cron_definition': '@hourly'
        }

        self.invoke('zato.scheduler.job.create', request)
```

> **Note:** An interval-based job counts its runs from its start date - the first run is at the start date, the next one an interval later, and so on. With a start date in the past, the runs are still counted from it, and the first one to happen is the next one that has not passed yet. When a server restarts, each job resumes with the first run that falls after the server is back. Runs that fell into the time the server was down are not repeated - if the server was down from 02:00 to 05:00, the 03:00 run does not happen, the next run is at 03:00 the following day, and the time of the run stays at 03:00 rather than moving to 05:00. There is never a burst of catch-up runs after a restart.

## See also {#see-also}

- [IMAP](https://zato.io/docs/dev/examples/imap.html) - Poll mailboxes on a schedule with an auto-created job
- [Config files](https://zato.io/docs/dev/examples/config.html) - Keep a job's settings outside code
- [SMTP](https://zato.io/docs/dev/examples/smtp.html) - Send email from scheduled services

## Learn more {#learn-more}

- [Development documentation](https://zato.io/docs/dev/) - The complete reference for writing services
- [Requests and responses](https://zato.io/docs/dev/request-response/) - What a service receives, what it returns and how both are structured
- [Integration examples](https://zato.io/docs/dev/examples/) - Ready-made code for the systems you are likely to connect to
- [IDE and debugging](https://zato.io/docs/dev/ide/) - Write services in the Dashboard or in your own editor
- [Data models](https://zato.io/docs/dev/model/) - Declare inputs and outputs and have them validated for you
- [In-depth API tutorial](https://zato.io/tutorials/main/01.html) - The full platform tutorial, from installation to production patterns
