@@ -0,0 +1,73 @@ |
| 1 | +# Followers / following |
| 2 | + |
| 3 | +Mirrors GitHub's followers REST surface: list followers and |
| 4 | +following for any user, plus the per-actor follow / unfollow |
| 5 | +edge management. |
| 6 | + |
| 7 | +Scopes: |
| 8 | + |
| 9 | +- `user:read` on the GETs |
| 10 | +- `user:write` on PUT/DELETE (mutating the authenticated user's |
| 11 | + follow edges) |
| 12 | + |
| 13 | +## Endpoints |
| 14 | + |
| 15 | +``` |
| 16 | +GET /api/v1/users/{username}/followers followers list (paginated) |
| 17 | +GET /api/v1/users/{username}/following following list (paginated) |
| 18 | +GET /api/v1/user/following/{target} 204 / 404 membership probe |
| 19 | +PUT /api/v1/user/following/{target} follow |
| 20 | +DELETE /api/v1/user/following/{target} unfollow |
| 21 | +``` |
| 22 | + |
| 23 | +Note: shithub's follow model also lets users follow **orgs**. |
| 24 | +This REST surface only exposes user→user follows; the org-follow |
| 25 | +variants stay on the HTML profile pages for now (post-MVP |
| 26 | +roadmap item). |
| 27 | + |
| 28 | +## List shapes |
| 29 | + |
| 30 | +```json |
| 31 | +[ |
| 32 | + { |
| 33 | + "user_id": 42, |
| 34 | + "username": "bob", |
| 35 | + "display_name": "Bob", |
| 36 | + "followed_at": "2026-05-12T18:00:00Z" |
| 37 | + } |
| 38 | +] |
| 39 | +``` |
| 40 | + |
| 41 | +Paginated with `Link:` headers (default per_page 30, max 100). |
| 42 | + |
| 43 | +## Membership probe |
| 44 | + |
| 45 | +``` |
| 46 | +GET /api/v1/user/following/bob |
| 47 | +``` |
| 48 | + |
| 49 | +Returns `204 No Content` when the authenticated user follows |
| 50 | +`bob`, `404 Not Found` otherwise. No body either way — clients |
| 51 | +inspect the status code directly (matches gh). |
| 52 | + |
| 53 | +## Follow / unfollow |
| 54 | + |
| 55 | +``` |
| 56 | +PUT /api/v1/user/following/bob |
| 57 | +DELETE /api/v1/user/following/bob |
| 58 | +``` |
| 59 | + |
| 60 | +Both return `204 No Content` and are idempotent — re-following or |
| 61 | +re-unfollowing has no side effect beyond emitting the audit row. |
| 62 | +The follow side fires a public `followed_user` domain event for |
| 63 | +activity-feed surfaces. |
| 64 | + |
| 65 | +## Errors |
| 66 | + |
| 67 | +- `404` — target username doesn't exist (uniform envelope; can't |
| 68 | + enumerate via response shape). |
| 69 | +- `422` — `target` equals the authenticated user (cannot follow |
| 70 | + yourself). |
| 71 | +- `429` — per-user follow/unfollow rate cap exceeded |
| 72 | + (200 events/hour by default). |
| 73 | +- `403` — PAT lacks the required scope. |