Skip to content
DocsAll docs
Console

Triggers and inputs

Start runs on a schedule, from a webhook call or after another pipeline, and give a run its inputs.

Last updated

On this page

Description and operation

General

A trigger is a way to start a run. You can always start a run with the SDK, the CLI or the API. The triggers key of a definition adds three more ways. A run from a trigger uses the newest version at the time that it starts.

StartKeyThe run has eventInputs
A callnonemanualThe values of the call, else the defaults
A scheduletriggers.schedulescheduleThe defaults
A webhook calltriggers.webhookwebhookThe defaults, plus the data of the call in webhook.body
The end of another pipelinetriggers.afterpipelineThe defaults

To turn a trigger off, remove its key and save the definition as a new version.

To do these tasks with an AI agent, see Agentic workflows – General.

Inputs

inputs declares the values that a run takes. Every value is text. A job reads an input as ${{ inputs.NAME }}.

KeyMeaning
defaultThe value when the start gives none.
optionsThe only values that a start can give. The default must be one of them.
requiredA start that gives no value, with no default, is refused.

An input name starts with a letter or an underscore, and has at most 40 letters, digits, underscores or hyphens. An input with options needs a default or required, because an empty value is not one of the options.

A start can give only declared inputs. A trigger gives no values, so every trigger run uses the defaults. For that reason, a definition with a trigger cannot have a required input with no default.

Schedule

triggers.schedule is a list of cron lines in UTC, with five fields: minute hour day-of-month month weekday. Each line that matches a minute starts one run in that minute. A minute in which the platform does not run is skipped, not run later.

When a line restricts both day-of-month and weekday, a minute matches if either field matches. Month names, weekday names and @daily are not accepted. Weekday 0 and 7 are Sunday. The full syntax is in Cron.

LineStarts a run
0 2 * * *At 02:00 UTC every day
*/15 * * * *Every 15 minutes
30 6 * * 1-5At 06:30 UTC from Monday to Friday
0 0 1 * *At 00:00 UTC on the first day of each month

Webhook

"webhook": true gives the pipeline a secret URL. A POST to the URL starts a run, with no token. The answer of the save that turns the webhook on shows the URL once. No later answer shows it, so store it as a secret of the calling system. A rotate makes a new URL and stops the old one at once. "webhook": false deletes the secret.

ItemRule
BodyOptional. A JSON body of at most 256 KiB is available as webhook.body. Any other body starts a run with no data.
Rate30 calls in one minute (UTC) for each pipeline. The next call answers 429. Each api replica counts its own calls, so the total can be higher.
Answer202 with the id of the run.
Wrong URL404 with no body, the same for a wrong secret, a wrong pipeline and a webhook that is off. A body over 256 KiB answers 413 before the URL is checked.
WARNING

Anyone with the URL can start a run, and that run gets the secrets of the pipeline. Keep the URL secret, and rotate it when it is lost or shared.

Read webhook data safely

The caller of a webhook controls the text of webhook.body. A job can read it only in env and in if. It cannot read it in run, cwd, image or packages. Put a value in a variable, then use the variable in quotes in the command.

DefinitionResult
"env": {"PAGE": "${{ webhook.body.page }}"} and "run": "./reindex.sh \"$PAGE\""Accepted. The shell reads the value as data.
"run": "./reindex.sh ${{ webhook.body.page }}"Refused at save. The text of the caller would become part of the command.
"run": "./reindex.sh ${{ env.PAGE }}", where PAGE holds webhook.body dataRefused at save, for the same reason.

A field that is not in the body reads as the empty text. A field that is a number, an object or a list reads as its JSON text. Use dots for nested fields, for example webhook.body.repo.name.

After another pipeline

triggers.after starts a run when a run of another pipeline of the same owner ends. With result (success, failure or cancelled), only that end state starts a run. Without result, any end starts a run.

A pipeline cannot follow itself, and the pipeline that it follows must exist when you save. A chain of after triggers stops at a depth of 5. The finished run lists in triggered each run that it started, and the error of each start that was refused.

Tasks

Give a run its inputs

Reason for the task: Make one pipeline do different work for each run, for example on another data set.

Job set-up

  • Access: you. For a team, an admin to change the definition, and an active member to start the run.
  • References: Inputs, SDK pipelines.run.

