---
title: "Create a schedule for an approved script on explicit profiles; a once schedule turns itself off after it runs."
method: POST
path: "/v1/schedules"
---

# Create a schedule for an approved script on explicit profiles; a once schedule turns itself off after it runs.

`POST /v1/schedules`

## Request

- Base URL: `http://127.0.0.1:10108`
- URL: `http://127.0.0.1:10108/v1/schedules`
- Auth: HTTP bearer

Creates a schedule that runs an approved script on these profiles at the given times, unattended. Every profile and the script must exist; timing that would never fire is refused.

## Request body

- object
  - `concurrency` integer
  - `cron_expr` string: 5 fields, e.g. "0 * * * *"
  - `daily_hour` integer
  - `daily_minute` integer
  - `enabled` boolean
  - `headless` boolean: Launch the run's profiles headless, without a window — the same launch as launch_profile's `headless` (see there): the profile's fingerprint is unchanged. Default false.
  - `interval_minutes` integer: interval: count of interval_unit
  - `interval_unit` 'minutes' | 'seconds'
  - `name` string, required
  - `profile_ids` string[], required
  - `run_at` string, date-time: RFC 3339 with offset, e.g. 2026-10-04T09:00:00+07:00; required for once
  - `schedule_type` 'interval' | 'daily' | 'weekly' | 'cron' | 'once', required
  - `script_id` string, uuid, required: An approved script
  - `stagger_ms` integer
  - `weekly_day` integer: 0 = Sunday

## Response `200`

The schedule as stored, with its next run.

- object
  - `concurrency` integer, required
  - `createdAt` string, date-time, required
  - `cronExpr` string, nullable, required
  - `dailyHour` integer, nullable, required
  - `dailyMinute` integer, nullable, required
  - `enabled` boolean, required
  - `headless` boolean, required
  - `id` string, uuid, required
  - `intervalMinutes` integer, nullable, required
  - `intervalUnit` 'seconds' | 'minutes', required
  - `lastRunAt` string, date-time, nullable, required
  - `name` string, required
  - `nextRunAt` string, date-time, nullable, required
  - `profileIds` string[], required
  - `runAt` string, date-time, nullable, required
  - `scheduleType` 'interval' | 'daily' | 'weekly' | 'cron' | 'once', required
  - `scriptId` string, nullable, required
  - `staggerMs` integer, required
  - `target` union, required
    - object
      - `kind` 'explicit', required
      - `value` string[], required
    - object
      - `kind` 'filter', required
      - `value` string, uuid, required
  - `updatedAt` string, date-time, required
  - `weeklyDay` integer, nullable, required

## Other responses

- `400`: Empty name or profile list, `concurrency` or `stagger_ms` out of range, or timing fields that would never fire.
- `401`: No bearer token, or not a token this app issued. The body is empty: it never says which.
- `403`: Refused by the plan: it no longer includes the local API (checked on every request), or it does not allow this action (profile or schedule limit, parallel runs). A request whose Host header is not loopback is also 403, with an empty body.
- `404`: A profile or the script does not exist.
- `409`: The script is not approved; the user approves it in Veilus Flow.
- `500`: Veilus failed: storage could not be read or written, or a task panicked. The message says what failed; retrying rarely helps.

---

[API](https://skmtc.dev/veilus/apis/veilus-local-api.md) · [All operations](https://skmtc.dev/veilus/apis/veilus-local-api/llms.txt) · [OpenAPI document](https://skmtc.dev/veilus/apis/veilus-local-api/revisions/67ac3818f9d2?raw)
