Skip to content

feat(api): Page the conversation list with a signed cursor - #2232

Draft
sentry-junior[bot] wants to merge 3 commits into
mainfrom
feat/conversation-list-cursor
Draft

sentry-junior[bot] wants to merge 3 commits into
mainfrom
feat/conversation-list-cursor

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

GET /api/conversations returns at most 50 root conversations, and a client cannot read more. This is PR 1 of #2231.

  • New optional cursor query param. Without it, the request behaves as before.
  • New optional nextCursor response field. It is set only when more rows remain. Existing clients ignore it.
  • The cursor holds the lastActivityAt and conversationId of the last row. This is the same order the list already uses (lastActivityAt DESC, conversationId ASC). It is HMAC-signed like the event cursor, with its own context string. An invalid cursor returns 400.
  • The feed reads limit + 1 rows to learn whether another page exists.
curl -s -H "Authorization: Bearer $JUNIOR_API_TOKEN" "$JUNIOR_URL/api/conversations?cursor=$NEXT"

Notes for review:

  • Allow listing all active conversations with dashboard and Slack links #2231 proposed to reject cursor with q. Search uses the same order, so the cursor works with q and codeRepositoryId. We decided not to add the restriction.
  • The cursor holds only a position, not the filters. Membership and access checks run on every page, so a mismatched cursor cannot expose other data. The README tells clients to send the same filters on each page. We decided that binding filters into the cursor is not worth the code for now.
  • lastActivityAt only moves forward (greatest(...) on upsert). A conversation with new activity during paging moves above the cursor, so later pages skip it. Pages do not return duplicates.
  • The cursor keeps lastActivityAt in milliseconds. All writers set it from JS Date values, so the stored value matches exactly.
  • The event cursor format and context string do not change, so existing event cursors stay valid.

Tests: the linked-user test in tests/integration/api/conversations/list.test.ts now follows cursors with limit: 1 across a tie in lastActivityAt. The route test checks the 400 for a bad cursor. I checked that the tie case fails when the conversationId comparison is reversed.

via @dingsdax.

--

View Junior Session [Sentry]

sentry-junior Bot and others added 2 commits October 26, 2026 18:42
GET /api/conversations returns at most 50 root conversations and had no
way to read more. Add an optional cursor query param and an optional
nextCursor response field. Without cursor the request behaves as before.

The cursor holds the lastActivityAt and conversationId of the last row,
which match the existing list order, and is HMAC-signed like the event
cursor. An invalid cursor returns 400.

Refs #2231

Co-Authored-By: Johannes Daxboeck <114897+dingsdax@users.noreply.github.com>
Co-Authored-By: Johannes Daxboeck <114897+dingsdax@users.noreply.github.com>
@vercel

vercel Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
junior-docs Ready Ready Preview Oct 26, 2026 7:01pm UTC

Request Review

The cursor fields pushed schema/conversation.ts past the 1,000-line
limit. Conversation stats are a separate resource
(/api/conversations/stats), so move their schemas and types to
schema/conversation-stats.ts. The public schema exports do not change.

This branch was successfully deployed

1 active deployment
Preview – junior-docs — c0835e74 Deployed Oct 26, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk: high PR risk score: high

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant