Skip to main content
Version: Latest
UPDATED 2026-11-03 — Breaking: posts no longer carry interestStatus. Stop reading it and don't show a tick or any "active" state on Interested / Not interested. Both options are one-shot actions now: show the undo toast, and call PUT /api/posts/:id/interest only after the toast closes without undo. Undo sends nothing, so it never needs a reverse call (it used to flip to interested, which was wrong).
```bashTOKEN=... # from POST /api/auth/login (alice@example.com / Password123)TOKEN=... # from POST /api/auth/login (use your own test account)

Post Interest API — Mobile Integration Guide

Interested / Not Interested on posts: private, tri-state recommendation feedback that reshapes the user's feed. This doc is for the mobile (React Native) client. Feed internals: feed-algorithm.md.

What's new​

  • 2026-10-08 — Breaking: posts no longer carry interestStatus. Stop reading it and don't show a tick or any "active" state on Interested / Not interested. Both options are one-shot actions now: show the undo toast, and call PUT /api/posts/:id/interest only after the toast closes without undo. Undo sends nothing, so it never needs a reverse call (it used to flip to interested, which was wrong).

Scope​

This is recommendation feedback only — "show me more / fewer posts like this." It is not a generic hide-post, mute-creator, or seen-tracking mechanism; those would be separate features. One state per (user, post).

Endpoint​

PUT /api/posts/:id/interest
Authorization: Bearer <accessToken>
Content-Type: application/json

{ "status": "interested" | "not_interested" | null }

null clears any previous state. Idempotent — sending the current state again is a no-op.

Response 200​

{
"success": true,
"data": {
"status": "not_interested",
"updatedAt": "2026-07-08T09:12:33.000Z"
}
}

Errors​

CodeWhen
400Post is the caller's own (Cannot set interest on your own post)
401Missing/expired token
404Post not found, or not visible to the caller
429Rate limited (30 calls / 10 s)

Reading state​

There is none. Posts do not carry the viewer's interest state; the menu never shows a tick or an active option. Repeating a call is harmless: setting the state the post already has is a no-op on the server (no double-counting), so the client doesn't need to know the current state. Don't show the options on the viewer's own posts (author.id === me).

What it does to the feed​

  • not_interested — the post is permanently excluded from every future feed build for this user (all pools and fallback tiers). Clearing the state makes it eligible again.
  • Both states — the viewer's topic-interest weights shift immediately (one explicit tap ≈ 10 likes' worth of signal): interested boosts the post's topics, not_interested suppresses them. The next feed rebuild reflects it — no sync-cycle delay.
  • Reversible — signals track the user's current preference, not accumulated taps. Flipping interested → not_interested fully undoes the boost and applies the suppression; clearing undoes whichever was set.
  • Reposts: marking a repost affects only that repost row, not the original.
  • Privacy: the author is never notified; no public counts exist.
  • Post overflow ("…") menu on non-own posts: "Interested" / "Not interested". Plain actions — no checkmark, no toggle.
  • Commit after the undo window, not on tap. Tap → show the toast with Undo. Undo tapped → restore the local UI, send nothing. Toast closes without undo → send the PUT once. No reverse call is ever needed.
  • On not_interested: optimistically remove the post from the local feed list, show the toast ("You'll see fewer posts like this"). After the PUT succeeds you may refetch feed page 1 without a cursor — a no-cursor GET /api/feed rebuilds the server-side snapshot with the exclusion applied. Continuing to page an old cursor is also safe — snapshot pages are frozen and deduped, so the post cannot resurface mid-scroll.
  • On interested: toast ("You'll see more posts like this"). Nothing changes on the card.
  • On error: put a hidden post back; no other UI to roll back.
  • null (clear) still works but the client has no use for it under this UX.

Examples​

TOKEN=... # from POST /api/auth/login (use your own test account)

# Mark not interested
curl -X PUT localhost:3001/api/posts/<POST_ID>/interest \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"status":"not_interested"}'

# Flip to interested
curl -X PUT localhost:3001/api/posts/<POST_ID>/interest \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"status":"interested"}'

# Clear (not used by the app UX; kept for completeness)
curl -X PUT localhost:3001/api/posts/<POST_ID>/interest \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"status":null}'

# Verify exclusion — id must be absent after marking not_interested
curl -s localhost:3001/api/feed -H "Authorization: Bearer $TOKEN" \
| jq '[.data.items[].id] | index("<POST_ID>")'

Swagger UI: http://localhost:3001/api/docs (Posts → PUT /api/posts/{id}/interest).

⤓ Download .md