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.
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
| Code | When |
|---|---|
400 | Post is the caller's own (Cannot set interest on your own post) |
401 | Missing/expired token |
404 | Post not found, or not visible to the caller |
429 | Rate limited (30 calls / 10 s) |
Reading state
Every serialized post (feed items, post detail, profile lists) now carries:
interestStatus: "interested" | "not_interested" | null;
Drive the menu's active/check state from this field. Own posts always have
null and must not show the options.
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):
interestedboosts the post's topics,not_interestedsuppresses 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.
Recommended client UX
- Post overflow ("…") menu on non-own posts: "Interested" / "Not
interested". Tapping the currently active option sends
null(toggle-clear). - On
not_interested: optimistically remove the post from the local feed list, show a toast ("You'll see fewer posts like this"), then refetch feed page 1 without a cursor. A no-cursorGET /api/feedalways rebuilds the server-side snapshot, so the refetched feed already has 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: setinterestStatuslocally (menu checkmark), optional toast ("You'll see more posts like this"). - On error: roll back the optimistic change.
Examples
TOKEN=... # from POST /api/auth/login (alice@example.com / Password123)
# 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
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).