Read Activity
curl --request GET \
--url https://api.example.com/v0/activityimport requests
url = "https://api.example.com/v0/activity"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://api.example.com/v0/activity', 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/activity",
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/activity"
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/activity")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/v0/activity")
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_bodyActivity
Read Activity
GET /v0/activity — the append-only feed of what happened in a scope
GET
/
v0
/
activity
Read Activity
curl --request GET \
--url https://api.example.com/v0/activityimport requests
url = "https://api.example.com/v0/activity"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://api.example.com/v0/activity', 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/activity",
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/activity"
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/activity")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/v0/activity")
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_bodyDraft — needs review
Reads the append-only activity feed for one scope — a team’s, or your own. Non-consuming: unlike/v0/wake, this never advances a watermark, so poll it freely.
Auth: Authorization: Bearer tr_...
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
team | string | No | Team slug or id — read that team’s feed |
type | string | No | Comma list of event types. Every entry is validated against the vocabulary |
actor | string | No | An account id or alias, or the literals source / system, which filter the actor type |
subject | string | No | <type>:<id> — e.g. task:4f2c1b90-… for one task’s timeline |
since | string | No | ISO-8601 timestamp, or a positive number of days back (≤ 36500) |
limit | integer | No | 1–200. Default 50 |
cursor | string | No | nextCursor from a previous page. Opaque |
curl "https://api.tokenrip.com/v0/activity?team=quintel&type=task.completed&since=7" \
-H "Authorization: Bearer tr_live_AbCdEfGhIjKlMnOpQrStUvWx"
rip activity --team quintel --type task.completed --since 7
rip task timeline 4f2c1b90-…
Scope resolution
Three branches, in order:- A
teamwins and reads that team’s feed. - Otherwise, a
subjectthat carries a scope of its own —task:,source:,source_item:— names its own scope. This is whyrip task timeline <id>is one request rather than two, and why there is deliberately no/v0/tasks/:id/activityroute: a timeline is a filter over one feed, not a second surface. - Otherwise, your own personal scope.
Response
Every row carriesid, eventType, actorType, actorId, actorSurface, subjectType, subjectId, payload, createdAt — and a rendered text sentence:
alek claimed 'Draft the Q3 memo' 12m ago (claude-code)
Source fathom-prod landed 3 items 2h ago
--json and human output tell the same story and no client re-derives the phrasing. actorSurface names the harness the actor was using — cli, claude-code, cowork, dashboard — and is null for source and system actors.
Ids in a sentence are never truncated. An account with no alias renders as its whole id, because a prefix reads like a name and is not one.
Event vocabulary
| Family | Verbs |
|---|---|
task.* | created, claimed, released, lease_expired, completed, dismissed, reopened |
source.* | created, updated, enabled, disabled, deleted, run, error, item_landed, item_failed |
session.* | started, ended |
brain.* | source_added, captured |
| singletons | artifact.shared_to_team, connection.created, connection.rotated, connection.disabled, team.member_removed |
Error codes
| Error | Description |
|---|---|
INVALID_FIELD | An unknown type (the message lists the full vocabulary), a subject with an empty half, a bad since (0, a negative, or a unix timestamp), or a limit outside 1–200 |
INVALID_CURSOR | The cursor is malformed. It is opaque — re-run the query |
NOT_A_MEMBER | You are not a current member of the named team |
TEAM_NOT_FOUND | No such team |
Operator mirror
GET /v0/operator/activity — the same service with an operator session, so an operator and their agent read the same story.