Procedure

  1. Add the inputs to the definition, and read them in env.

    json
    {  "name": "refresh",  "inputs": {    "dataset": { "options": ["orders", "customers"], "default": "orders" },    "day": { "required": true }  },  "jobs": {    "load": {      "steps": [        {          "name": "load",          "run": "echo \"loading $DATASET for $DAY\"",          "env": { "DATASET": "${{ inputs.dataset }}", "DAY": "${{ inputs.day }}" }        }      ]    }  }}
  2. Create the pipeline from the file. See Create a pipeline.

  3. Start a run with a value for each input.

    Python
    run = kl.pipelines.run(owner="acme", pipeline="refresh", inputs={"dataset": "customers", "day": "2026-10-21"})print(run.inputs)
    TypeScript
    const run = await kl.pipelines.run({ owner: "acme", pipeline: "refresh", inputs: { dataset: "customers", day: "2026-10-21" } });console.log(run.doc.inputs);
    Go
    run, err := kl.Pipelines.Run(kloudlite.RunPipeline{    Owner: "acme", Pipeline: "refresh",    Inputs: map[string]string{"dataset": "customers", "day": "2026-10-21"},})if err != nil {    log.Fatal(err)}fmt.Println(run.Inputs)
    Ruby
    run = kl.pipelines.run(owner: "acme", pipeline: "refresh", inputs: { "dataset" => "customers", "day" => "2026-10-21" })p run.inputs
    Java
    var run = kl.pipelines().run(new Kloudlite.RunPipeline().owner("acme").pipeline("refresh")    .input("dataset", "customers").input("day", "2026-10-21"));System.out.println(run.json());
    CLI
    kloudlite pipelines run acme/refresh --input dataset=customers --input day=2026-10-21

Close-out

  1. Make sure that inputs of the run shows both values.

Run a pipeline on a schedule

Reason for the task: Do recurring work, such as a nightly data refresh, with no person to start it.

Job set-up

  • Access: you. For a team, an admin.
  • Conditions: every input of the pipeline has a default, or is not required.
  • References: Schedule, Cron.

Procedure

  1. Add triggers.schedule to the definition. This line starts a run at 02:00 UTC every day.

    json
    {  "name": "nightly-report",  "triggers": { "schedule": ["0 2 * * *"] },  "jobs": {    "fetch": {      "packages": ["curl"],      "steps": [{ "name": "download", "run": "curl -fsSL https://api.github.com/repos/nixos/nixpkgs -o data.json" }]    }  }}
  2. Send the definition as the next version. See Save a new version.

Close-out

  1. Read the pipeline. Make sure that triggers.schedule lists the line.
  2. Wait until after 02:00 UTC, then show the runs of the pipeline. Make sure that the newest run has event schedule.

Start a run from a webhook

Reason for the task: Let another system start a run with one HTTP call, for example when its content changes.

Job set-up

Procedure

  1. Set triggers.webhook to true, and read the data of the call in env.

    json
    {  "name": "reindex",  "triggers": { "webhook": true },  "jobs": {    "index": {      "if": "webhook.body.page != ''",      "steps": [        { "name": "reindex", "run": "echo \"reindexing page $PAGE\"", "env": { "PAGE": "${{ webhook.body.page }}" } }      ]    }  }}
  2. Create the pipeline, or send the definition as a new version. Read webhookUrl in the answer.

    Python
    with open("reindex.json") as f:    doc = kl.pipelines.create(owner="acme", definition=json.load(f))print(doc["webhookUrl"])
    TypeScript
    const doc = await kl.pipelines.create({ owner: "acme", definition: JSON.parse(readFileSync("reindex.json", "utf8")) });console.log(doc.webhookUrl);
    Go
    raw, err := os.ReadFile("reindex.json")if err != nil {    log.Fatal(err)}var d kloudlite.PipelineDefinitionif err := json.Unmarshal(raw, &d); err != nil {    log.Fatal(err)}doc, err := kl.Pipelines.Create("acme", d)if err != nil {    log.Fatal(err)}fmt.Println(doc.WebhookURL)
    Ruby
    doc = kl.pipelines.create(owner: "acme", definition: JSON.parse(File.read("reindex.json")))puts doc["webhookUrl"]
    Java
    System.out.println(kl.request(new Kloudlite.RawRequest().method("POST").path("/v1/pipelines/acme")    .body(Files.readString(Path.of("reindex.json")))));
    CLI
    kloudlite pipelines create --owner acme --file reindex.json
  3. Keep the URL as a secret of the calling system.

  4. Send a call to the URL from that system.

    CLI
    curl -fsS -X POST -H 'Content-Type: application/json' -d '{"page": "pricing"}' "$WEBHOOK_URL"

