pipelines; this page documents routine-specific controls.
All workspace routes require an authenticated session or CLI token and
membership in {workspaceId}. Error responses use the API Problem Details
shape with an error message. An unauthenticated request is 401; a caller
outside the workspace is 403.
Governance
Each governance operation is aPOST with an empty request body. The response
is the updated routine object (200 OK). A missing or soft-deleted routine is
404; a routine in the wrong lifecycle state is 409; malformed JSON is
400; the role gate is 403.
Approval/rejection are valid only for
proposed routines. Disable cancels
in-flight runs; only active routines can run.
Standing approval grants
Await:approval gate blocks every run until a human decides. A standing
grant lets a gate the operator keeps waving through stop asking.
A grant is scoped to one gate of one routine and pinned to the routine’s
definition_hash. Editing the routine moves that hash, so no grant matches
and the gate asks again — trust never carries onto a body nobody reviewed. A
grant is never an invisible path: when it fires, an ordinary approved
waitpoint is still written, attributed to the granting operator, with the
grant id in its decision_payload.
Request notes for
POST:
step_idis required. A grant with no step would be trust in the routine as a whole, which this endpoint deliberately cannot express —400.definition_hashis the anti-TOCTOU token. The inbox card that offered the grant carries the hash it was rendered for. If the routine changed in between, the supplied hash no longer matches and the request is409rather than being silently retargeted at the new body. Omit it to accept whatever is saved now.- A second grant for the same gate and definition is
409, not a silent reset of the existing grant’s use counter. max_usesandexpires_atare both optional; absent means unbounded. Anexpires_atalready in the past is400: such a grant could never fire, and a row that reads as trust granted while changing nothing is worse than a refusal.
GET reports definition_hash at the top level — the value a
client must echo back to grant new trust — alongside each grant’s own pinned
hash. A grant whose hash differs from the current one is already inert.
DELETE is scoped to the routine in the path: grant ids are workspace-unique,
so revoking through an unrelated routine’s URL is 404 rather than a silent
cross-routine write. Both granting and revoking require an authenticated user —
the audit exists to answer who trusted this gate and who took it back, and
neither half may be NULL.
Grants are ignored entirely when the governing crew is at autonomy_level: strict.
That is the routine’s author crew — a routine executes in its author’s
context, reusing that crew’s persona and credentials — or the invoking crew when
one invokes across crews.
Schedules
See the dedicated Schedules API reference for the complete request fields, response fields, merge semantics, and status branches.
The create request accepts
target_pipeline_slug or target_pipeline_id and
requires cron_expr:
catchup_policy is once, all, or skip. A wake pipeline must be a
different routine whose DSL declares agentless: true. Invalid cron, timezone,
target, wake references, or JSON is 400; so is a preset the target recipe
rejects, or a pin to a version the routine never archived, on create, on a
PATCH that changes inputs, target, pin or enables the plan, and on activate
(see Schedules). The schedule representation
includes next_run_at, last_run_at, last_status, last_run_id, failure
breaker counters, wake telemetry, and the version a fire would use
(effective_version, null when the target is gone) with version_pinned
saying whether it comes from target_pipeline_version. A circuit-breaker
disable sets disabled_reason to circuit_breaker.
Run detail
The routines workspace reads a run’s step executions and its artifacts from two paginated endpoints, and the schedule board from a calendar endpoint. Executions and artifacts each answer in two shapes on the same path. Without a selector they return a page of rows — at most 100 executions, at most 50 artifacts — withnext_cursor set when more remain, which is passed back as
?after=. The two caps differ, so a client that treats a short page as the last
one has to know which endpoint it is reading. Name one row with
?execution_id= or ?artifact_id= and the response is that row’s payload
instead. ?download= on artifacts is the exception: it streams the stored
bytes with the artifact’s own content type, nosniff, an attachment
disposition and Cache-Control: private, no-store, so it is not a JSON body
at all.
The calendar merges three kinds of event into one list: planned occurrences
computed from schedules, pending runs already queued, and run rows that
have executed. Both planned and pending events carry a read-only inputs
preview ({} when empty); historical run events omit it. Planned presets
are not run results or a guarantee of success. Preview maps retain safe primitives;
credential, file and recognized secret values become {type: "credential"},
{type: "file"} or {type: "redacted"}. Other objects/arrays become empty
containers, preserving field counts without exposing contents. Never replay a preview as inputs. truncated is true when the window held more than the endpoint
will return, in which case narrow from/to rather than paging.
A run outside
{workspaceId} is 404, as is a named execution or artifact
that does not belong to the run. A malformed cursor, a missing or unparseable
from/to, and a calendar window outside zero to 32 days are all 400.
Webhooks
The public dispatch token is the authentication mechanism. When a signing
secret is configured, the request must also carry a valid HMAC signature.
Dispatch is asynchronous and returns
202; an inactive routine returns 409.
Editing a webhook in place (F21)
BeforePATCH existed, changing anything about a webhook meant delete +
recreate — which mints a new token and therefore a new public URL, breaking
every sender already configured against the old one. PATCH edits in place
instead; every field is optional and an absent field keeps its existing
value, the same convention PATCH .../pipeline-schedules/{scheduleId} uses:
The token — and therefore the public URL — never changes through this
endpoint, under any field combination,
rotate_secret included. Rotating
the URL still requires delete + recreate; that is the one thing this
endpoint deliberately does not do. signing_secret is present in the
response only when rotate_secret: true was sent; an ordinary edit returns
signing_secret_set: true with no secret value, matching the list/get
show-once contract.
CLI: crewship routine webhooks update <webhook_id> [--name ...] [--rate-limit N] [--inputs-template '{"...":"..."}'] [--enabled[=false]] [--rotate-secret].