# Runtime API

> Submit local recipe jobs, poll their status, upload inputs and retrieve outputs, and distinguish Runtime's short-lived job store from Code's session API.

Source: https://kaitoi.io/docs/cli/runtime-api/
Section: Kaitoi CLI / Local services and recipes
Written for: Developers calling Runtime from an application or script.
Prerequisites: Kaitoi Runtime installed with the intended recipe available; Access to its protected machine token
Last reviewed: 2026-10-24

---

Runtime's API installs and executes recipes and transfers their files. It is
separate from Code's conversational session API. This guide follows the
registered routes in CLI **0.9.0**.

## Start the local service

```bash
kaitoi runtime serve --host 127.0.0.1 --port 8766
```

Unlike Code serve, Runtime accepts a host override. Keep loopback for local
clients; changing the host changes who can reach the service. Runtime stores a
machine token in its config folder's `server-token`, or accepts
`KAITOI_RUNTIME_TOKEN`. There is no `--token` serve option. Config/cache locations
honor XDG on Unix and APPDATA/LOCALAPPDATA on Windows.

Only `GET /health` is public. Other routes require
`Authorization: Bearer <runtime-token>`. Browser CORS and interactive OpenAPI
docs are disabled. The machine token is not your Kaitoi account API key.

## Submit, poll, then collect

POST a job with the recipe's own inputs, for example the authored recipe from
the [recipe guide](/docs/cli/create-recipes/):

```json
{
	"recipe": "hello-text",
	"inputs": { "message": "Hello from the API" }
}
```

The recipe name must resolve in that Runtime. Optional fields are `backend`,
`timeout`, and `force_reinstall`. A 202 response with `job_id` means accepted;
validation or execution failure can still arrive in the polled job.


Submit POST /jobs, keep job_id, and poll GET /jobs/{job_id}. Pending work moves
to running, then completed with a result or failed with an error. Download
declared output files separately. Completed and failed records are in memory
and can be removed after 300 seconds; restarting Runtime loses them. There is
no job cancellation or SSE endpoint in this API.

The example keeps ticket j1 through pending, running and a completed or failed result. A collected local file can remain after the terminal job record becomes eligible for lazy removal; a failed job has no successful file to collect.

*Figure: Runtime jobs are accepted asynchronously. Collect successful outputs before the short-lived record disappears.*

Poll for `pending`, `running`, `completed`, or `failed`. Successful results
include outputs, duration, backend, recipe name/version, and stderr. Failure
includes an error and, when provided, an error code. Job records are in memory;
terminal jobs are eligible for lazy removal after 300 seconds and all jobs
disappear on server restart. Save the result you need promptly.

## Route reference

| Method and path               | Purpose                                                          |
| ----------------------------- | ---------------------------------------------------------------- |
| `GET /health`                 | Public status, version, and input/source-policy metadata.        |
| `GET /hardware`               | Detected hardware.                                               |
| `POST /jobs`                  | Submit `{recipe, inputs, backend?, timeout?, force_reinstall?}`. |
| `GET /jobs/{job_id}`          | Poll status and eventual result or error.                        |
| `POST /install`               | Install `{recipe, backend?, force?}`.                            |
| `GET /packages`               | Installed package metadata.                                      |
| `GET /recipes/available`      | Registry entries and installed flags.                            |
| `GET /packages/{name}/recipe` | Installed recipe YAML and companion source.                      |
| `DELETE /packages/{name}`     | Uninstall a package.                                             |
| `POST /upload`                | Multipart file upload; returns `{path}`.                         |
| `GET /files?path=...`         | Download a resolved file within Runtime cache.                   |

There is no registered `POST /run`, cancel-job route, or SSE job stream. Use the
job submission and polling routes even if older help text mentions `/run`.

## Respect remote input and file boundaries

HTTP jobs enforce the remote input policy. `remote: false` fields cannot be
supplied by the caller, though their defaults can apply. File inputs must use a
path returned by `/upload`; that is a Runtime cache path, not a Kaitoi cloud
file ID. Path-like strings and unapproved model/weight selectors are restricted.
Bounded enums or explicitly remote-enabled selectors define the allowed choices.

Download only returned files within Runtime cache through `/files`. Arbitrary
machine paths and cloud library files are outside that transfer contract.

## Use the in-process Python API when appropriate

For owner-local Python execution, the distinct `kaitoi_runtime` package offers:

```python
from kaitoi_runtime import run

result = run("./recipe.yaml", inputs={"message": "Hello"})
print(result.outputs)
```

This synchronous helper uses `asyncio.run`. Inside an existing async loop, use
`await Runner().run(...)` after importing `Runner` instead. It returns a
`RunResult`, executes locally, and does not apply HTTP-only remote restrictions.
It is unrelated to `KaitoiCodeClient` and does not create a Code conversation.
