Skip to content
DocsAll docs
Console

Pipelines API

Lists the API routes that save pipeline definitions, start runs and read their status and logs.

Last updated

On this page

The run document

Every run route returns this object. An optional field with no value is null.

json
{  "id": "1760400000000-0a1b2c",  "number": 14,  "pipeline": "test",  "owner": "acme",  "pipelineVersion": 3,  "definitionSha256": "5e2b8c0d4f6a1e3b7c9d0f2a4b6c8e0a1c3e5f7b9d1f3a5c7e9b0d2f4a6c8e0a",  "event": "manual",  "inputs": { "target": "staging" },  "environment": { "clone": "staging" },  "requestedBy": "karthik",  "requestedAtMs": 1760400000000,  "state": "running",  "cancelRequested": false,  "claimedBy": "worker-3f2a",  "leaseExpiresMs": 1760400124000,  "startedAtMs": 1760400005000,  "finishedAtMs": null,  "detail": null,  "jobs": [    {      "key": "test (postgres16)",      "job": "test",      "matrix": { "db": "postgres16" },      "needs": [],      "state": "running",      "sandbox": "sbx-5a6b7c8d9e0f1a2b",      "startedAtMs": 1760400006000,      "finishedAtMs": null,      "exitCode": null,      "failedStep": null,      "detail": null,      "home": { "volume": "ws-0a1b2c3d4e5f6a7b", "name": "go-base", "version": 3, "snapshot": "ws-0a1b2c3d4e5f6a7b-8a7b6c91" }    }  ],  "after": null,  "triggered": [],  "definition": null}
FieldTypeMeaning
idstring13 digits of creation time in milliseconds, a dash and 6 hex digits. Sorts by creation time
numberintegerper owner and pipeline, from 1
pipelinestringthe definition's name
ownerstringthe person or team the run acts as
pipelineVersionintegerthe version the run was planned from. A version cannot change, so the run always does what that version says
definitionSha256stringthe SHA-256 of that version's definition
eventstringmanual, schedule, webhook or pipeline
inputsobjectthe resolved inputs: the value you gave, else the default
environmentstring, object or nullas the request gave it: an environment, or {"clone": "name"}. null means the pipeline's own default
requestedBystringthe handle that asked, or the event's actor
requestedAtMsintegermilliseconds since the epoch
statestringqueued, running, success, failure or cancelled. The last three are final
cancelRequestedbooleana cancel was asked; a running run stops at its next report
claimedBy, leaseExpiresMsstring or integer, nullwhich worker holds the run and when its claim lapses. Bookkeeping; every report renews the claim for 120 seconds
startedAtMs, finishedAtMsinteger or nullmilliseconds since the epoch
detailstring or nullwhy it stopped, when it did not succeed
jobsobject[]the planned jobs, fixed when the run was created
afterobject or nullfor event pipeline: the pipeline and run whose end started this run, and the depth of the chain
triggeredobject[]the runs that this run's end started: pipeline, and run or the error that stopped it
definitionobject or nullthe version's definition. Only reading one run returns it, and only while the version exists. A list returns null

A job is:

FieldTypeMeaning
keystringunique within the run: test (postgres16) for one matrix instance of test
jobstringthe name in the definition. Other jobs' needs name this
matrixobjectthis instance's matrix values; {} without a matrix
needsstring[]the names of the jobs that must end first; a matrix job counts once for all its instances
statestringpending, running, success, failure, skipped or cancelled
sandboxstring or nullthe sandbox id that ran or is running it
startedAtMs, finishedAtMsinteger or nullmilliseconds since the epoch
exitCodeinteger or nullthe exit code the job ended with
failedStepstring or nullthe step that failed
detailstring or nulla sentence about why the job stopped
homeobjectabsent for an empty home. What the job started from: volume (the volume id), name and version when the volume is named and the snapshot is a numbered version, and snapshot
savedobjectwhat the job saved: volume, name, version and snapshot. Absent until the save is ready, and for a job that does not save

The index in the log route is the position of a job in jobs.

The pipeline document

The definition routes return this object. A new version is saved only when the definition changes.

json
{  "owner": "acme",  "name": "test",  "version": 2,  "latestVersion": 2,  "createdBy": "karthik",  "createdAtMs": 1760400000000,  "definitionSha256": "9f2c0b7e5a41d3c8a6b05e7f1d2c4a3b8e9f0a1b2c3d4e5f60718293a4b5c6d7",  "definition": { "name": "test", "jobs": { "test": { "steps": [{ "run": "make test" }] } } },  "triggers": { "schedule": [], "webhook": false },  "webhook": { "set": false }}
FieldTypeMeaning
version, latestVersioninteger, integerthe version shown, and the newest one
definitionSha256stringthe hash of the canonical definition
definitionobjectthe definition as saved
triggersobjectschedule, webhook and after of the version shown
webhook.setbooleanwhether the pipeline has a webhook secret
webhookUrlstringthe webhook URL with its secret. Only in the answer of the save that made the secret; no later answer shows it

