---
title: "Get run"
method: GET
path: "/v1/runs/{id}"
tags: ["runs"]
---

# Get run

`GET /v1/runs/{id}`

## Request

- Base URL: `https://api.patricia.app` (the document declares no server; this is the origin it was published from)
- URL: `https://api.patricia.app/v1/runs/{id}`
- Auth: HTTP bearer

Retrieve a single run by ID, its live progress, and its available skill details.

## Path parameters

- `id` string, uuid, required

## Headers

- `X-Tenant-Id` string, nullable

## Response `200`

The requested agent run.

- RunDetailResponse: ``GET /runs/{id}`` with live progress and this run's peekable-skill signal. ``progress`` projects the Redis marker for a non-terminal run. It stays explicit when no live marker can be observed, and it is always null after the run reaches a terminal status. ``peekable_skill_slugs`` names the distinct skill slugs THIS run loaded (``read_skill_file`` / ``stage_skill_scripts``) that ``GET /skill-catalog/{slug}`` would answer for the caller right now — resolved through the exact same predicate that route serves by (:func:`services.skill_platform.catalog_detail.resolve_peekable_skill_slugs`), so the dashboard's "See skill" button can never offer a slug the peek drawer would then refuse. Deliberately absent from the list route: it costs a per-run resolution the run list has no use for, so only the detail load pays it.
  - `id` string, uuid, required
  - `tenant_id` string, uuid, required
  - `conversation_id` string, uuid, nullable
  - `actor_id` string, uuid, nullable
  - `trigger_code` 'mention' | 'dm' | 'message' | 'proactive' | 'cron' | 'event' | 'heartbeat' | 'discovery' | 'onboarding' | 'manual' | 'spawn', required
  - `trigger_context` RunTriggerContext: Curated provenance for the request card at the start of a run timeline.
    - `sender` string, nullable
    - `attachments` RunTriggerAttachment[]
      - `name` string, nullable
      - `mime_type` string, nullable
      - `size_bytes` integer, nullable
      - `external_id` string, nullable
    - `schedule_name` string, nullable
    - `prompt` string, nullable
    - `description` string, nullable
    - `cron_job_id` string, nullable
    - `built_in_template_key` string, nullable
    - `one_shot` boolean
    - `post_every_run` boolean
    - `mcp_direct_tool_ref` string, nullable
    - `admin_console_operator` boolean
    - `loop_id` string, nullable
    - `loop_final` boolean
    - `task_test_run` boolean
    - `task_summary` string, nullable
    - `prompt_digest` string, nullable
    - `admission` RunTriggerAdmission: Why a chat run's message was admitted as addressed to Patricia (``core.chat.types.AdmissionBasis``).
      - `basis` string, required
      - `generation` integer, required
      - `admitted_at` string, required
      - `classifier_source` string, nullable
    - `answered_ask_id` string, nullable
  - `message_excerpt` string, nullable
  - `status_code` 'queued' | 'running' | 'waiting_approval' | 'suspended' | 'completed' | 'failed' | 'cancelled' | 'denied_autonomous', required
  - `truncated` boolean
  - `no_result` boolean
  - `silent` boolean
  - `model` string, required
  - `input_tokens` integer, required
  - `output_tokens` integer, required
  - `cache_read_tokens` integer, required
  - `cache_write_tokens` integer, required
  - `cost_micro_usd` integer, required
  - `budget_micro_usd` integer, nullable
  - `duration_ms` integer, nullable
  - `error` string, nullable
  - `started_at` string, date-time, nullable
  - `completed_at` string, date-time, nullable
  - `created_at` string, date-time, required
  - `updated_at` string, date-time, required
  - `peekable_skill_slugs` string[]: Skill slugs this run loaded that the peek drawer (GET /skill-catalog/{slug}) would serve right now. Gated by the skills:read scope: a caller who holds runs:read but not skills:read always gets an empty list here, even for a run that did load a skill.
  - `progress` RunProgressOut: The live progress signal for a non-terminal run.
    - `state` 'working' | 'waiting_approval' | 'no_signal', required: Live marker state. no_signal means the API cannot observe the run right now. It is not a claim that the run is healthy.
    - `activity` string, required: Newest reported step label, or an empty string when unknown.
    - `steps` string[], required: Recent reported step labels, with the newest label last.
    - `waiting_on` string, required: Approval label when the run is waiting, or an empty string otherwise.
    - `checklist` string: The task checklist's current line, as 'the step in flight, N of M' while one step is in flight and 'N of M done' once none is. Empty when the run keeps no checklist, or when its list has nothing left to count. It says what the run is for; the step labels say what it is doing.
    - `started_at` string, date-time, nullable: When the live marker started this run.
    - `updated_at` string, date-time, nullable: When the live marker last changed.
  - `models_used` string[]: The models that served this run's rounds, in the order each first served one. A run the person switched to another model mid-way names both; each round is billed at its own model. Read from the first 1,024 rounds the run records, so a model that first served after that is not listed.
  - `credits` integer, required: Credits fixed when the run finished, with live-rate conversion for runs without a snapshot.

## Other responses

- `400`: Bad request
- `401`: Unauthorized
- `403`: Forbidden. Inspect response body `code` for the specific reason (for example: PERMISSION_DENIED, EMAIL_NOT_VERIFIED, TENANT_ACCESS_REQUIRED, ROLE_UNRESOLVED)
- `404`: Not found
- `422`: Validation error

---

[API](https://skmtc.dev/patricia/apis/patricia-api.md) · [All operations](https://skmtc.dev/patricia/apis/patricia-api/llms.txt) · [OpenAPI document](https://skmtc.dev/patricia/apis/patricia-api/revisions/0dfb44495ea1?raw)
