> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://onepin.ai/docs/reference/api-reference/workflows/runs/get/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://onepin.ai/_mcp/server. # Get Run GET https://api.onepin.ai/api/v1/workflows/{workflow_id}/runs/{run_id} Fetch full detail for a single workflow run. Returns all run fields plus `definition_snapshot` — the graph and execution config captured at the moment the run started. The snapshot is returned raw (no config migrations applied), so it faithfully represents the workflow as it existed for this specific execution even if the workflow definition has since been edited. This is the heaviest run endpoint. For progress polling, use the lighter `GET /runs/{run_id}/status` which omits the snapshot. For aggregated visual metrics, use `GET /runs/{run_id}/overview`. For the per-node step log, use `GET /runs/{run_id}/steps`; opt into full results and audio playback URLs with `include_result=true`. Reference: https://onepin.ai/docs/reference/api-reference/workflows/runs/get ## Authentication - `Authorization` header (bearer token, required) — Clerk JWT token - `Authorization` header (bearer token, required) — Onepin live API key (`op_live_...`). Test and public keys are reserved in Phase 1. ## Servers - `https://api.onepin.ai` (prod, default) - `https://dev-api.onepin.ai` (dev) ## Request ### Path parameters - `workflow_id` (string, required) - `run_id` (string, required) ### Headers - `X-Workspace-Id` (string, optional, nullable) ## Response ### 200 Successful Response - `data` (WorkflowRunDetailOut, required) — Run detail response, including the graph/execution snapshot. The snapshot is returned raw — no config migrations are applied — so it reflects the workflow exactly as it existed when the run started, even if the underlying workflow has since been edited. - `meta` (Meta, required) ## Errors ### 422 Unprocessable Entity Error Validation Error - `detail` (list of ValidationError, optional) ## Types ### WorkflowRunDetailOut Run detail response, including the graph/execution snapshot. The snapshot is returned raw — no config migrations are applied — so it reflects the workflow exactly as it existed when the run started, even if the underlying workflow has since been edited. - `id` (string, required) - `workflow_id` (string, required) - `status` (string, required) - `run_number` (integer, required) - `token_cost` (integer, required) - `created_at` (datetime, required) - `updated_at` (datetime, required) - `total_nodes` (integer, optional, default: 0) - `total_steps` (integer, optional, default: 0) - `finished_steps` (integer, optional, default: 0) - `finished_nodes` (integer, optional, default: 0) — Graph nodes that have reached a terminal state, judged by each node's highest-iteration step, so a retried node counts once. Pair with total_nodes for 'N of M completed'. A node interrupted by an automatic pause does NOT count while parked (its step is cancelled with a hard recovery checkpoint and is reopened in place on resume), so that kind of pause neither inflates the count nor drops it on resume. A partial-recovery resume schedules a NEW iteration for the node, so the count can drop by one until that iteration finishes. finished_steps counts rows and never excludes the parked row. - `held_nodes` (list of HeldNodeOut, optional, nullable) — Nodes the run is parked in front of, each with the locales it will process. Meaningful while status == 'paused'; cleared to null on resume and RETAINED on a run cancelled while paused. Null (never []) also means never computed: a run paused before this field shipped, or one parked straight from 'pending'. [] means it WAS computed and nothing is held. Excludes nodes skipped by an upstream discard and a node interrupted mid-execution by an automatic pause (that one is visible as its own cancelled step). - `usage_summary` (map from string to any, optional) - `started_at` (datetime, optional, nullable) - `completed_at` (datetime, optional, nullable) - `pause_requested_at` (datetime, optional, nullable) - `paused_at` (datetime, optional, nullable) - `paused_ms` (integer, optional, default: 0) - `pause_reason` (enum, optional, nullable) — Why the run is paused or draining toward an automatic pause. Manual pauses use user; null when no pause is active. - Allowed values: `user`, `provider_outage`, `provider_rate_limited`, `provider_billing`, `node_timeout` - `pause_error` (string, optional, nullable) — Aggregate customer-facing explanation for an automatic pause. Null for manual pauses and separate from terminal error. - `error` (string, optional, nullable) - `has_export` (boolean, optional, default: false) - `triggered_by` (TriggeredByOut, optional, nullable) — Actor that triggered a workflow run. ``user_name`` is guaranteed non-empty: derived from ``donut.utils.user.user_display_name`` which falls back to ``email`` when both first/last names are empty (``email`` is NOT NULL on User). - `credits` (integer, optional, default: 0) — Credits debited from the user's spendable balance for this run; excludes invoiced overage. - `credits_absorbed` (double, optional, default: 0) — Credits discounted by floor-rounding for this run. This is the positive sub-credit remainder above the charged floor and excludes any minimum charge adjustment; charged credits plus this value reconstructs true cost only for fully covered, uncapped runs that were not lifted by the minimum charge. Returns 0 for unsettled or legacy runs. - `definition_snapshot` (map from string to any, optional) - `public_share_id` (string, optional, nullable) — Share id when this run is published publicly, else null. Clients build the share URL as `/r/`. Readable with an API key, but only the dashboard (Clerk JWT) can create or revoke a share. - `shared_at` (datetime, optional, nullable) — When the run was published. Null whenever `public_share_id` is null. ### Meta - `request_id` (string, required) - `timestamp` (datetime, required) ### ValidationError - `loc` (list of ValidationErrorLocItems, required) - `msg` (string, required) - `type` (string, required) - `input` (any, optional) - `ctx` (ValidationErrorCtx, optional) ### HeldNodeOut One node the run is parked in front of — it has no step row yet. - `node_id` (string, required) - `locales` (list of string, optional) — Locales of the lines this node will process once the run resumes. Empty means render no language line. ### TriggeredByOut Actor that triggered a workflow run. ``user_name`` is guaranteed non-empty: derived from ``donut.utils.user.user_display_name`` which falls back to ``email`` when both first/last names are empty (``email`` is NOT NULL on User). - `user_id` (string, required) - `user_name` (string, required) ### ValidationErrorLocItems ### ValidationErrorCtx ## Examples **Response** ```json { "data": { "id": "string", "workflow_id": "string", "status": "string", "run_number": 1, "token_cost": 1, "created_at": "2024-01-15T09:30:00Z", "updated_at": "2024-01-15T09:30:00Z", "total_nodes": 0, "total_steps": 0, "finished_steps": 0, "finished_nodes": 0, "held_nodes": [ { "node_id": "string", "locales": [ "string" ] } ], "usage_summary": {}, "started_at": "2024-01-15T09:30:00Z", "completed_at": "2024-01-15T09:30:00Z", "pause_requested_at": "2024-01-15T09:30:00Z", "paused_at": "2024-01-15T09:30:00Z", "paused_ms": 0, "pause_reason": "user", "pause_error": "string", "error": "string", "has_export": false, "triggered_by": { "user_id": "string", "user_name": "string" }, "credits": 0, "credits_absorbed": 0, "definition_snapshot": {}, "public_share_id": "string", "shared_at": "2024-01-15T09:30:00Z" }, "meta": { "request_id": "string", "timestamp": "2024-01-15T09:30:00Z" } } ``` **SDK Code** ```python import requests url = "https://api.onepin.ai/api/v1/workflows/workflow_id/runs/run_id" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.onepin.ai/api/v1/workflows/workflow_id/runs/run_id'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.onepin.ai/api/v1/workflows/workflow_id/runs/run_id" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "Bearer ") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.onepin.ai/api/v1/workflows/workflow_id/runs/run_id") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["Authorization"] = 'Bearer ' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.onepin.ai/api/v1/workflows/workflow_id/runs/run_id") .header("Authorization", "Bearer ") .asString(); ``` ```php request('GET', 'https://api.onepin.ai/api/v1/workflows/workflow_id/runs/run_id', [ 'headers' => [ 'Authorization' => 'Bearer ', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.onepin.ai/api/v1/workflows/workflow_id/runs/run_id"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", "Bearer "); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": "Bearer "] let request = NSMutableURLRequest(url: NSURL(string: "https://api.onepin.ai/api/v1/workflows/workflow_id/runs/run_id")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` > Onepin is a voice workflow platform that orchestrates, validates, and ships production-ready audio across 33 TTS models.