pipeline-schedules; the product UI may call these triggers routines.
All routes require an authenticated workspace member. Error responses use the
API Problem Details shape with an error message.
Endpoints
Workspace membership failures are
401 or 403. An unwired schedule store or
runner is 503; unexpected persistence failures are 500.
Preview fire times
GET .../pipeline-schedules/preview (B9, #2362) computes upcoming fire times
for a cron expression and timezone WITHOUT requiring a saved schedule — the
reliability editor calls it on every keystroke while a cron/timezone pair is
still being drafted, and it is what backs crewship routine schedules preview.
Query parameters:
Europe/Prague and is skipped) and a fall-back fold (an ambiguous
local time occurs twice, one hour apart in UTC) are both real answers, not
approximations. An invalid cron_expr or timezone, or a missing
cron_expr, returns 400.
Create and update
POST requires cron_expr and one of target_pipeline_slug or
target_pipeline_id. The target must be in the caller’s workspace. timezone
defaults to UTC; enabled defaults to true; name defaults to the target
slug when omitted or blank. target_pipeline_version pins execution to an
immutable version; omit it to follow the pipeline head.
PATCH uses merge semantics for omitted fields: omitted name, target, cron,
timezone, inputs, enabled, wake, catch-up, and breaker settings are retained.
Invalid JSON, an invalid cron or timezone, a missing target, an invalid
catchup_policy, or an invalid wake reference returns 400. A missing or
foreign schedule is 404.
The plan’s preset is judged against the recipe it will actually run. On
create, and on a PATCH that sends inputs, changes the target or the version
pin, or enables a disabled plan, the effective inputs are validated against
the target’s current definition (or the pinned version’s), and the wake
preset against the wake routine: a required input that is missing, or a value
of the wrong type or outside its choices, is 400 naming the input. Pinning
target_pipeline_version to a version the routine never archived is 400
too. The check is repeated inside the write transaction, so a recipe replaced
between preflight and commit cannot slip a bad preset through. A PATCH that
touches none of those fields — {"enabled": false}, a new cron, a rename —
is never judged, so switching a broken plan off always works. The mirror-image
gate on the recipe side (a publish, import or rollback that would strand an
enabled unpinned plan) answers 409 schedule_conflict on the
save door.
Schedule representation
Each successful list/create/update response contains an object with these fields (nullable/empty telemetry fields are omitted when unset):id, workspace_id, name, target_pipeline_id, target_pipeline_slug,
target_pipeline_version, effective_version, version_pinned, cron_expr,
timezone, inputs, enabled,
last_run_at, last_status, last_run_id, next_run_at, wake_pipeline_id,
wake_pipeline_slug, wake_inputs, wake_fail_closed, wake_check_count,
wake_fire_count, last_wake_at, last_wake_status, catchup_policy,
last_missed_count, consecutive_failures, max_consecutive_failures,
disabled_reason, activation, created_at, and updated_at.
version_pinned is true when target_pipeline_version is set.
effective_version is the version a fire would run right now — the pin when
there is one, otherwise the target routine’s current head — and null when
the target routine no longer exists. Both are always present, so a client can
say “Uses latest (now v3)” or “Pinned to v2” without a second request.
last_status can be COMPLETED, FAILED, SKIPPED, WAITING, or DEDUPED.
Wake telemetry uses WOKE, SKIPPED, ERROR, or HELD. A circuit breaker
disables the schedule after its threshold and sets disabled_reason to
circuit_breaker; operator-disabled schedules have an empty reason.
activation is "draft" for a trigger atomic routine authoring created
with activation="draft" (see Pipelines § Atomic trigger
authoring) and still
awaiting a MANAGER’s sign-off, and omitted for every ordinary schedule —
including one that is enabled: false for an operator disable or a tripped
circuit breaker. Editing a draft through PATCH cannot set enabled: true
(it answers 400, naming the .../activate route instead); only
POST .../activate may turn one on.
Force-fire, activate, and delete
POST .../pipeline-schedules/{scheduleId}/run takes an empty JSON body and
executes the stored inputs immediately without advancing the cron cadence. It
honors target_pipeline_version and returns the normal run result with 200.
It returns 404 for a missing/foreign schedule or target, 409 when a pinned
version no longer exists, and 429 with Retry-After: 5 when the pipeline’s
concurrency key is already running.
POST .../pipeline-schedules/{scheduleId}/activate takes an empty JSON body
and turns on a trigger atomic routine authoring created with
"activation": "draft" (see Pipelines § Atomic trigger authoring):
enabled becomes true, next_run_at is recomputed from the current time,
and the trigger’s approval item in the workspace inbox is resolved as
approved. Request auth is create, the same threshold as an ordinary
schedule create. Activation re-validates the draft’s stored inputs against
the recipe it would run at that moment (the routine may have been published
since the draft was created); a preset the recipe now rejects, or a recipe or
pinned version that no longer exists, is 400 and the draft stays off. A
schedule that was never created as a draft, or was already activated, answers
409; a missing or foreign schedule is 404. Re-enabling an ordinary
disabled plan through PATCH {"enabled": true} runs the same re-validation.
DELETE is a soft delete: the row is retained, future cron ticks stop, and an
already-running invocation is not cancelled. The successful response is empty
(204). A missing or foreign schedule is 404. Deleting a schedule still
awaiting activation also resolves its approval item (as dismissed).