# 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](https://github.com/Axiant-Labs/adolescent_social/blob/main/docs/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`

```json
{
  "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:

```ts
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.

## 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-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

```bash
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`).
