Skip to main content
Version: Latest

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

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

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): 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". 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-cursor GET /api/feed always 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: set interestStatus locally (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).

⤓ Download .md