Inbound triggers
A trigger files a work item from an event instead of kraft item create. There
are two kinds, a schedule in intake.yaml and an HTTP call. Both always file the
item paused, exactly like manual intake: a
trigger cannot start work, any more than kraft item create without
--autostart can. To set one up, see
Schedule or webhook work.
Schedules
A schedule is an entry in the schedules: list of intake.yaml:
schedules:
- cron: "0 9 * * 1,2,3,4,5" # 9:00 UTC on weekdays
repo: /path/to/repo
chain: default
title: "Weekday dependency check"
description: "Filed by the 09:00 UTC weekday schedule"
| Field | Type | Default | Meaning |
|---|---|---|---|
cron | string | required | A five-field cron expression, read in UTC. See Cron syntax. |
repo | string | required | Path of a repo you connected with kraft repo connect. |
chain | string | required | A chain id from config/chains/. |
title | string | required | The work item's title. It is one line: see Schedule titles. |
description | string | "" | The work item's description. |
Cron syntax
The five fields, in order:
| Field | Allowed values |
|---|---|
| minute | 0-59 |
| hour | 0-23 |
| day of month | 1-31 |
| month | 1-12 |
| day of week | 0-7 (0 and 7 are both Sunday) |
Each field takes any of these forms, or a comma-separated list of them, such as
1,2,3,4,5:
| Form | Example | Matches |
|---|---|---|
* | * | Every value. |
| An integer | 9 | That value. |
| A range | 1-5 | Every value from 1 to 5. |
| A step over everything | */15 | Every 15th value of the field, from its first (minutes 0, 15, 30, 45). |
| A step over a range | 10-40/10 | Every 10th value from 10 to 40 (10, 20, 30, 40). |
| A step from a start | 5/20 | Every 20th value from 5 to the field's top (minutes 5, 25, 45). |
Names (MON, JAN) are not read. A day of month and a day of week must both
match: 0 9 1 * 1 fires only on a 1st that is a Monday, not on every 1st and
every Monday as some crons do.
A value outside its field, a backwards range, or a zero step is refused where
intake.yaml is read, and the file does not load. When the file does not load says what you see.
Schedule titles
- A title is one line. One with a line break is filed with its first line as the title and the rest at the top of the description, rather than skipped.
- A block scalar (
title: >ortitle: |) ends in a line break unless written>-or|-. Kraft drops that trailing break. - Control characters are dropped too.
- A blank title takes the description's first line.
When a schedule fires
- Kraft evaluates the schedule in UTC, not in the server's local time zone.
- It checks once a minute. A minute missed while the server is down is not backfilled.
- The poller runs for the life of the server, so a schedule you add later fires without a restart.
kraft admin reloadrereadsintake.yaml, and so does a publish from Settings › Auto-intake.
A triggers: entry left in policy.yaml still fires. The Auto-intake screen reads and writes intake.yaml only, so it does not show one: change it in the file. A cron there that Kraft cannot read at all (a word, the wrong number of fields) refuses policy.yaml. Upgrade from 1.4 says how schedules moved, and what happens to the other bad ones.
When Kraft skips a schedule
Kraft skips an entry, logs a warning naming the reason, and still fires the other entries due in the same tick, when:
- the
repois not connected (the warning nameskraft repo connect); - the
chainid is unknown; - the
titleand thedescriptionare both blank; - the library or a chain is invalid;
- its
croncannot be read (a guard: anintake.yamlholding one does not load, see When the file does not load); - intake refuses the item (a chain policy past the instance maximum, or an unreadable attachment).
When policy.yaml is invalid, Kraft skips the whole tick instead, so no entry
fires that minute.
POST /api/triggers
The HTTP twin of a schedule, for anything that can fire a webhook and cannot wait for the next minute tick. A call like this, from the machine running Kraft:
curl -s -X POST http://127.0.0.1:8765/api/triggers \
-H 'Content-Type: application/json' \
-d '{"title": "Nightly dependency check", "repo": "/path/to/repo"}'
Once Kraft is bound off loopback, add -H "Authorization: Bearer $TOKEN", where $TOKEN is the trigger token and never the MCP token: see the warning below and Authentication. Schedule or webhook work has the worked example, including the call from another machine.
The call answers 201 with the new work item as JSON, filed paused. Cut to the fields
that matter, the body reads:
{"id": "<work-item-id>", "title": "Nightly dependency check", "status": "paused", "...": "..."}
The full shape is in /openapi.json; see HTTP API.
Request
The request body is JSON:
| Field | Type | Default | Meaning |
|---|---|---|---|
title | string | required | The work item's title. |
repo | string | required | Path of a connected repo. |
chain | string | The repo's default_chain, else default | A chain id. |
description | string | "" | The work item's description. |
Authentication
Once Kraft is bound off loopback, send the trigger token in $KRAFT_HOME/run/trigger-token as Authorization: Bearer <token>. The MCP bearer token or a browser session cookie also works. While Kraft is bound to loopback, the machine running it needs no token. Who may call lists every caller.
Responses
| Status | Meaning |
|---|---|
201 | The item was filed. The body is the work item as JSON: id is the new item's id and status is paused. |
401 | Kraft is bound off loopback, and the request has no valid bearer token or session. |
403 | The caller is refused. Who may call lists the causes. |
422 | title is missing, empty, over 500 characters, or not one line of plain text. See The title. |
422 | repo is missing or not connected (the message names kraft repo connect), its path no longer exists, or repos.yaml, the repo's policy or its steering cannot be read or is invalid. |
422 | chain names no chain or one that does not resolve, or chain and chain_template are both sent and name different chains (the detail names both). |
422 | Another field has the wrong type. |
502 | Filing failed after the checks passed. The message starts intake failed:. |
503 | policy.yaml, the library or a chain is invalid, so Kraft refuses all new work. kraft admin doctor names the file. |
The title
A 422 for title says why:
title cannot be empty: the title is empty or only whitespace.the title is one line: it holds a line break, a trailing one included. Put the rest indescription.the title is plain text: it holds a tab, an escape or another control character.
Related
- Schedule or webhook work: set up a schedule or a webhook, and check it arrived.
- HTTP API: the rest of the API, and who may call it.
- intake.yaml: the other fields of
intake.yaml.