curl --request GET \
--url https://api.raydocs.com/workspaces/{workspaceId}/workflows/{workflowId}/runs \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.raydocs.com/workspaces/{workspaceId}/workflows/{workflowId}/runs"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.raydocs.com/workspaces/{workspaceId}/workflows/{workflowId}/runs', 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.raydocs.com/workspaces/{workspaceId}/workflows/{workflowId}/runs",
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.raydocs.com/workspaces/{workspaceId}/workflows/{workflowId}/runs"
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.raydocs.com/workspaces/{workspaceId}/workflows/{workflowId}/runs")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.raydocs.com/workspaces/{workspaceId}/workflows/{workflowId}/runs")
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{
"data": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"workflow_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"status": "pending",
"created_at": "2023-11-07T05:31:56Z",
"workflow_version_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"trigger_node_id": "<string>",
"trigger_type": "<string>",
"completed_at": "2023-11-07T05:31:56Z"
}
],
"meta": {
"server_time": "2023-11-07T05:31:56Z",
"next_cursor": "<string>",
"has_more": true
}
}List Workflow Runs
List workflow runs with page-number pagination or reliable completed-run cursor discovery.
curl --request GET \
--url https://api.raydocs.com/workspaces/{workspaceId}/workflows/{workflowId}/runs \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.raydocs.com/workspaces/{workspaceId}/workflows/{workflowId}/runs"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.raydocs.com/workspaces/{workspaceId}/workflows/{workflowId}/runs', 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.raydocs.com/workspaces/{workspaceId}/workflows/{workflowId}/runs",
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.raydocs.com/workspaces/{workspaceId}/workflows/{workflowId}/runs"
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.raydocs.com/workspaces/{workspaceId}/workflows/{workflowId}/runs")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.raydocs.com/workspaces/{workspaceId}/workflows/{workflowId}/runs")
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{
"data": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"workflow_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"status": "pending",
"created_at": "2023-11-07T05:31:56Z",
"workflow_version_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"trigger_node_id": "<string>",
"trigger_type": "<string>",
"completed_at": "2023-11-07T05:31:56Z"
}
],
"meta": {
"server_time": "2023-11-07T05:31:56Z",
"next_cursor": "<string>",
"has_more": true
}
}pagination=cursor, the endpoint preserves its existing page-number response and created_at DESC order.
Authentication and scope
Cursor mode requires bothworkspaces-read and workflows-read. The token owner must be an administrator of the workflow workspace.
Cursor mode request
cursor.completed. Other statuses are rejected in cursor mode.created_at strictly greater than this activation boundary.meta.next_cursor. Do not decode or modify it.curl --get "https://api.raydocs.com/workspaces/12/workflows/7d3a20e1-1e99-4eb0-9ab7-39ee5028caf4/runs" \
--header "Authorization: Bearer $RAYDOCS_API_TOKEN" \
--data-urlencode "pagination=cursor" \
--data-urlencode "status=completed" \
--data-urlencode "created_after=2026-07-21T10:00:00.000Z" \
--data-urlencode "per_page=100"
Cursor mode response
Cursor mode always excludes debug runs and incomplete runs. Results use the stable ordercompleted_at ASC, id ASC. The API holds back the current open second so no run can complete later behind an emitted checkpoint.
{
"data": [
{
"id": "75058f1e-f44e-46de-93f2-8c6889663be3",
"workflow_id": "7d3a20e1-1e99-4eb0-9ab7-39ee5028caf4",
"status": "completed",
"completed_at": "2026-07-21T12:34:55.000000Z",
"created_at": "2026-07-21T10:05:00.000000Z"
}
],
"meta": {
"server_time": "2026-07-21T12:34:55Z",
"next_cursor": "eyJjb21wbGV0ZWRfYXQiOi4uLn0",
"has_more": false
}
}
next_cursor is a durable checkpoint, including when has_more is false. Store it only after you have durably recorded every run on the page. The cursor contains the last (completed_at, id) position, encrypted and signed by the API.
Results advance strictly in completed_at ASC, id ASC order. Runs created after activation remain eligible until they complete, so a slow run appears on a later poll when its completion time moves beyond the stored checkpoint. Use only next_cursor as the client checkpoint and keep workflow_id:run_id:documentId as an idempotency key for client retries.
created_after fixed to the workflow activation time. Direct activation boundaries are locked by the first cursor. Do not replace the boundary with the last run’s creation or completion time.Activate without downloading history
- Send a bootstrap cursor request with
created_after=1970-01-01T00:00:00Zand ignore itsdata. - Store
meta.server_timeas the workflow’s immutableenabled_atvalue. - Store
meta.next_cursoras the initial checkpoint. - Use
created_after=enabled_atand the stored cursor for every later poll. The cursor accepts this one forward movement from the bootstrap boundary and skips the rest of the ignored history. - Follow pages while
has_more=true.
server_time before executing the bootstrap query. A run created before enabled_at stays excluded even if it finishes later. A run created after enabled_at remains eligible and appears when its status becomes completed, regardless of how long it takes.
Page-number compatibility
Whenpagination is omitted, the endpoint keeps the existing behavior:
- page-number pagination with 30 runs per page by default
- newest
created_atfirst - optional status filter
- optional
include_debug=1 - existing Laravel pagination metadata
Authorizations
Query Parameters
Set to cursor to activate cursor mode. Omit it to preserve page-number pagination.
cursor Page number in the existing page-number mode. Ignored in cursor mode.
x >= 1Required as completed in cursor mode. Other supported statuses remain available in page-number mode.
pending, running, paused, completed, failed, cancelled Required RFC 3339 activation boundary in cursor mode. Only runs with created_at strictly after this value are returned. It is immutable after the first cursor, except for the documented one-time transition from the epoch bootstrap boundary.
Opaque checkpoint returned in meta.next_cursor. Do not decode or modify it.
Include internal debug runs in page-number mode. Cursor mode always excludes them.
Number of runs per page. Defaults to 30 in page-number mode and 100 in cursor mode.
1 <= x <= 100