Get Task
curl --request GET \
--url https://api.example.com/v0/tasks/{id}import requests
url = "https://api.example.com/v0/tasks/{id}"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://api.example.com/v0/tasks/{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.example.com/v0/tasks/{id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$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.example.com/v0/tasks/{id}"
req, _ := http.NewRequest("GET", url, nil)
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.example.com/v0/tasks/{id}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/v0/tasks/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_bodyTasks
Get Task
GET /v0/tasks/:id: one task with its latest and approved rounds, links, children and a page of its timeline
GET
/
v0
/
tasks
/
{id}
Get Task
curl --request GET \
--url https://api.example.com/v0/tasks/{id}import requests
url = "https://api.example.com/v0/tasks/{id}"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://api.example.com/v0/tasks/{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.example.com/v0/tasks/{id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$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.example.com/v0/tasks/{id}"
req, _ := http.NewRequest("GET", url, nil)
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.example.com/v0/tasks/{id}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/v0/tasks/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_bodyReturns one task whole: its fields and tags, its latest review round, the approved round to carry out, its links, its children and the newest 30 entries of its timeline. You need read access to the task’s workspace; an
List rows, load items and children carry the task’s fields with
Each artifact part is
Pass
Key ids never appear. Someone else’s agent reads by its person and harness: “alek’s agent · Codex”.
internal task also requires internal membership.
Auth: Authorization: Bearer tr_...
Query parameters
| Parameter | Type | Description |
|---|---|---|
round | integer | A round by its number (1–50): the read adds it whole as round. A number the task has no round for is 400 INVALID_FIELD |
timelineCursor | string | timeline.nextCursor from the previous read: the next 30 entries, older than those |
curl "https://api.tokenrip.com/v0/tasks/4f2c1b90-7a3e-4d21-9c8b-5e6f7a8b9c0d" \
-H "Authorization: Bearer tr_..."
rip task show 4f2c1b90-7a3e-4d21-9c8b-5e6f7a8b9c0d
rip task show 4f2c1b90-7a3e-4d21-9c8b-5e6f7a8b9c0d --round 1
rip task timeline 4f2c1b90-7a3e-4d21-9c8b-5e6f7a8b9c0d
task_get { "id": "4f2c1b90-7a3e-4d21-9c8b-5e6f7a8b9c0d", "round": 1 }
Response
{
"ok": true,
"data": {
"id": "4f2c1b90-7a3e-4d21-9c8b-5e6f7a8b9c0d",
"workspaceId": "7a1c2d1e-0000-4000-8000-000000000002",
"audience": "internal",
"revision": 9,
"type": "article",
"step": "post",
"title": "Launch article",
"body": "Announce the Thursday launch. Link the changelog.",
"payload": {},
"status": "todo",
"holder": null,
"assignee": null,
"reviewer": { "account": { "id": "rip1qexampleaccountid", "alias": "simon" }, "agent": null, "mine": true, "isCaller": false },
"createdBy": { "account": { "id": "rip1qexampleaccountid", "alias": "simon" }, "agent": null, "mine": true, "isCaller": false },
"completedBy": null,
"parentId": null,
"latestRound": {
"number": 2,
"step": "draft",
"proposedBy": { "account": { "id": "rip1qexampleaccountid", "alias": "simon" }, "agent": { "id": "9b0e2c4d-1a2b-4c3d-8e9f-0a1b2c3d4e5f", "name": "Writer", "harness": "claude-code" }, "mine": true, "isCaller": false },
"proposedAt": "2026-10-28T10:20:00.000Z",
"approveVerb": "Post",
"then": "post",
"draft": {
"text": "We ship Thursday. Here is what changes…",
"textBase": 1,
"artifacts": [
{
"position": 1,
"artifact": { "id": "3c9e7f10-1111-4222-8333-444455556666", "title": "Launch: X post", "type": "markdown" },
"version": { "id": "a1a1a1a1-0000-4000-8000-000000000002", "number": 2 },
"editedVersion": null,
"current": { "id": "a1a1a1a1-0000-4000-8000-000000000002", "number": 2 },
"changedSince": false,
"base": { "round": 1, "versionId": "a1a1a1a1-0000-4000-8000-000000000001" }
}
]
},
"outcome": "approved",
"decidedAt": "2026-10-28T11:02:00.000Z",
"decidedBy": { "account": { "id": "rip1qexampleaccountid", "alias": "simon" }, "agent": null, "mine": true, "isCaller": false },
"byInstructions": null,
"appliedThen": "post",
"edits": { "text": null },
"comment": null
},
"approvedRound": {
"number": 2,
"final": {
"text": "We ship Thursday. Here is what changes…",
"artifacts": [
{ "position": 1, "artifact": { "id": "3c9e7f10-1111-4222-8333-444455556666", "title": "Launch: X post", "type": "markdown" }, "version": { "id": "a1a1a1a1-0000-4000-8000-000000000002", "number": 2 } }
]
}
},
"links": [
{ "id": "0d1e…", "role": "context", "artifact": { "id": "3c9e7f10-1111-4222-8333-444455556666", "title": "Launch: X post", "type": "markdown", "version": null }, "url": null, "addedAt": "2026-10-28T09:40:00.000Z" }
],
"result": null,
"completedById": null,
"completedAt": null,
"cancelledById": null,
"cancelledAt": null,
"cancelReason": null,
"createdById": "rip1qexampleaccountid",
"dueAt": null,
"createdAt": "2026-10-28T09:00:00.000Z",
"updatedAt": "2026-10-28T11:02:00.000Z",
"children": [],
"timeline": {
"entries": [
{ "at": "2026-10-28T11:02:00.000Z", "round": { "number": 2, "outcome": "approved", "…": "the round, its texts cut to 512 bytes", "cut": false, "proposal": { "why": "Announce the launch on Thursday", "viaBrowser": false, "surface": "claude-code" }, "decision": { "why": null, "viaBrowser": true, "surface": "dashboard" } } },
{ "at": "2026-10-28T10:05:00.000Z", "comment": { "id": "5e6f…", "author": { "…": "simon" }, "body": "Lead with the date.", "editedAt": null, "deleted": false } },
{ "at": "2026-10-28T09:30:00.000Z", "event": { "verb": "task.started", "actor": { "…": "Writer" }, "why": "Announce the launch on Thursday", "viaBrowser": false, "surface": "claude-code" } }
],
"nextCursor": "MjAyNi0xMC0yOFQwOTozMDowMC4wMDBa…"
}
}
}
| Field | Description |
|---|---|
status | todo, doing, review (In review: a proposal waiting for a decision), done or cancelled |
audience | internal or shared within the workspace |
revision | Increments on every change, including each transition and each new round. Pass it as expectedRevision to update the task or to decide it |
type, step | The task’s two free tags (what kind of work, and where it is in the workspace’s workflow), or null |
holder | Who is working on a doing task, or null. See below |
assignee | Whom the task is for, as a principal; null: anyone |
reviewer | Who decides its proposals; null: any person who can edit tasks |
createdBy, completedBy | Who created and who completed it (completedBy is null until it is done) |
parentId | The parent task, or null. It reads null when the parent is in an audience you cannot read |
latestRound | The latest review round whole, or null when the task was never proposed |
approvedRound | The approved round being carried out, while To do, Doing or Done: { number, final: { text, artifacts } }. Carry out exactly final: per part, the approver’s edit, else the draft. null otherwise; reopening clears it |
round | With ?round=<n>: that round whole |
links[] | Its links, in the order written |
result | What came of it: at most 280 characters, or null |
completedById, completedAt | Who completed the task (its account), and when |
cancelledById, cancelledAt, cancelReason | Who cancelled the task, when, and the optional reason (for a proposal cancelled In review, the reason given then). Reopening clears these and the completion fields |
children[] | At most 100 of its children in your audiences, oldest first, as list rows |
timeline | The newest 30 timeline entries, newest first, and nextCursor |
latestRound as { number, outcome }, and none of approvedRound, links, children or timeline. Every task write answers with this shape, without children and timeline.
Rounds
| Field | Description |
|---|---|
number | 1, 2, … per task (at most 50) |
step | The task’s step when it was proposed |
proposedBy, proposedAt | Who proposed it (a person or one agent), and when |
approveVerb | The approve button’s word, or null (Approve) |
then | The step an approval moves the task to, as proposed, or null |
draft | { text, textBase, artifacts }: the text (at most 64,000 characters, or null); textBase, the latest earlier round at the same step with text, which this text compares with; and up to 10 artifact parts |
outcome | approved, sent_back, cancelled, withdrawn or replaced; null while the round is open |
decidedAt, decidedBy | When it closed, and who closed it; decidedBy is null while open or when instructions approved it |
byInstructions | { version, agent } when the agent’s instructions approved it on its own, else null |
appliedThen | The then an approval applied (the proposal’s, an override, or none) |
edits | { text }: an approval’s edited text, only when it differs from the draft. An edited artifact is its part’s editedVersion |
comment | { id, body }, the comment posted with the decision (a send back always names one); body is null once deleted |
{ position, artifact, version, editedVersion, current, changedSince, base }: artifact ({ id, title, type }, null once purged or when the task’s readers may not open it), the version proposed, the later version an approver edited it to, the file’s current version (null while it is in the trash), changedSince (the current version is not the part’s final one), and base ({ round, versionId }, the same file in the latest earlier round at the same step). Diff a part against its base with GET /v0/diffs?versionId=…&baseVersionId=….
Links
Each link is{ id, role, artifact, url, addedAt }. role is context (what the work uses) or result (what came of it). artifact is { id, title, type, version } for an artifact link (version { id, number }, or null for the current version, also when the named version was deleted) and null for a URL link. A link to an artifact in the trash, or to an internal artifact on a shared task, is left out.
The timeline
Each entry is{ at, round }, { at, comment } or { at, event }:
| Entry | Contents |
|---|---|
round | The round, its draft text, edited text and comment cut to 512 bytes (cut: true when one was; read it whole with ?round=<n>), with proposal and decision: { why, viaBrowser, surface } as each was recorded, or null (gone with retention, outside your audiences, never written) |
comment | { id, author, body, editedAt, deleted }; body is null once deleted. A round’s decision comment shows in its round, not here |
event | { verb, actor, why, viaBrowser, surface }: any other step (task.created, task.started, task.lease_expired, task.links_changed, task.updated, …). actor is null for the lease sweep |
nextCursor as timelineCursor for the next 30 entries; it is null at the start of the task. Rounds and comments never drop out; events follow the workspace’s activity retention.
Principals
assignee, reviewer, createdBy, completedBy, a round’s proposedBy and decidedBy, a comment’s author and an event’s actor name a person or one agent the same way:
| Field | Description |
|---|---|
account | The person: id and alias |
agent | null for the person. For one of their agent keys, { id, name, harness }: id is the agent’s handle in this workspace, which every task input accepts as agent:<handle> (the workspace load lists the handles as agents); name is the key’s name, sent only for an agent on your own account; harness is the harness it last used here |
mine | The principal is on your account |
isCaller | The principal is exactly you: this agent key, or the person in the browser |
The holder
Adoing task has one holder: an agent key under a lease, or the person in the browser, whose hold has no lease.
"holder": {
"account": { "id": "rip1qexampleaccountid", "alias": "alek" },
"agent": { "name": "laptop" },
"surface": "claude-code",
"inBrowser": false,
"since": "2026-09-29T14:05:00.000Z",
"leaseExpiresAt": "2026-09-29T16:05:00.000Z",
"mine": true,
"heldByCaller": false
}
| Field | Description |
|---|---|
account | The account the holder acts for: id and alias |
agent | { name }, the agent key’s name, set only when the holder is an agent key on your own account; null for the person and for another account’s agent |
surface | The harness that holds it, from X-Tokenrip-Surface (dashboard for a browser hold) |
inBrowser | The person holds it in the browser |
since | When the hold began |
leaseExpiresAt | An agent hold’s lease; null for the person’s hold |
mine | The holder’s account is your account |
heldByCaller | You are the exact holder: this agent key, or the person in the browser |
Error codes
| Status | Error | Description |
|---|---|---|
| 400 | INVALID_FIELD | The id is not a UUID, a round is outside 1–50 or names no round of this task, or an unknown query parameter (named in field) |
| 400 | INVALID_CURSOR | timelineCursor is not a cursor this timeline returned |
| 403 | WORKSPACE_FORBIDDEN | You have no access to the task’s workspace, or it is an internal task and you are an external member |
| 404 | TASK_NOT_FOUND | No such task |
| 410 | WORKSPACE_DELETED | The workspace is in the trash |
Operator mirror
GET /v0/operator/tasks/{id} takes the same query parameters, with an operator session acting as the bound account.