Start a run
From the CLI
Section titled “From the CLI”kindgi runs start --flow=acme.review-order --input='{"orderId":"A-200"}'kindgi runs start --flow=acme.review-order --input=@order.json--input is the run's input as JSON, or @ and a file to read it from. The
command waits for the run to finish, however long it takes, and prints it.
Ctrl+C stops the waiting, not the run:
Stopped waiting. Run f91ff793-b029-48e8-9ce4-f9fa70bd0c40 goes on: kindgi runs get f91ff793-b029-48e8-9ce4-f9fa70bd0c40With --no-wait, it prints the run as soon as it exists:
kindgi runs start --flow=acme.reserve-order --input='{"orderId":"A-300"}' --no-wait{ "id": "1b1f6486-3faf-4ab7-8506-c09f50f989bc", … "flowId": "acme.reserve-order", "flowVersion": "0.1.0", "status": "pending", "dryRun": false, …}kindgi runs get <run-id> shows it again, with its output once it's done.
--agent-version=<version> (or --flow-version) runs that version instead of
the latest, and --project=<project-id> runs in that project instead of the
agent's or flow's own, which is Default for what kindgi deploy registers
(kindgi projects list lists the projects with their ids). With
authorization on, naming a project needs write on it:
kindgi runs start --agent=acme.order-desk --agent-version=0.1.0 --project=<project-id> --input='{"userMessage":"Where is order A-1002?"}'A --project that isn't a project in your tenant stops before anything runs,
with No project with id "<project-id>" in this tenant (the API answers 404
project-not-found). One that isn't an id at all, like --project=support,
gets `projectId` must be a project id (a UUID).
From your app
Section titled “From your app”Create a client. With no arguments it reads the API's URL and a token from
KINDGI_API_URL and KINDGI_API_TOKEN, which you set in your env file
(.env / .env.local). In development, when they aren't set, it uses the
running kindgi dev, which writes both to .kindgirc.json, and warns once
that you should set them. In production (NODE_ENV or KINDGI_ENV set to
production) it never does, so set them there.
import { createClient } from '@kindgi/sdk/client';
const kindgi = createClient(); // KINDGI_API_URL, KINDGI_API_TOKEN, or the running kindgi devfrom kindgi.client import Kindgi
kindgi = Kindgi() # KINDGI_API_URL, KINDGI_API_TOKEN, or the running kindgi devWait for the result
Section titled “Wait for the result”const run = await kindgi.runs.start({ flow: 'acme.review-order', input: { orderId: 'A-200' },});console.log(run.status, run.output);completed { status: 'held', orderId: 'A-200' }run = kindgi.runs.start(flow="acme.review-order", input={"orderId": "A-200"})print(run.status, run.output)completed {'status': 'held', 'orderId': 'A-200'}start returns when the run completes, fails or waits for something (a
person's approval, for example: status is then suspended). Use it when the
run is short and the caller needs the answer.
Each client waits for an answer up to its timeout: 30 seconds in TypeScript
(timeoutMs, on createClient or on one call to start), 60 seconds in
Python (timeout=, on Kindgi(...) or on one call). A waited start whose run
takes longer fails on the client, and the run's id never arrives, though the
run may still be going:
The run didn't end within 30 s, the client's timeout (timeoutMs). A waited start answers only when the run ends, so the run may still be going, and its id didn't arrive. Start a run that can take longer with `options: { wait: false }`: the answer carries its id at once. Then follow it with `runs.stream(runId)` or `runs.get(runId)`. Or raise `timeoutMs`.A NetworkError, after 60 seconds:
The run didn't end within 60 s, the client's timeout (timeout). A waited start answers only when the run ends, so the run may still be going, and its id didn't arrive. Start a run that can take longer with `options={"wait": False}`: the answer carries its id at once. Then follow it with `runs.stream(run_id)` or `runs.get(run_id)`. Or raise `timeout`.So start a run that can take longer than the timeout in the background (below), and follow it, rather than waiting on it or sending the start again.
Start it in the background
Section titled “Start it in the background”With wait: false, start returns as soon as the run exists, with status
pending, and the run finishes on the server:
let run = await kindgi.runs.start({ flow: 'acme.reserve-order', input: { orderId: 'A-300' }, options: { wait: false },});console.log(run.id, run.status);
while (run.status === 'pending' || run.status === 'running') { await new Promise((resolve) => setTimeout(resolve, 1000)); run = await kindgi.runs.get(run.id);}console.log(run.status, run.output);dbcf7457-0d25-49b9-9112-b03c9b44e9e8 pendingcompleted { attempt: 3, orderId: 'A-300', reserved: true }import time
run = kindgi.runs.start( flow="acme.reserve-order", input={"orderId": "A-300"}, options={"wait": False})print(run.id, run.status)
while run.status in ("pending", "running"): time.sleep(1) run = kindgi.runs.get(run.id)print(run.status, run.output)0ed299be-e04d-47bb-84b8-2ff9a4286822 pendingcompleted {'attempt': 3, 'orderId': 'A-300', 'reserved': True}Polling works, but there are better ways to learn that a run ended: follow its events, or have Kindgi send your app a webhook when it finishes.
Agent runs take options: { wait: false } too: the turn then runs in the
background. Start an agent with agent instead of flow, and its input
({ "userMessage": "…" }, see
Give an agent its input).
What you get back
Section titled “What you get back”| Field | |
|---|---|
id |
The run's id: for get, cancel, the journal and the event stream. |
status |
pending, running, suspended, completed, failed or cancelled. kindgi runs resume <run-id> says what a suspended run waits for (details). |
output |
What the run returned, once it completed. |
failure |
Why it failed: its code and message (an agent's turn has its own code, such as budget-exceeded; any other failure is run-failed). See why runs failed. |
flowId, flowVersion |
The flow and the version the run is pinned to. An agent's turn reports agent.turn, and its agent is in agent. |
agent |
For an agent's turn: the agent's id, the version that ran, and the conversationId. A flow's run has none, nor does a turn from before Kindgi 0.1.3. |
dryRun |
Whether it was a dry run. |
publicAccessToken |
A read-only token for this run, for a browser, when the deployment issues them (details). Only start returns it. |
The client references have every field and option: TypeScript, Python, and the CLI.
When it fails
Section titled “When it fails”A run that fails is still a run: start returns it, with status: "failed"
and its failure. A request the API refuses (a flow that doesn't exist,
a malformed input) raises an error instead, and no run exists:
import { KindgiApiError } from '@kindgi/sdk/client';
const run = await kindgi.runs.start({ flow: 'acme.review-order', input: { orderId: 'Z-9' } });if (run.status === 'failed') console.log(run.failure?.code, run.failure?.message);
try { await kindgi.runs.start({ flow: 'acme.no-such-flow', input: {} });} catch (err) { if (err instanceof KindgiApiError) console.log(err.error.code, err.message);}run-failed handler-error: Tool "acme.get-order" handler threw: handler-throw: Handler for tool "acme.get-order" threw: Error: No order Z-9not-found No flow "acme.no-such-flow" is registered in this tenant.from kindgi.client import NotFoundError
run = kindgi.runs.start(flow="acme.review-order", input={"orderId": "Z-9"})if run.status == "failed": print(run.failure.code, run.failure.message)
try: kindgi.runs.start(flow="acme.no-such-flow", input={})except NotFoundError as err: print(err)run-failed handler-error: Tool "acme.get-order" handler threw: handler-throw: Handler for tool "acme.get-order" threw: ValueError: No order Z-9No flow "acme.no-such-flow" is registered in this tenant.A request that times out on your side may have started the run anyway. Retry a start safely shows how to retry without starting it twice.
In the console
Section titled “In the console”A failed run's page says what happened in a sentence, then the runtime's own words and the code:
The turn ran out of its budget.Agent turn steps budget exceeded (limit 1, observed 1)Code budget-exceeded
A run that ended at its approval didn't go wrong: someone, or the approval's deadline, decided. Its page says which, not in red: "A reviewer rejected it.", "Its approval was withdrawn.", "No one decided its approval in time." or "Its approval was closed before anyone decided.", then "It ended at its approval: a review outcome, not an error.", the reviewer's rationale and the code (Decide an approval).

Run it again
Section titled “Run it again”A finished run's page has Run again for whoever may run its agent or flow. A step inside a flow has none. The dialog, Run again, for real, says what the new run is and does, for that run:
The agent demo.echo-agent at 1.0.0, its version then, with the same input.The turn's parameters aren't kept with the run: add them under `parameters` if it had any.This is a new, live run: every tool it calls acts for real again (a refund would be issued again).What the agent remembers about cust-9 is read, and may be added to.It starts a new conversation; the original one is left as it was.It starts as you, with your access, not as whoever started the original.The input is the run's, and you can change it before Start the run.
