Show runs in your app
This guide shows a run in your own app: its status, output, steps, sources and cost, read through Kindgi's API from the run's id.
Your app keeps what a run did on its own rows, such as a ticket a flow triaged or an answer an agent gave. It keeps the run's id there, and reads the rest from Kindgi's API when it needs it. Your users see the run in your app, in your UI.
Before you start
Section titled “Before you start”- An app that starts runs with Kindgi 0.1's client, as Start a run shows.
- Node 22.12 or later, Python 3.11 or later, or Java 17 or later, where your app's server runs.
KINDGI_API_URLandKINDGI_API_TOKENset there: your runtime's URL and its API token. The client reads them.
Keep the run's id
Section titled “Keep the run's id”Your app needs only the run's id to read the run again.
Store the id that runs.start answers with on your own record, in a column
such as kindgi_run_id.
Each record now names its run.
Hear when it finished
Section titled “Hear when it finished”A webhook lets your app read the run once it ends, instead of polling.
Subscribe to the run.finished webhook, as
Get a webhook when a run finishes
shows.
Each delivery carries the run's id, its outcome and what its model calls
cost. The outcome is completed, failed or cancelled. A delivery never
carries the run's output, so your handler finds the row by kindgi_run_id
and reads the run.
Read what you show
Section titled “Read what you show”Each thing your app shows has its own read:
| Your app shows | Read | Client call | Guide |
|---|---|---|---|
| The status, the output, when it started and finished | GET /v1/runs/{runId} |
runs.get |
|
| Only the status and timing, with no data | GET /v1/runs/{runId}/progress |
runs.progress |
|
| What happened, step by step: tool calls, branches, approvals | GET /v1/runs/{runId}/journal |
runs.journal |
Read a run's journal |
| Where an agent's answer came from: its model calls and tool results | GET /v1/provenance/{runId} |
provenance.get |
Trace an answer |
| What it cost: each model call's tokens and cost, its agent steps' included | GET /v1/cost/records?rootRunId={runId} |
cost.usage.query, or cost.records.list in Python |
Cost per run and per customer |
Read the run from your app's server, with the id from your record. Ask your coding agent: "I want my app to show the status and output of the run behind each record." For reference, this function reads what a ticket's page shows:
import { createClient } from '@kindgi/sdk/client';import type { RunId } from '@kindgi/sdk/types';
const kindgi = createClient(); // KINDGI_API_URL, KINDGI_API_TOKEN
/** What a ticket's page shows about its run, from the ticket's kindgi_run_id. */export async function runDetails(kindgiRunId: string) { const runId = kindgiRunId as RunId; const run = await kindgi.runs.get(runId); const { data: steps } = await kindgi.runs.journal(runId); return { status: run.status, startedAt: run.createdAt, finishedAt: run.completedAt, output: run.output, steps: steps.length, };}from kindgi.client import Kindgi
kindgi = Kindgi() # KINDGI_API_URL, KINDGI_API_TOKEN
def run_details(kindgi_run_id: str) -> dict[str, object]: """What a ticket's page shows about its run, from the ticket's kindgi_run_id.""" run = kindgi.runs.get(kindgi_run_id) steps = kindgi.runs.journal(kindgi_run_id).data return { "status": run.status, "started_at": run.created_at, "finished_at": run.completed_at, "output": run.output, "steps": len(steps), }package com.acme.app;
import com.kindgi.client.Kindgi;import com.kindgi.client.models.Run;import java.time.OffsetDateTime;import java.util.UUID;
public final class RunDetails { private static final Kindgi kindgi = Kindgi.create(); // KINDGI_API_URL, KINDGI_API_TOKEN
public record Details( String status, OffsetDateTime startedAt, OffsetDateTime finishedAt, Object output, int steps) {}
/** What a ticket's page shows about its run, from the ticket's kindgi_run_id. */ public static Details runDetails(String kindgiRunId) { UUID runId = UUID.fromString(kindgiRunId); Run run = kindgi.runs().get(runId); int steps = kindgi.runs().journal(runId).data().size(); return new Details(run.status().toString(), run.createdAt(), run.completedAt(), run.output(), steps); }
private RunDetails() {}}package acme.app
import com.kindgi.client.Kindgiimport java.time.OffsetDateTimeimport java.util.UUID
object RunDetails { private val kindgi = Kindgi.create() // KINDGI_API_URL, KINDGI_API_TOKEN
final case class Details( status: String, startedAt: OffsetDateTime, finishedAt: OffsetDateTime, output: Any, steps: Int)
/** What a ticket's page shows about its run, from the ticket's kindgi_run_id. */ def runDetails(kindgiRunId: String): Details = { val runId = UUID.fromString(kindgiRunId) val run = kindgi.runs().get(runId) val steps = kindgi.runs().journal(runId).data.size Details(run.status.toString, run.createdAt, run.completedAt, run.output, steps) }}It returns the run's status, its start and finish times, its output, and how many journal entries it read.
A flow run has no provenance of its own, because each agent step in it is a
turn with its own run. The step's step.completed entry in the flow's
journal names that run in payload.output.runId. Read the turn's provenance
by that id.
Kindgi's database and console aren't your app's
Section titled “Kindgi's database and console aren't your app's”Read Kindgi's data only through its API, from your app's server:
- Don't query Kindgi's database, even when it's on the same Postgres server as your app's. Its schema is private and changes with every release. Row-level security guards every query by tenant. A runtime Kindgi hosts for you gives no database access at all. The API is the contract that stays.
- Don't send your users to Kindgi's console to see a run. Show what they need in your own UI, with your own access rules.
- To keep a copy for reporting or search, pull it through the API into your own tables.