Inbound triggers
A trigger files a work item from an event instead of kraft item create.
There are two kinds, a cron entry in policy.yaml and an HTTP call. Both
always file the item paused, exactly like manual
intake: an agent can't start work from a trigger
any more than from your own kraft item create. To set one up, see
Schedule or webhook work.
Cron entries
Each entry in the triggers: list of policy.yaml has these fields:
| Field | Required | Meaning |
|---|---|---|
cron | yes | Five fields: minute, hour, day of month, month, day of week (0 is Sunday). Each field is * or a comma-separated list of integers. Ranges (1-5) and steps (*/15) aren't supported. |
repo | yes | Path of a repo you connected with kraft repo connect. |
chain | yes | A chain template id from templates/chains/. |
title | yes | The work item's title. |
description | no | The work item's description. Defaults to "". |
Kraft evaluates the schedule in UTC, not in the server's local time zone. It
checks once a minute, and a minute missed while the server is down isn't
backfilled. The poller runs for the life of the server, so a trigger you add
later fires without a restart. kraft admin reload rereads policy.yaml.
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 template library is invalid.
POST /api/triggers
The HTTP twin of a cron entry, for anything that can fire a webhook and can't wait for the next minute tick. The request body is JSON:
| Field | Required | Meaning |
|---|---|---|
title | yes | The work item's title. |
repo | yes | Path of a connected repo. |
chain_template | no | A chain template id. Defaults to the repo's default_chain_template, else default. |
description | no | The work item's description. Defaults to "". |
The chain field is chain in policy.yaml and chain_template in the HTTP
body.
| Caller | Authentication |
|---|---|
| The machine running Kraft, while it is bound to loopback | None. |
| Any caller after a non-loopback bind, the same machine included | The trigger token in $KRAFT_HOME/run/trigger-token, sent as Authorization: Bearer <token>. The MCP bearer token in $KRAFT_HOME/run/mcp-token or a browser session cookie also works. See Remote access. |
| 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 | A remote caller while no password is set, or a browser request from a disallowed Host or another site. |
422 | The body is not valid: repo or title is missing, or a field has the wrong type. |
422 | The repo is not connected. The message names kraft repo connect. |
422 | The repo path no longer exists. |
422 | chain_template names no chain, or that chain does not resolve. |
422 | The title is longer than 500 characters. |
422 | repos.yaml cannot be read, or the repo's policy or steering is invalid. The message names the problem. |
502 | Filing failed after the checks passed. The message starts intake failed:. |
503 | policy.yaml or the template library is invalid, so Kraft refuses all new work. kraft admin doctor names the file. |
See HTTP API for the rest of the API.
{"id": "<work-item-id>", "title": "Nightly dependency check", "status": "paused", "...": "..."}
POST /api/triggers only, so its holder can file paused work and nothing else.
The MCP bearer token passes it on every route, so whoever holds that one can
also start, approve and cancel work and change settings. To rotate either, see
The bearer token.