Get Result Rows
curl --request GET \
--url https://agentos.useparagon.com/api/tools/projects/{projectId}/results/{resultId}/rows \
--header 'Authorization: Bearer <token>'import requests
url = "https://agentos.useparagon.com/api/tools/projects/{projectId}/results/{resultId}/rows"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://agentos.useparagon.com/api/tools/projects/{projectId}/results/{resultId}/rows', 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://agentos.useparagon.com/api/tools/projects/{projectId}/results/{resultId}/rows",
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://agentos.useparagon.com/api/tools/projects/{projectId}/results/{resultId}/rows"
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://agentos.useparagon.com/api/tools/projects/{projectId}/results/{resultId}/rows")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://agentos.useparagon.com/api/tools/projects/{projectId}/results/{resultId}/rows")
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": "1",
"name": "Open issue"
}
],
"paging": {
"pageSize": 50,
"continuationToken": null
},
"matched": 1,
"tokenBudget": {
"fullOutputTokens": 20,
"returnedTokens": 8,
"savedTokens": 12
}
}{
"error": {
"code": "REQUEST_INVALID",
"message": "The request payload failed validation.",
"hint": "Correct the fields named in details and retry.",
"retryable": false,
"requestId": "req_01hxyz",
"details": [
{
"field": "query",
"issue": "query should not be empty"
}
]
}
}{
"error": {
"code": "REQUEST_INVALID",
"message": "The request payload failed validation.",
"hint": "Correct the fields named in details and retry.",
"retryable": false,
"requestId": "req_01hxyz",
"details": [
{
"field": "query",
"issue": "query should not be empty"
}
]
}
}{
"error": {
"code": "REQUEST_INVALID",
"message": "The request payload failed validation.",
"hint": "Correct the fields named in details and retry.",
"retryable": false,
"requestId": "req_01hxyz",
"details": [
{
"field": "query",
"issue": "query should not be empty"
}
]
}
}Tools
Get Result Rows
Filter and page through records in a stored tool result.
GET
/
api
/
tools
/
projects
/
{projectId}
/
results
/
{resultId}
/
rows
Get Result Rows
curl --request GET \
--url https://agentos.useparagon.com/api/tools/projects/{projectId}/results/{resultId}/rows \
--header 'Authorization: Bearer <token>'import requests
url = "https://agentos.useparagon.com/api/tools/projects/{projectId}/results/{resultId}/rows"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://agentos.useparagon.com/api/tools/projects/{projectId}/results/{resultId}/rows', 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://agentos.useparagon.com/api/tools/projects/{projectId}/results/{resultId}/rows",
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://agentos.useparagon.com/api/tools/projects/{projectId}/results/{resultId}/rows"
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://agentos.useparagon.com/api/tools/projects/{projectId}/results/{resultId}/rows")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://agentos.useparagon.com/api/tools/projects/{projectId}/results/{resultId}/rows")
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": "1",
"name": "Open issue"
}
],
"paging": {
"pageSize": 50,
"continuationToken": null
},
"matched": 1,
"tokenBudget": {
"fullOutputTokens": 20,
"returnedTokens": 8,
"savedTokens": 12
}
}{
"error": {
"code": "REQUEST_INVALID",
"message": "The request payload failed validation.",
"hint": "Correct the fields named in details and retry.",
"retryable": false,
"requestId": "req_01hxyz",
"details": [
{
"field": "query",
"issue": "query should not be empty"
}
]
}
}{
"error": {
"code": "REQUEST_INVALID",
"message": "The request payload failed validation.",
"hint": "Correct the fields named in details and retry.",
"retryable": false,
"requestId": "req_01hxyz",
"details": [
{
"field": "query",
"issue": "query should not be empty"
}
]
}
}{
"error": {
"code": "REQUEST_INVALID",
"message": "The request payload failed validation.",
"hint": "Correct the fields named in details and retry.",
"retryable": false,
"requestId": "req_01hxyz",
"details": [
{
"field": "query",
"issue": "query should not be empty"
}
]
}
}Reads a
Filtering with
Values are not trimmed, quoted, or split on commas, so
To express an OR, make one request for each alternative and merge the results in your agent.
records-shaped result. You can use the following query parameters to filter, trim, and page through the records:
| Query | Meaning |
|---|---|
pointer | JSON Pointer (/ prefix) into the stored value. Defaults to the envelope recordsPointer. Depth is at most 8. |
select | Comma-separated field names to return for each record, at most 32. |
where | Repeatable filter clause in the format field:operator:value, at most 8. See Filtering with where. |
pageSize | Integer from 1 to 100. Default 50. |
continuationToken | paging.continuationToken from the previous page. |
const params = new URLSearchParams({
select: "id,name",
pageSize: "50",
});
params.append("where", "status:eq:open");
const response = await fetch(
`https://agentos.useparagon.com/api/tools/projects/${projectId}/results/${resultId}/rows?${params}`,
{ headers: { Authorization: `Bearer ${paragonUserToken}` } },
);
Filtering with where
where does not use a query language like SQL or OData. Each filter is a single colon-separated string, and you pass one where query parameter per filter.
Clause format
field:operator:value
| Part | Description |
|---|---|
field | The name of a top-level key on each record, such as status or Amount. Field names are case-sensitive. Nested paths like owner.name are not supported, and a field name cannot contain a :. |
operator | One of the operators below. Operators are lowercase and case-sensitive. |
value | The value to compare against. Always written as text, with no quotes. Everything after the second : is the value, so values can contain colons, such as createdAt:gte:2026-01-01T00:00:00Z. Omit it for exists. |
name:eq: Acme matches a leading space. Remember to URL-encode each clause when you build the query string. URLSearchParams does this for you.
Combining filters
Repeat thewhere parameter to add more filters. A record is returned only if it matches every clause (logical AND). There is no OR or NOT, and you can use at most 8 clauses per request.
GET .../rows?where=status:eq:open&where=amount:gte:1000&where=name:contains:Acme
Operators
| Operator | Matches when | Needs a value |
|---|---|---|
eq | The field equals the value. | Yes |
ne | The field does not equal the value. Records that are missing the field also match. | Yes |
gt | The field is greater than the value. | Yes |
gte | The field is greater than or equal to the value. | Yes |
lt | The field is less than the value. | Yes |
lte | The field is less than or equal to the value. | Yes |
contains | The field, read as text, contains the value as a case-sensitive substring. | Yes |
exists | The field is present and is not null. | No |
How values are compared
Because every value in the query string is text, the comparison depends on what the field and the value look like:- Numbers. If a field is a number, or a string that parses as a number (like
"10"or"1e3"), and the value also parses as a number, they are compared numerically.Amount:eq:10.0matches10, andAmount:gt:9matches"10". Numeric strings are converted to numbers, so identifiers like"00123"and"123"are treated as equal. - Strings. If either side is not numeric,
eqandnecompare the text exactly (case-sensitive).gt,gte,lt, andltecompare strings alphabetically when both sides are non-numeric. This works for ISO 8601 timestamps in the same format and time zone, such ascreatedAt:gte:2026-01-01T00:00:00Z, but it does not work for formats that don’t sort alphabetically, like01/02/2026. - Booleans. Compare against the text
trueorfalse, such asarchived:eq:false. Ordering operators never match booleans. - Empty and missing values.
gt,gte,lt, andltenever match when the field is missing,null, empty, a boolean, an object, or an array, or when the value is empty or can’t be compared to the field (such asAmount:gt:abcagainst a numeric field).containsnever matches a missing ornullfield, or an empty value.eqwith an empty value (name:eq:) only matches fields that are an empty string. - Objects and arrays. Comparison operators are meant for scalar fields. Use
existsto check that a nested value is present, and useselectto read it.
Filtering and selecting together
Filters run beforeselect and paging, so you can filter on a field you do not return. select only controls which fields appear in each record in data; fields that are not on a record are omitted.
const params = new URLSearchParams({ select: "id,name", pageSize: "25" });
params.append("where", "status:eq:open");
params.append("where", "amount:gte:1000");
params.append("where", "closedAt:exists");
Paging through filtered results
matched is the total number of records that match your filters across all pages, not only the page returned. When paging.continuationToken is not null, there are more matches. Pass it as continuationToken on the next request.
A continuation token is tied to the pointer, select, and where clauses (in the same order) of the request that created it. If you change any of them, the request fails with RESULT_SLICE_INVALID. Start a new query without a token when you want different filters.
Errors
An invalid request returns400 with the error code RESULT_SLICE_INVALID and a message describing the problem. Common causes:
| Message | Cause |
|---|---|
Invalid where clause "..." | The clause is missing a field or an operator, such as status or :eq:open. |
Unknown filter operator "..." | The operator is not one of the eight listed above, or is not lowercase. |
where exceeds 8 clauses. | More than 8 where parameters were sent. |
The continuation token is invalid. | The token was altered, or was created with a different pointer, select, or where. |
Authorizations
Your Paragon User Token (JWT), which you can generate using your project's signing keys.
Query Parameters
RFC 6901 JSON Pointer. Must start with /.
Comma-separated fields, at most 32.
Repeatable filter in the format field:operator:value, at most 8. Clauses are combined with AND. field is a top-level record key. Operators (lowercase): eq, ne, gt, gte, lt, lte, contains, exists. Everything after the second colon is the value, and exists takes no value. Example: status:eq:open.
Required range:
1 <= x <= 100