# Show runs in your app

> Keep a run's id on your own row, and read its status, output, steps, sources and cost through the API, never from Kindgi's database or console.

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

- **An app that starts runs** with Kindgi 0.1's client, as [Start a run](https://docs.kindgi.com/v0.1.8/guides/runs/start-a-run.md) shows.
- **Node 22.12 or later**, **Python 3.11 or later**, or **Java 17 or later**, where your app's server runs.
- **`KINDGI_API_URL` and `KINDGI_API_TOKEN`** set there: your runtime's URL and its API token. The client reads them.

## 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

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](https://docs.kindgi.com/v0.1.8/guides/webhooks/receive-run-finished.md) 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

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](https://docs.kindgi.com/v0.1.8/guides/runs/read-the-journal.md)               |
| Where an agent's answer came from: its model calls and tool results        | `GET /v1/provenance/{runId}`             | `provenance.get`                                     | [Trace an answer](https://docs.kindgi.com/v0.1.8/guides/observability/trace-an-answer.md)            |
| 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](https://docs.kindgi.com/v0.1.8/guides/observability/cost-per-run.md) |

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:

- **TypeScript**

  `app/run-details.ts`

  ```ts
  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,
    };
  }
  ```

- **Python**

  `app/run_details.py`

  ```python
  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),
      }
  ```

- **Java**

  `src/main/java/com/acme/app/RunDetails.java`

  ```java
  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() {}
  }
  ```

- **Scala**

  `src/main/scala/acme/app/RunDetails.scala`

  ```scala
  package acme.app

  import com.kindgi.client.Kindgi
  import java.time.OffsetDateTime
  import 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

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.

## Next

- [Get a webhook when a run finishes](https://docs.kindgi.com/v0.1.8/guides/webhooks/receive-run-finished.md)
- [Read a run's journal](https://docs.kindgi.com/v0.1.8/guides/runs/read-the-journal.md)
- [Trace an answer](https://docs.kindgi.com/v0.1.8/guides/observability/trace-an-answer.md)
- [Cost per run and per customer](https://docs.kindgi.com/v0.1.8/guides/observability/cost-per-run.md)
