Feed list
The caller's own feed, newest first — for browsing. projectId reads that board's activity, omitted reads account news, all merges every scope (rows carry unread). sinceSeen returns only what they have not caught up on. reader picks whose cursor: the human (user, default) or the orchestrator — each keeps its own.
query Parameters
projectIdstringallbooleanreadertierlimitinteger · min: 1 · max: 200beforeinteger · min: 0cursorinteger · min: 0sinceSeenboolean
Responses
Successful response
itemsobject[] · requiredunseennumber · requiredEvents after the reader's cursor — the unread count, whatever the page size.
seennumber · requiredThe reader's scalar base cursor. Rows carry
unreadthemselves: category cursors can advance past this.
nextCursornumberunseenByTierobjectConstruct a type with a set of properties K of type T
Feed drain
The oldest unseen events, oldest first — take them, relay them, then markSeen the last seq delivered. Paging newest-first and acking the top would bury everything older.
query Parameters
projectIdstringreaderlimitinteger · min: 1 · max: 200
Responses
Successful response
idstring · requiredscopestring · requiredA projectId, or 'acct' for news about the person rather than a board.
kindrequiredONE feed, one row shape, whatever the news is.
team_inviteis account-scoped and acted on from the bell; thetodo_*kinds are project-scoped and drained by the voice agent. Kind decides scope, audience and rendering — not which store it lives in.subjectstring · requiredWhat it's about — a todo id, an invite token. Dedupe and resolution match on this.
messagestring · requiredOne line, already human-readable: a digest never re-derives prose from fields.
seqnumber · requiredMonotonic position in the stream. Ordering and cursors use THIS, not
createdAt: timestamps tie when a run finishes several todos in one millisecond, and a cursor over tied scores silently skips events.createdAtnumber · required
titlestringHeadline, when the row is rendered in a list (the bell).
hrefstringIn-app route that acts on the subject (e.g.
/join/<token>).expiresAtnumberPast this the subject is dead; the row renders as expired.
resolvedAtnumberresolutionreadAtnumberWhen the user opened this row (bell click, toast, push tap) — read regardless of the cursor.
senderobjectteam = a person at TODO for AI; agent = the user's own agent; member = a teammate on a shared project (via their agent).
unreadbooleanPer-row human read state; category cursors cannot be represented by a single
seen.statusRevnumberTodo outcomes: the todo's
statusRevat this transition — the same rev its live status event carried.heardbooleanOrchestrator drain only: already relayed live (feed.markHeard covers this rev) — ack it, don't replay it.
Feed markHeard
The orchestrator already relayed this todo's outcome live, as of statusRev (from the status event). Orchestrator drain then flags that todo's rows up to it as heard, so catch_up doesn't repeat them.
Request Body
projectIdstring · minLength: 1 · requiredtodoIdstring · minLength: 1 · maxLength: 100 · requiredstatusRevinteger · min: 1 · required
Responses
Successful response
Feed message
Leave a note for your user in their Messages inbox (a tip, a heads-up). Shown as from your agent. Re-sending the same subject is a no-op (created: false). href must be an in-app path like /t/<id>. At most 10 new messages per day (repeats do not count).
Request Body
subjectstring · minLength: 1 · maxLength: 120 · requiredtitlestring · minLength: 1 · maxLength: 120 · requiredmessagestring · minLength: 1 · maxLength: 4000 · required
hrefstring · maxLength: 500 · pattern:^/(?![/\\])\S*$In-app path only: no scheme (javascript:, https:) and no protocol-relative
//host.
Responses
Successful response
idstring · requiredscopestring · requiredA projectId, or 'acct' for news about the person rather than a board.
kindrequiredONE feed, one row shape, whatever the news is.
team_inviteis account-scoped and acted on from the bell; thetodo_*kinds are project-scoped and drained by the voice agent. Kind decides scope, audience and rendering — not which store it lives in.subjectstring · requiredWhat it's about — a todo id, an invite token. Dedupe and resolution match on this.
messagestring · requiredOne line, already human-readable: a digest never re-derives prose from fields.
seqnumber · requiredMonotonic position in the stream. Ordering and cursors use THIS, not
createdAt: timestamps tie when a run finishes several todos in one millisecond, and a cursor over tied scores silently skips events.createdAtnumber · requiredcreatedboolean · required
titlestringHeadline, when the row is rendered in a list (the bell).
hrefstringIn-app route that acts on the subject (e.g.
/join/<token>).expiresAtnumberPast this the subject is dead; the row renders as expired.
resolvedAtnumberresolutionreadAtnumberWhen the user opened this row (bell click, toast, push tap) — read regardless of the cursor.
senderobjectteam = a person at TODO for AI; agent = the user's own agent; member = a teammate on a shared project (via their agent).
unreadbooleanPer-row human read state; category cursors cannot be represented by a single
seen.statusRevnumberTodo outcomes: the todo's
statusRevat this transition — the same rev its live status event carried.heardbooleanOrchestrator drain only: already relayed live (feed.markHeard covers this rev) — ack it, don't replay it.
Feed messageMember
Leave a note for another member of a project you are both on (by their account email). Lands in their inbox + phone push, shown as from you. Same subject = no-op (created: false). href must be an in-app path. At most 20 new teammate notes per day.
Request Body
projectIdstring · minLength: 1 · maxLength: 100 · requiredtostring · email · requiredsubjectstring · minLength: 1 · maxLength: 120 · requiredtitlestring · minLength: 1 · maxLength: 120 · requiredmessagestring · minLength: 1 · maxLength: 4000 · required
hrefstring · maxLength: 500 · pattern:^/(?![/\\])\S*$In-app path only: no scheme (javascript:, https:) and no protocol-relative
//host.
Responses
Successful response
idstring · requiredscopestring · requiredA projectId, or 'acct' for news about the person rather than a board.
kindrequiredONE feed, one row shape, whatever the news is.
team_inviteis account-scoped and acted on from the bell; thetodo_*kinds are project-scoped and drained by the voice agent. Kind decides scope, audience and rendering — not which store it lives in.subjectstring · requiredWhat it's about — a todo id, an invite token. Dedupe and resolution match on this.
messagestring · requiredOne line, already human-readable: a digest never re-derives prose from fields.
seqnumber · requiredMonotonic position in the stream. Ordering and cursors use THIS, not
createdAt: timestamps tie when a run finishes several todos in one millisecond, and a cursor over tied scores silently skips events.createdAtnumber · requiredcreatedboolean · required
titlestringHeadline, when the row is rendered in a list (the bell).
hrefstringIn-app route that acts on the subject (e.g.
/join/<token>).expiresAtnumberPast this the subject is dead; the row renders as expired.
resolvedAtnumberresolutionreadAtnumberWhen the user opened this row (bell click, toast, push tap) — read regardless of the cursor.
senderobjectteam = a person at TODO for AI; agent = the user's own agent; member = a teammate on a shared project (via their agent).
unreadbooleanPer-row human read state; category cursors cannot be represented by a single
seen.statusRevnumberTodo outcomes: the todo's
statusRevat this transition — the same rev its live status event carried.heardbooleanOrchestrator drain only: already relayed live (feed.markHeard covers this rev) — ack it, don't replay it.
Feed adminMessage
Send an authored message from the team to one user (by email). Lands in their Messages inbox with from as the sender name. Same subject = no-op (created: false). href must be an in-app path. Audited.
Request Body
emailstring · email · requiredfromstring · minLength: 1 · maxLength: 80 · requiredsubjectstring · minLength: 1 · maxLength: 120 · requiredtitlestring · minLength: 1 · maxLength: 120 · requiredmessagestring · minLength: 1 · maxLength: 4000 · required
hrefstring · maxLength: 500 · pattern:^/(?![/\\])\S*$In-app path only: no scheme (javascript:, https:) and no protocol-relative
//host.
Responses
Successful response
idstring · requiredscopestring · requiredA projectId, or 'acct' for news about the person rather than a board.
kindrequiredONE feed, one row shape, whatever the news is.
team_inviteis account-scoped and acted on from the bell; thetodo_*kinds are project-scoped and drained by the voice agent. Kind decides scope, audience and rendering — not which store it lives in.subjectstring · requiredWhat it's about — a todo id, an invite token. Dedupe and resolution match on this.
messagestring · requiredOne line, already human-readable: a digest never re-derives prose from fields.
seqnumber · requiredMonotonic position in the stream. Ordering and cursors use THIS, not
createdAt: timestamps tie when a run finishes several todos in one millisecond, and a cursor over tied scores silently skips events.createdAtnumber · requiredcreatedboolean · required
titlestringHeadline, when the row is rendered in a list (the bell).
hrefstringIn-app route that acts on the subject (e.g.
/join/<token>).expiresAtnumberPast this the subject is dead; the row renders as expired.
resolvedAtnumberresolutionreadAtnumberWhen the user opened this row (bell click, toast, push tap) — read regardless of the cursor.
senderobjectteam = a person at TODO for AI; agent = the user's own agent; member = a teammate on a shared project (via their agent).
unreadbooleanPer-row human read state; category cursors cannot be represented by a single
seen.statusRevnumberTodo outcomes: the todo's
statusRevat this transition — the same rev its live status event carried.heardbooleanOrchestrator drain only: already relayed live (feed.markHeard covers this rev) — ack it, don't replay it.