# Function Schedulers API

Manage scheduled invocations that run a function on a recurring cron schedule.

Function schedulers are managed below a function:

- `GET /projects/:id/functions/:functionId/schedulers`
- `POST /projects/:id/functions/:functionId/schedulers`
- `GET /projects/:id/functions/:functionId/schedulers/:schedulerId`
- `PATCH /projects/:id/functions/:functionId/schedulers/:schedulerId`
- `DELETE /projects/:id/functions/:functionId/schedulers/:schedulerId`

[Durable functions](https://docs.volcano.dev/platform/functions/durable-functions.md) take schedulers on the same
routes below `/projects/:id/durable-functions/:functionId`, and a tick starts an
execution instead of invoking the function.

`GET /projects/:id/schedulers` lists every scheduler in the project, both kinds
together. Each one carries `function_kind`, which is `standard` or `durable`, and
tells you which collection to read the function back from.

Pass `function_kind` to list one kind only. Pagination totals and cursors follow
the filter, and a cursor from one kind is rejected for the other:

```bash
curl "$VOLCANO_API_URL/projects/$PROJECT_ID/schedulers?function_kind=durable&limit=20" \
  -H "Authorization: Bearer $VOLCANO_TOKEN"
```

```json
{
  "data": [
    {
      "id": "2c9d1e0f-3a4b-4c5d-8e9f-0a1b2c3d4e5f",
      "function_id": "6f1c0f6e-6b0e-4a1d-9f8a-2c3d4e5f6a7b",
      "function_kind": "durable",
      "name": "nightly-reconcile",
      "enabled": true,
      "cron_expression": "0 3 * * *"
    }
  ],
  "limit": 20,
  "total": 1,
  "has_more": false
}
```

Any value other than `standard` or `durable` answers `400`.

Create request:

```json
{
  "name": "refresh-cache",
  "enabled": true,
  "schedule": {
    "kind": "cron",
    "cron_expression": "*/5 * * * *"
  },
  "payload": {
    "source": "scheduled"
  },
  "regions": ["us-east-1"]
}
```

`regions` is optional. If omitted, Volcano randomly chooses one deployed region and invokes the function in that region according to the cron schedule. The chosen region remains stable while it is still deployed; if geofencing removes it, Volcano randomly chooses one of the remaining deployed regions. Explicit regions must contain exactly one deployed region. Function schedulers never fan out to every deployed region; the per-region coverage Volcano runs to [keep runtimes ready](https://docs.volcano.dev/platform/functions/overview.md#response-time-on-the-first-invocation) is its own and is not a scheduler.

`schedule.cron_expression` must be a standard 5-field cron expression evaluated in UTC. Seconds fields, descriptors such as `@daily`, and Quartz-only syntax such as `?`, `L`, `W`, or `#` are not supported. Volcano validates that the schedule runs no more frequently than once per minute and at least once every 31 days.

At runtime, the function receives the configured `payload` as its event. Volcano adds `__volcano_schedule` metadata to the event with scheduler/run identifiers and the region that performed the invocation.

## Health

Every scheduler response reports how its recent runs went:

```json
{
  "id": "2c9d1e0f-3a4b-4c5d-8e9f-0a1b2c3d4e5f",
  "name": "hourly-sync",
  "enabled": true,
  "run_count": 52,
  "health": "failing",
  "consecutive_failures": 49,
  "last_run_status": "failed",
  "last_error": "TypeError: Cannot read properties of undefined (reading 'rows')",
  "last_success_at": "2026-10-17T13:00:05Z",
  "last_failure_at": "2026-10-19T14:00:04Z"
}
```

| Field                                | Description                                                                                                                                                                                                                                              |
| ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `health`                             | `failing` while `consecutive_failures` is above `0`, otherwise `healthy`, including before the first run.                                                                                                                                                |
| `consecutive_failures`               | Runs failed in a row. Resets to `0` when a run succeeds.                                                                                                                                                                                                 |
| `last_run_status`                    | `succeeded` or `failed`. Absent before the first finished run.                                                                                                                                                                                           |
| `last_error`                         | What the most recent counted failed run reported. Cleared when a run succeeds. A failure that does not count, such as a tick Volcano is still retrying in its first 15 minutes, leaves this and `health` unchanged and is not reported on the scheduler. |
| `last_success_at`, `last_failure_at` | When the most recent run of each outcome finished. Absent until there is one.                                                                                                                                                                            |

A durable function's run finishes when the execution it started does, so its
health follows the execution's outcome: failed, timed out, or stopped counts as
a failure. A standard function's run fails only when Volcano cannot invoke it;
an error the handler returns does not count. A disabled scheduler keeps the
health it had, apart from durable runs already started: they still count when
their executions finish, and can still send an alert.

`health` and `consecutive_failures` describe the region the scheduler runs in
now. `last_run_status` and `last_error` describe the last run to finish in any
region. After you move a scheduler to another region, they can disagree until
it first runs there: a failed run in the old region is still the last run, but
the new region starts `healthy`.

To be told when a scheduler starts failing rather than polling for it, see
[Notifications](https://docs.volcano.dev/platform/projects/notifications.md).