Close-out

  1. Make sure that the call answered 202 with the id of a run.
  2. Read the log of the job index. Make sure that it shows reindexing page pricing.

Make a new webhook URL

Reason for the task: Stop an old webhook URL that is lost or shared, and get a new one.

Job set-up

CAUTION

The old URL stops at once. Calls from systems that still use it answer 404.

Procedure

  1. Make a new webhook URL. The answer shows the new URL once.

    Python
    print(kl.pipelines.rotate_webhook(owner="acme", name="reindex")["webhookUrl"])
    TypeScript
    console.log((await kl.pipelines.rotateWebhook({ owner: "acme", name: "reindex" })).webhookUrl);
    Go
    url, err := kl.Pipelines.RotateWebhook("acme", "reindex")if err != nil {    log.Fatal(err)}fmt.Println(url)
    Ruby
    puts kl.pipelines.rotate_webhook(owner: "acme", name: "reindex")["webhookUrl"]
    Java
    System.out.println(kl.pipelines().rotateWebhook("acme", "reindex"));
    CLI
    kloudlite pipelines rotate-webhook acme/reindex
  2. Replace the URL in each system that calls it.

Close-out

  1. Send a call to the new URL. Make sure that it answers 202.

Start a pipeline after another pipeline

Reason for the task: Run follow-up work when earlier work succeeds, for example a release after tests.

Job set-up

Procedure

  1. Add triggers.after to the definition of the follow-up pipeline.

    json
    {  "name": "publish-status",  "triggers": { "after": { "pipeline": "site-check", "result": "success" } },  "jobs": {    "publish": {      "steps": [{ "name": "publish", "run": "echo \"all sites are up\"" }]    }  }}
  2. Create the pipeline from the file. See Create a pipeline.

  3. Start a run of site-check and wait for it to end. See Start a run and follow it.

Close-out

  1. Read the finished run of site-check. Make sure that triggered names publish-status and a run.
  2. Show the runs of publish-status. Make sure that the newest run has event pipeline.

Troubleshooting

SymptomPossible causeCorrective action
422 with `inputs.day`: required with no default, so a schedule, webhook or after trigger could never start this pipelineA trigger cannot give a value for a required input.Give the input a default, or remove required.
422 with unknown input `date` (declared: dataset, day)The start names an input that the definition does not declare.Use a declared name.
422 with input `day` is requiredThe start gives no value for a required input with no default.Give the value at the start.
422 with input `dataset`: `users` is not one of orders, customersThe value is not in options.Give a value from options.
422 with `triggers.schedule`: `0 2 * *`: a schedule has five fields (minute hour day month weekday), not 4The cron line is not valid.Correct the line. See Cron.
422 with `run`: `webhook.body.*` is readable only in `env` and `if`; set it in `env` and read it in the script as "$NAME"A command reads the webhook data.Move the value to env. See Read webhook data safely.
422 with `run`: `env.PAGE` carries the webhook body; read it in the script as "$PAGE", never as `${{ env.PAGE }}`A command reads a variable that holds webhook data as an expression.Use "$PAGE" in the command.
422 with `triggers.after`: a pipeline cannot follow itselfThe after trigger names its own pipeline.Name another pipeline.
422 with `triggers.after`: no pipeline site-checkThe pipeline that after names does not exist.Create it first, or correct the name.
triggered shows the after chain is deeper than 5Six or more pipelines start each other in a chain.Make the chain shorter.
A webhook call answers 404 with no bodyThe URL is wrong or old, the pipeline is gone, or the webhook is off.Check triggers.webhook. Rotate the URL if it is lost.
A webhook call answers 413The body is larger than 256 KiB.Send a smaller body, for example an id that the job reads.
A webhook call answers 429The pipeline had 30 calls in this minute.Wait for the next minute, or send fewer calls.
409 with the webhook is off; turn it on in a new versionA rotate of a pipeline whose newest version has no webhook.Set triggers.webhook to true and save a new version.
No run starts at the scheduled timeThe newest version has no such line, or the platform did not run in that minute.Read the pipeline and check triggers.schedule.