# Scheduled Trigger

Run a workflow automatically on a repeating or one-time schedule.

The Scheduled trigger runs your workflow at a fixed interval or at a specific point in time — no external event needed.

## Schedule types

### Repeat

Runs the workflow repeatedly at a fixed interval. Configure in the workflow editor:

| Setting | Options |
| --- | --- |
| **Frequency** | Every N minutes, hours, days, or months |
| **Start** | The date and time of the first run (UTC) |
| **End** | Never, on a specific date, or after N runs |

Minimum interval: **10 minutes**.

### Once

Runs the workflow once at a specific date and time, then stops.

## The `data` object

For scheduled triggers, `data` contains run context:

```js
{
  counter: 1,              // How many times this workflow has run (1-indexed)
  next_schedule_time: 3600000  // Milliseconds until the next scheduled run (0 if no next run)
}
```

`counter` starts at `1` on the first run and increments on each subsequent run. Use it to differentiate early runs from later ones or to branch logic based on how many times the workflow has executed.

Your store domain and API version are always available via `env.SHOPIFY_STORE` and `env.SHOPIFY_API_VERSION`.

## Example — daily order count to Slack

A workflow that runs every day and posts how many orders were placed in the last 24 hours. `start` fetches the count from Shopify and passes it to `notifySlack`, which sends the message:

```js
export class Workflow {
  async start(data, headers, api) {
    console.log(`Daily summary — run #${data.counter}`);

    // Fetch order count for the last 24 hours
    const since = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
    const resp = await fetch(
      `https://${env.SHOPIFY_STORE}/admin/api/${env.SHOPIFY_API_VERSION}/graphql.json`,
      {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          query: `query OrdersCount($query: String!) {
            ordersCount(query: $query) {
              count
              precision
            }
          }`,
          variables: { query: `created_at:>${since}` },
        }),
      }
    );
    const { data: gqlData } = await resp.json();
    const { count, precision } = gqlData.ordersCount;
    const countLabel = precision === 'EXACT' ? `${count}` : `${count}+`;

    await api.scheduleNextStep({
      delay: 10,
      action: 'notifySlack',
      payload: { counter: data.counter, countLabel },
    });
  }

  async notifySlack({ counter, countLabel }, headers, api) {
    const { token } = await api.getOAuthToken('my-slack');
    await fetch('https://slack.com/api/chat.postMessage', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`,
      },
      body: JSON.stringify({
        channel: '#orders',
        text: `Daily summary (run #${counter}): ${countLabel} orders in the last 24 hours.`,
      }),
    });
  }
}
```

Set this workflow to run on a **Repeat** schedule of every **1 day**.

For longer workloads (e.g. processing many products), use `api.scheduleNextStep()` to spread work across steps rather than doing everything in `start`. See [Steps & Workflow Design](/getting-started/steps/) for patterns.