> ## Documentation Index
> Fetch the complete documentation index at: https://iii.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Cron

> Schedule functions with cron expressions.

Schedule functions to execute at specific times using cron expressions.

```
iii-cron
```

## Sample Configuration

```yaml theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
- name: iii-cron
  config:
    adapter:
      name: redis
      config:
        redis_url: ${REDIS_URL:redis://localhost:6379}
```

## Configuration

<ResponseField name="adapter" type="Adapter">
  The adapter to use for distributed locking. Defaults to `kv`. Use `redis` for multi-instance deployments.
</ResponseField>

## Adapters

### kv

<Warning>
  When running multiple engine instances, the `kv` adapter does not provide reliable distributed locking — the same cron job may execute on every instance simultaneously. Use the `redis` adapter for multi-instance deployments.
</Warning>

Built-in adapter using process-local locks. Suitable for single-instance deployments.

```yaml theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
name: kv
config:
  lock_ttl_ms: 30000
  lock_index: cron_locks
```

#### Configuration

<ResponseField name="lock_ttl_ms" type="integer">
  Duration in milliseconds for which a lock is held before it expires. Defaults to `30000` (30 seconds).
</ResponseField>

<ResponseField name="lock_index" type="string">
  Key namespace used to store lock entries in the KV store. Defaults to `cron_locks`.
</ResponseField>

### redis

Uses Redis for distributed locking to prevent duplicate job execution across multiple engine instances.

```yaml theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
name: redis
config:
  redis_url: ${REDIS_URL:redis://localhost:6379}
```

#### Configuration

<ResponseField name="redis_url" type="string">
  The URL of the Redis instance to use for distributed locking.
</ResponseField>

## Trigger Type

This Worker adds a new Trigger Type: `cron`.

<Expandable title="Trigger Config">
  <ResponseField name="expression" type="string" required>
    Cron expression defining the schedule. Accepts 6-field (`second minute hour day month weekday`) or 7-field (`second minute hour day month weekday year`) format — the year field is optional.

    ```
    * * * * * * [*]
    │ │ │ │ │ │  │
    │ │ │ │ │ │  └── Year (optional, * for any)
    │ │ │ │ │ └──── Day of week (0–7, Sun=0 or 7)
    │ │ │ │ └────── Month (1–12)
    │ │ │ └──────── Day of month (1–31)
    │ │ └────────── Hour (0–23)
    │ └──────────── Minute (0–59)
    └──────────── Second (0–59)
    ```

    Both `"0 0 * * * *"` (6-field) and `"0 0 * * * * *"` (7-field) are valid and equivalent.
  </ResponseField>

  <ResponseField name="condition_function_id" type="string">
    Function ID for conditional execution. The engine invokes it with the cron event; if it returns `false`, the handler function is not called.
  </ResponseField>
</Expandable>

<Expandable title="Trigger Event Payload">
  The following fields are passed to the handler function (and to `condition_function_id` if set) each time the trigger fires.

  <ResponseField name="trigger" type="string">
    Always `"cron"`.
  </ResponseField>

  <ResponseField name="job_id" type="string">
    The ID of the cron trigger that fired.
  </ResponseField>

  <ResponseField name="scheduled_time" type="string">
    The time the job was scheduled to run, in RFC 3339 format.
  </ResponseField>

  <ResponseField name="actual_time" type="string">
    The actual time the job began executing, in RFC 3339 format.
  </ResponseField>
</Expandable>

### Sample Code

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
    const fn = iii.registerFunction(
      { id: 'jobs::cleanupOldData' },
      async (event) => {
        console.log('Running cleanup scheduled at:', event.scheduled_time)
        return {}
      },
    )

    iii.registerTrigger({
      type: 'cron',
      function_id: fn.id,
      config: { expression: '0 0 2 * * * *' },
    })
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
    def cleanup_old_data(event):
        print('Running cleanup scheduled at:', event['scheduled_time'])
        return {}

    iii.register_function("jobs::cleanupOldData", cleanup_old_data)
    iii.register_trigger({'type': 'cron', 'function_id': 'jobs::cleanupOldData', 'config': {'expression': '0 0 2 * * *'}})
    ```
  </Tab>

  <Tab title="Rust">
    ```rust theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
    iii.register_function(
        RegisterFunctionMessage::with_id("jobs::cleanupOldData".into()),
        |event: Value| async move {
            println!("Running cleanup scheduled at: {}", event["scheduled_time"]);
            Ok(json!({}))
        },
    );

    iii.register_trigger(RegisterTriggerInput {
        trigger_type: "cron".into(),
        function_id: "jobs::cleanupOldData".into(),
        config: json!({ "expression": "0 0 2 * * * *" }),
        metadata: None,
    })?;
    ```
  </Tab>
</Tabs>

## Common Cron Expressions

| Expression           | Description                                    |
| -------------------- | ---------------------------------------------- |
| `0 * * * * *`        | Every minute (6-field)                         |
| `0 0 * * * *`        | Every hour (6-field)                           |
| `0 0 2 * * *`        | Every day at 2 AM (6-field)                    |
| `0 * * * * * *`      | Every minute (7-field)                         |
| `0 0 * * * * *`      | Every hour (7-field)                           |
| `0 0 0 * * * *`      | Every day at midnight                          |
| `0 0 0 * * 0 *`      | Every Sunday at midnight                       |
| `0 */5 * * * * *`    | Every 5 minutes                                |
| `0 0 9-17 * * 1-5 *` | Every hour from 9 AM to 5 PM, Monday to Friday |

## Distributed Execution

When running multiple iii Engine instances, the Cron Worker uses distributed locking to ensure jobs execute only once:

```mermaid theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
sequenceDiagram
    participant E1 as Engine1
    participant R as Redis
    participant E2 as Engine2

    Note over E1,E2: Cron trigger fires at<br/>scheduled time

    E1->>R: Try acquire lock
    E2->>R: Try acquire lock
    R-->>E1: Lock acquired
    R-->>E2: Lock unavailable

    E1->>E1: Execute job
    E1->>R: Release lock

    Note over E2: Skip execution<br/>(lock not acquired)
```
