Build a social app
06 · Home feed

One endpoint, two flavours.

`GET /actors/:actorId/feed` — chronological for a strict timeline, ranked for an algorithmic home tab. Same response shape, same cursor pagination, same block + visibility filtering.


1

Chronological feed

Time-ordered. Returns posts authored by the actor or by anyone they follow, excluding any actor involved in a block with them. private-visibility posts are surfaced only when the requester is the author.

bash
curl -H "Authorization: Bearer pcft_live_..." \
  "https://agora.productcraft.co/v1/communities/<c>/actors/<actor-uuid>/feed?order=chronological&limit=20"
response.json
{
  "data": [
    { "id": "...", "kind": "image", "actor_id": "<carol>", "title": "Mercado Benito Juárez at golden hour", ... },
    { "id": "...", "kind": "link",  "actor_id": "<bob>",   "title": "Database schema migrations without downtime", ... },
    { "id": "...", "kind": "text",  "actor_id": "<alice>", "body":  "Just shipped the Glow style guide v0.4 ...", ... }
  ],
  "pagination": { "next_cursor": null, "has_more": false }
}

2

Ranked feed (default)

order=ranked blends four signals into a single SQL-side score:

  • Recency — exponential decay with a configurable half-life.
  • Engagement — log-scaled count of comments + reactions.
  • Follow-graph bias — currently a constant; reserved for a future "discover" widening of the candidate pool.
  • Self-author boost — keeps the actor's own posts visible.

Each signal's weight is read from community.settings.ranking at query time. Defaults work out of the box.

bash
# Ranked is the default; you can omit ?order=ranked
curl -H "Authorization: Bearer pcft_live_..." \
  "https://agora.productcraft.co/v1/communities/<c>/actors/<actor-uuid>/feed?limit=20"

3

Tuning ranking

Per-community weights live on the community settings. Lower the recency weight if you want more evergreen content; bump engagement to push high-comment threads up; adjust the half-life for how fast posts cool off.

bash
# Update the ranking weights from the admin lane (cookie / PlatformUser bearer).
curl -X PATCH -H "content-type: application/json" \
  -H "Cookie: auth_token=<your-platform-session>" \
  -d '{
    "settings": {
      "ranking": {
        "w_recency": 0.6,
        "w_engagement": 0.3,
        "w_follow_bias": 0.05,
        "w_self_boost": 0.05,
        "half_life_hours": 12,
        "candidate_window_days": 7
      }
    }
  }' \
  https://agora.productcraft.co/v1/workspaces/<ws>/communities/<c>

4

Cursors

Cursors are not interchangeable between modes — passing a chronological cursor with order=ranked returns 400 INVALID_CURSOR. A malformed cursor is silently treated as page-1 (no error, just a restart). Always re-paginate from has_more=true + next_cursor within a single mode.


5

Empty state

When the actor follows no-one and has no posts, both modes return { "data": [], "pagination": { "next_cursor": null, "has_more": false } } with a 200. The endpoint is the right one to wire up your empty-feed CTA against — it doesn't 404 on a no-content actor.