curl --request GET \
--url https://api.instaview.sk/runs/{id} \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.instaview.sk/runs/{id}"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.instaview.sk/runs/{id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.instaview.sk/runs/{id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.instaview.sk/runs/{id}"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.instaview.sk/runs/{id}")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.instaview.sk/runs/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"id": "789e0123-e45b-67d8-a901-234567890123",
"name": "Q3 product follow-ups",
"agentId": "456e7890-e12b-34d5-a678-901234567890",
"status": "RUNNING",
"progress": {
"total": 42,
"queued": 30,
"inProgress": 3,
"completed": 8,
"failed": 1,
"cancelled": 0,
"unreachable": 0,
"completion": 0.21
},
"createdAt": "2026-08-20T09:00:00Z",
"updatedAt": "2026-08-20T11:42:00Z",
"scheduledAt": "2026-09-15T09:00:00Z",
"scheduleError": "Insufficient minutes available to schedule 10 interviews (50 minutes total). Please purchase additional minutes."
}Get Run
Returns one run with its lifecycle status and aggregate progress, ensuring it belongs to the API key’s company. A run batches conversations rather than replacing them: it mints one conversation per contact, and progress counts those. For the per-contact detail behind the aggregate, use GET /runs/{id}/conversations.
curl --request GET \
--url https://api.instaview.sk/runs/{id} \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.instaview.sk/runs/{id}"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.instaview.sk/runs/{id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.instaview.sk/runs/{id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.instaview.sk/runs/{id}"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.instaview.sk/runs/{id}")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.instaview.sk/runs/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"id": "789e0123-e45b-67d8-a901-234567890123",
"name": "Q3 product follow-ups",
"agentId": "456e7890-e12b-34d5-a678-901234567890",
"status": "RUNNING",
"progress": {
"total": 42,
"queued": 30,
"inProgress": 3,
"completed": 8,
"failed": 1,
"cancelled": 0,
"unreachable": 0,
"completion": 0.21
},
"createdAt": "2026-08-20T09:00:00Z",
"updatedAt": "2026-08-20T11:42:00Z",
"scheduledAt": "2026-09-15T09:00:00Z",
"scheduleError": "Insufficient minutes available to schedule 10 interviews (50 minutes total). Please purchase additional minutes."
}Overview
A run is one agent dialling a list of contacts. It does not replace the conversation, it batches it: launching a run mints one conversation per contact, and the call attempts hang off that conversation exactly as they do for a conversation you created on its own. This endpoint returns the roll-up. For the per-contact detail behind it, use List Run Conversations.Use Cases
- Monitor a batch: poll one endpoint for the state of hundreds of calls
- Correlate: match a
conversation.*webhook back to the batch it came from, through therunIdin its payload - Report: show your users how far a campaign has got without holding call state yourself
Response Data
{
"id": "789e0123-e45b-67d8-a901-234567890123",
"name": "Q3 product follow-ups",
"agentId": "456e7890-e12b-34d5-a678-901234567890",
"status": "RUNNING",
"scheduledAt": null,
"scheduleError": null,
"progress": {
"total": 42,
"queued": 30,
"inProgress": 3,
"completed": 8,
"failed": 1,
"cancelled": 0,
"unreachable": 0,
"completion": 0.21
},
"createdAt": "2026-08-20T09:00:00Z",
"updatedAt": "2026-08-20T11:42:00Z"
}
- Every conversation lands in exactly one bucket, so the six counts sum to
total, andtotalis the run’s real conversation count. ADRAFTrun has produced none yet, so every count is0. inProgresscounts calls that are ringing or connected right now. That is not the same as the conversation’s storedstatus, which staysSCHEDULEDwhile a call is being placed — such a call is moved out ofqueuedrather than added on top, which is why the buckets still sum.unreachableis its own bucket rather than part offailed. Nothing malfunctioned: the retry budget was spent without the contact ever picking up.completionis the fraction of conversations in a terminal state —completed + failed + cancelled + unreachableovertotal— between0and1. It is0on a run that has produced nothing.
Scheduled runs
scheduledAt is when a SCHEDULED run will start dialling, and null on every run that launched immediately.
scheduleError is why a scheduled run did not start at its time. A scheduled launch is re-admitted against billing when it fires, and a refusal then has no request to answer — so it is reported here instead. The run stays SCHEDULED and is retried, which means resolving the cause is enough to make it go; there is nothing to re-book.
scheduledAt has passed
while its status is still SCHEDULED has been refused, and if you are not reading
scheduleError it will sit there indefinitely with nobody watching.Status
| Status | What it means |
|---|---|
DRAFT | Contacts may be attached; nothing has been dialled |
SCHEDULED | Booked for scheduledAt; nothing dialled until then |
RUNNING | Dispatching, or waiting on calls it has dispatched |
PAUSED | Stopped, reversibly. Nothing further is dialled until you resume |
COMPLETED | Every conversation has reached a terminal state |
CANCELLED | Stopped for good. Nothing further is dispatched |
COMPLETED is not terminal. Attaching a contact to a finished run reopens it to
RUNNING, which is what makes a run usable as a rolling sequence rather than a one-shot
batch. Do not treat COMPLETED as a signal to stop polling unless you also know nothing more
will be attached.Polling
async function waitForRun(runId) {
for (;;) {
const response = await fetch(`https://api.instaview.sk/runs/${runId}`, {
headers: { Authorization: `Bearer ${apiKey}` },
// Without a deadline a stalled request never settles.
signal: AbortSignal.timeout(10_000),
});
if (!response.ok) {
throw new Error(`Reading the run failed: ${response.status} ${response.statusText}`);
}
const run = await response.json();
if (run.progress.completion === 1) return run;
await new Promise((resolve) => setTimeout(resolve, 30_000));
}
}
conversation.* webhooks, and every one of those payloads carries runId — so a handler attributes the call to this run without polling, and without reading the conversation back.
Company Isolation
You can only read runs belonging to your own API key’s company. A run in another company returns404 Not Found, the same as one that does not exist — the API does not confirm that a resource you cannot read exists.
Error Scenarios
- 404 Not Found: the run does not exist, has been deleted, or belongs to a different company
- 403 Forbidden: the API key does not hold
read:runs
Related Resources
Runs Resource Guide
List Run Conversations
Conversations Resource Guide
Webhooks
Authorizations
API key for authentication using Bearer scheme
Path Parameters
Query Parameters
Required for ATS API keys to specify which company to access. Ignored for standard company API keys.
Response
Run ID
"789e0123-e45b-67d8-a901-234567890123"
Name of the run
"Q3 product follow-ups"
The agent this run dials with
"456e7890-e12b-34d5-a678-901234567890"
Lifecycle status. A DRAFT run has dialled nothing yet; COMPLETED is not terminal, because attaching a contact to a finished run reopens it to RUNNING.
DRAFT, SCHEDULED, RUNNING, PAUSED, COMPLETED, CANCELLED "RUNNING"
Aggregate progress across the run's conversations. Every conversation lands in exactly one bucket, so the six counts sum to total.
Show child attributes
Show child attributes
Created timestamp
"2026-08-20T09:00:00Z"
Updated timestamp
"2026-08-20T11:42:00Z"
When a scheduled run will start dialling, or null for one that launches immediately. Set together with the SCHEDULED status.
"2026-09-15T09:00:00Z"
Why a scheduled run did not start at its time, or null. A scheduled launch is re-admitted against billing when it fires, and a refusal then has no request to answer - it is reported here instead. The run stays SCHEDULED and is retried, so resolving the cause is enough to make it go.
"Insufficient minutes available to schedule 10 interviews (50 minutes total). Please purchase additional minutes."