Followers / following
Mirrors GitHub's followers REST surface: list followers and following for any user, plus the per-actor follow / unfollow edge management.
Scopes:
user:readon the GETsuser:writeon PUT/DELETE (mutating the authenticated user's follow edges)
Endpoints
GET /api/v1/users/{username}/followers followers list (paginated)
GET /api/v1/users/{username}/following following list (paginated)
GET /api/v1/user/following/{target} 204 / 404 membership probe
PUT /api/v1/user/following/{target} follow
DELETE /api/v1/user/following/{target} unfollow
Note: shithub's follow model also lets users follow orgs. This REST surface only exposes user→user follows; the org-follow variants stay on the HTML profile pages for now (post-MVP roadmap item).
List shapes
[
{
"user_id": 42,
"username": "bob",
"display_name": "Bob",
"followed_at": "2026-05-12T18:00:00Z"
}
]
Paginated with Link: headers (default per_page 30, max 100).
Membership probe
GET /api/v1/user/following/bob
Returns 204 No Content when the authenticated user follows
bob, 404 Not Found otherwise. No body either way — clients
inspect the status code directly (matches gh).
Follow / unfollow
PUT /api/v1/user/following/bob
DELETE /api/v1/user/following/bob
Both return 204 No Content and are idempotent — re-following or
re-unfollowing has no side effect beyond emitting the audit row.
The follow side fires a public followed_user domain event for
activity-feed surfaces.
Errors
404— target username doesn't exist (uniform envelope; can't enumerate via response shape).422—targetequals the authenticated user (cannot follow yourself).429— per-user follow/unfollow rate cap exceeded (200 events/hour by default).403— PAT lacks the required scope.
View source
| 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. |