Suggested follows.
Two-tier recommendation: friend-of-a-friend signal first, top-by-followers fallback for new accounts that don't have an FoF graph yet. The set is recomputed at most hourly, but follows and dismissals drop out of it immediately.
1
Read the suggestions
One endpoint, one shape. Returns up to limit candidates (default 20, max 50) ordered by signal strength. Each candidate carries a reason so the UI can label the row (“People you might know” vs “Popular in this community”). pagination is always null — a single bounded page, no cursor.
curl -H "Authorization: Bearer pcft_live_..." \
"https://social.productcraft.co/v1/communities/<communityId>/actors/<actorId>/suggested-follows?limit=10"{
"data": [
{
"actor": {
"id": "aaaaaaaa-...",
"external_id": "ada",
"display_name": "Ada Lovelace",
"avatar_url": null,
"status": "active",
"is_private": false,
"...": "..."
},
"score": 4,
"reason": "followed_by_friends"
},
{
"actor": { "id": "...", "external_id": "grace", "...": "..." },
"score": 1042,
"reason": "top_in_community"
}
],
"pagination": null
}2
How candidates are picked
- followed_by_friends: count, per candidate, of the requester's followees who follow that candidate. Ordered descending. The classic friend-of-a-friend graph signal — if 4 of the people you follow also follow Ada, Ada surfaces with
score: 4. - top_in_community: cold-start fallback when FoF doesn't fill the requested limit (or returns nothing for a brand-new account). Ranks candidates by their
follower_countfromactor_counter.scoreis the follower count.
Excluded
Who never appears
- The requester themselves.
- Actors the requester already follows.
- Actors the requester has muted or blocked.
- Actors who have blocked the requester back (visibility parity with the existing block-aware feed).
- Actors the requester dismissed via
POST /v1/communities/<c>/dismissed-suggestions({ "actor_id", "dst_actor_id" }) — the “don't suggest this person” action. Reversible with the matchingDELETE …/dismissed-suggestions/<src>/<dst>. - Suspended or deleted actors (status filter applied at the SQL layer).
Caching
What to expect
The candidate set is computed at most once an hour per (community, actor, limit). But every response runs through a read-time filter, so a follow or a dismissal takes effect immediately — the candidate disappears from the very next call. No refresh trick needed. Only a new mute or block waits for the hour to roll over.
The cache is per API process, so in the minute after a change different replicas can still answer with slightly different sets. Suggestions are inherently stale-tolerant — don't build UI that assumes two consecutive calls return an identical list.
Permissions
PAK scopes
- GET requires
social.listonpcft:agora:community/<communityId>. Member, admin, and owner system roles all have it by default.