Private accounts & follow requests.
Flip an actor to private and their effective post visibility is capped at followers — even their public posts become readable only by approved followers, on every surface (feed, search, hashtags, single reads). Following a private actor creates a request the actor approves or declines.
1
Make an actor private
PATCH the actor with is_private: true. From then on, non-followers can't read any of the actor's posts — a public post is effectively clamped to followers, and reads by a non-follower 404 rather than 403. Flipping back to public auto-approves every pending request.
curl -X PATCH -H "Authorization: Bearer pcft_live_..." \
-H "Content-Type: application/json" -d '{"is_private": true}' \
"https://social.productcraft.co/v1/communities/<communityId>/actors/<actorId>"
# → { "id": "...", "is_private": true, ... }2
Following a private actor
POST /follows against a private destination doesn't create a follow edge — it creates a pending request and the response is { "status": "pending", "edge": null, "created": true } (public destinations return { "status": "followed", "edge": {…}, "created": true } as before). The private actor gets a follow_request notification and a edge.follow.requested webhook. No follower/following counters move until approval.
curl -X POST -H "Authorization: Bearer pcft_live_..." \
-H "Content-Type: application/json" \
-d '{"actor_id":"<follower>","dst_actor_id":"<private>"}' \
"https://social.productcraft.co/v1/communities/<communityId>/follows"
# → { "status": "pending", "edge": null, "created": true }3
Approve, decline, list
The private actor reviews incoming requests, then approves (creating the follow edge — counters bump, edge.follow.created fires, the requester gets a follow_request_approved notification) or declines (the request is dropped silently).
# List incoming requests (newest-first, cursor-paginated)
curl -H "Authorization: Bearer pcft_live_..." \
"https://social.productcraft.co/v1/communities/<communityId>/actors/<private>/follow-requests"
# → { "data": [ { "request_id": "...", "src_actor_id": "...", "src_external_id": "...",
# "src_display_name": "...", "src_avatar_url": null, "created_at": "..." } ],
# "pagination": { "next_cursor": null, "has_more": false } }
# Approve → 201 { "edge": {…}, "created": true }
curl -X POST -H "Authorization: Bearer pcft_live_..." \
"https://social.productcraft.co/v1/communities/<communityId>/follow-requests/<private>/<follower>/approve"
# Decline → 204
curl -X DELETE -H "Authorization: Bearer pcft_live_..." \
"https://social.productcraft.co/v1/communities/<communityId>/follow-requests/<private>/<follower>"4
Notes
GET /actors/{actorId}/relationships?with=<dst>returns afollow_pendingflag alongsidefollowing/blocking/ …, so you can render a “Requested” state.- Going public→private keeps existing followers — only new follows require approval. Going private→public auto-approves everything pending.
- Blocking someone tears down any pending request between the pair, both directions.
- To remove an existing follower, use the normal
DELETE /follows/{src}/{dst}— there's no separate remove-follower route.