Volume checks

A save and a run both check saveTo and fromSnapshot. Each refusal is one line, prefixed with job {key}: .

  • saveTo {name}: …: the name breaks the rule, or is reserved.
  • saveTo is a literal volume name: saveTo has ${{ }}.
  • saveTo cannot be used with matrix.
  • fromSnapshot and saveTo name different volumes ({a}, {b}).
  • volume {name} does not exist and no job before it saves to it.
  • volume {name} has no version {n}.
  • {name} is an environment volume; a job starts only from a workspace volume.
  • {id} is not a push: a sync point is not a start point.
  • jobs {a} and {b} both save to {name} and neither needs the other (without the job prefix).

A fromSnapshot with ${{ }} is checked at run creation, after the inputs are filled in.

POST /v1/pipelines/{owner}

Creates a pipeline. The body is the definition, and its name is the pipeline's name. Response: 201, the pipeline document, version 1.

Only you, or an admin of the team, can write. A team member gets 403 only a team admin can change team pipelines.

Errors:

  • 409 — {owner} has 50 pipelines; delete one first; pipeline {name} exists.
  • 422 — pipeline {name} is invalid: and one line per error. The checks include that every referenced secret exists, that triggers.after names an existing pipeline, and the volume checks.
  • 503 — a secret or pipeline listing failed.

PUT /v1/pipelines/{owner}/{name}

Replaces the definition. The name in the body must equal {name}. A definition equal to the latest version saves nothing and returns the latest. Any other saves the next version. To roll back, send an old definition: it becomes a new version.

Response: 200, the pipeline document.

Errors:

  • 404 — the pipeline does not exist.
  • 409 — pipeline {name} changed; retry; pipeline {name} has 100 versions; delete an old version.
  • 422 — as for create, or the names differ.

When the latest version sets triggers.webhook to true and the pipeline has no secret, the save makes one and returns webhookUrl once. A version that sets triggers.webhook to false deletes the secret.

POST /v1/pipelines/{owner}/{name}/webhook/rotate

Makes a new webhook secret. The old URL stops at once. Only you, or an admin of the team, can rotate. Response: 200.

json
{ "webhookUrl": "https://kloudlite.io/v1/webhooks/pipelines/acme/test/Q2hhbmdlTWUtdGhpcy1pcy1hbi1leGFtcGxlLW9ubHk" }

Keep the URL secret. No later answer shows it.

Errors:

  • 403 — only a team admin can change team pipelines.
  • 404 — the pipeline does not exist.
  • 409 — the webhook is off; turn it on in a new version.

POST /v1/webhooks/pipelines/{owner}/{name}/{secret}

Starts a run of the newest version with event webhook and every input at its default. The call needs no token: the secret in the URL is the credential. Anyone with the URL can start a run, and that run gets the pipeline's secrets.

The body is optional. A JSON body of at most 256 KiB is webhook.body in env and if. Any other body runs with no webhook.body. Query parameters are ignored.

Response: 202.

json
{ "id": "1760400000000-0a1b2c" }

Errors:

  • 404, with no body — the owner, the pipeline or the secret is wrong, or the webhook is off. The answer is the same for each.
  • 413 — the body is larger than 256 KiB.
  • 422 — pipeline {name} cannot run: and one line per error.
  • 429, with no body — the pipeline had 30 webhook calls in this minute (UTC).
  • 503 — webhooks need an object store.

GET /v1/pipelines/{owner}

Lists the pipelines of an owner, sorted by name. Response: 200.

json
[{ "name": "test", "latestVersion": 2, "updatedAtMs": 1760400000000, "triggers": { "schedule": [], "webhook": false } }]

GET /v1/pipelines/{owner}/{name}

Reads a pipeline. Query ?version=N reads version N; absent means the latest. Response: 200, the pipeline document.

Errors:

  • 404 — the pipeline does not exist, or version {n} of {name} is gone.

GET /v1/pipelines/{owner}/{name}/versions

Lists the versions of a pipeline, oldest first. Response: 200, an array of { version, createdBy, createdAtMs, definitionSha256 }.

DELETE /v1/pipelines/{owner}/{name}

Deletes a pipeline and all its versions. Finished runs stay, with their records. Response: 204.

Errors:

  • 409 — pipeline {name} has a queued or running run.

DELETE /v1/pipelines/{owner}/{name}/versions/{n}

Deletes one version. Response: 204. A finished run of a deleted version still reads, with definition: null.

Errors:

  • 404 — version {n} of {name} is gone.
  • 409 — version {n} is the latest; it cannot be deleted; version {n} is used by run {id}, for a queued or running run.

POST /v1/pipelines/{owner}/{pipeline}/runs

Starts a run by hand. The API plans the run, not a worker. It reads one stored version and checks the inputs and the secrets. It expands the jobs and records the plan before the call returns. A version that cannot be planned is refused with every reason at once and nothing is recorded. Any active member of the owner can start a run.

FieldTypeRequiredRule
versionintegernoThe version to run. Default: the newest version when the call arrives
inputsobjectnoString values, keyed by input name. Only inputs the pipeline declares. An input with a default can be left out
environmentstring or objectnoAn environment, or {"clone": "name"} for a per-run copy. Overrides the environment of every job that does not name its own. Must not be empty
json
{ "version": 3, "inputs": { "target": "staging" }, "environment": { "clone": "staging" } }

Unknown fields are refused, repo and ref included. A job gets code with git clone in a step, see Code in a job.

Response: 202, the run document in queued.

json
{  "id": "1760400000000-0a1b2c",  "number": 14,  "pipeline": "test",  "owner": "acme",  "pipelineVersion": 3,  "definitionSha256": "5e2b8c0d4f6a1e3b7c9d0f2a4b6c8e0a1c3e5f7b9d1f3a5c7e9b0d2f4a6c8e0a",  "event": "manual",  "inputs": { "target": "staging" },  "environment": { "clone": "staging" },  "requestedBy": "karthik",  "requestedAtMs": 1760400000000,  "state": "queued",  "cancelRequested": false,  "claimedBy": null,  "leaseExpiresMs": null,  "startedAtMs": null,  "finishedAtMs": null,  "detail": null,  "jobs": [    { "key": "lint", "job": "lint", "matrix": {}, "needs": [], "state": "pending", "sandbox": null, "startedAtMs": null, "finishedAtMs": null, "exitCode": null, "failedStep": null, "detail": null }  ],  "after": null,  "triggered": [],  "definition": null}

Errors:

  • 400 — environment is empty.
  • 403 — your access to {owner} is paused; you are no longer a member of {owner}.
  • 404 — the owner is not yours; there is no such pipeline; version {n} of {pipeline} is gone.
  • 422 — an unknown field; or pipeline {pipeline} cannot run: and one line per error. The lines include unknown input (naming the declared ones), input … is required, input …: … is not one of …, and secret {name} does not exist, and the volume checks.
  • 503 — the secrets cannot be read right now.

GET /v1/pipelines/{owner}/{pipeline}/runs

Lists the runs of one pipeline, newest first.

QueryMeaning
limithow many. Default 50, at most 200; a larger number is clamped

Response: 200, an array of run documents.

Errors:

  • 404 — the owner is not yours, or the pipeline name is malformed. A limit that is not a number reads as the default.

GET /v1/pipelines/{owner}/{pipeline}/runs/{id}

Reads one run. Response: 200, the run document, with definition set to the definition of pipelineVersion while that version exists. After the version is deleted the run still reads, with definition null.

Errors:

  • 404 — the owner is not yours; the run belongs to another pipeline; {id} is not shaped like a run id; there is no such run.

POST /v1/pipelines/{owner}/{pipeline}/runs/{id}/cancel

Asks a run to stop. A queued run is cancelled at once, with its pending jobs. A running run is flagged, and its worker stops every job at its next report. Takes no body.

Response: 200, the run document; a queued run is already cancelled, a running one has cancelRequested true and is still running until the worker stops it.

Errors:

  • 404 — as for reading a run.
  • 409 — this run has already finished.

GET /v1/pipelines/{owner}/{pipeline}/runs/{id}/jobs/{index}/logs

Reads the combined output of one job from a byte offset. A running job is followed by calling again with next.

QueryMeaning
offsetthe byte to start from. Default 0, and so is a value that is not a number; one past the end reads from the end

Response: 200. A page is at most 1 MiB and never ends inside a character.

json
{ "data": "Run lint\n$ cargo clippy\n", "next": 24, "complete": false, "state": "running" }
FieldTypeMeaning
datastringthe output from offset
nextintegerthe offset to ask for next
completebooleantrue once the job has finished and everything it wrote was returned
statestringthe job's state

Job logs are kept for 90 days. After that a finished job's log is the single line log expired: job logs are kept for 90 days, with complete true. Before the job writes anything the log is empty, not an error.

Errors:

  • 404 — as for reading a run, or {index} is not a job of the run.
  • 502 — the log could not be read.
  • 503 — logs are unavailable: no object store.