Social guides
Guides

Bookmarks.

Save a post for later. Bookmarks are private — only the bookmarker sees them. No notifications, no counters; just a small per-actor save list with cursor pagination.


1

Save a post

Toggle-on. Idempotent — re-saving the same post returns the existing row with { created: false } instead of erroring. Visibility-aware: if the actor can't see the post, you get a 404 Post not found matching the opacity rule used everywhere else in social.

bash
curl -X POST \
  -H "Authorization: Bearer pcft_live_..." \
  -H "Content-Type: application/json" \
  -d '{"actor_id":"<actorId>"}' \
  "https://social.productcraft.co/v1/communities/<communityId>/posts/<postId>/bookmark"
json
{
  "bookmark": {
    "actor_id": "aaaaaaaa-...",
    "post_id": "bbbbbbbb-...",
    "community_id": "cccccccc-...",
    "created_at": "2026-05-11T19:55:00.000Z"
  },
  "created": true
}

2

Remove a bookmark

Toggle-off. Idempotent 204 either way — calling DELETE on a post that wasn't bookmarked still 204s. The body still carries actor_id so the request shape mirrors the POST.

bash
curl -X DELETE \
  -H "Authorization: Bearer pcft_live_..." \
  -H "Content-Type: application/json" \
  -d '{"actor_id":"<actorId>"}' \
  "https://social.productcraft.co/v1/communities/<communityId>/posts/<postId>/bookmark"

3

List my bookmarks

Per-actor, cursor-paginated newest-first. Same pagination envelope as the rest of social — { data, pagination: { next_cursor, has_more } }.

Bookmarks against posts whose visibility has narrowed since (or which have been deleted) stay in the list — we don't cross-check post visibility on read of bookmarks. The customer's UI can hydrate post-by-post if it wants the live state.

bash
curl -H "Authorization: Bearer pcft_live_..." \
  "https://social.productcraft.co/v1/communities/<communityId>/actors/<actorId>/bookmarks?limit=20"
json
{
  "data": [
    { "actor_id": "...", "post_id": "p-1", "community_id": "...", "created_at": "2026-05-11T19:55:00.000Z" },
    { "actor_id": "...", "post_id": "p-2", "community_id": "...", "created_at": "2026-05-10T08:00:00.000Z" }
  ],
  "pagination": { "next_cursor": null, "has_more": false }
}

UX

UX patterns

  • Optimistic UI. The toggle is fast and idempotent — flip the icon locally, fire the API call, and reconcile only on error. The duplicate POST returning created: false is your signal that someone else (the same user on another device, usually) already bookmarked the post.
  • Bookmark count is intentionally absent. Bookmarks are private by design — no global popularity signal, nothing for the post author to see. If you want public “saved” counts, that's a separate feature (X-style; out of scope here).
  • Scope the list endpoint to the right user. The PAK lane has no end-user principal — your backend is responsible for ensuring the actorId in the path matches the end-user behind the request before exposing the response. Returning user A's bookmarks to user B leaks reading habits.

Permissions

PAK scopes

  • POST requires social.create on pcft:agora:post/<postId>.
  • DELETE requires social.delete on the same URN.
  • GET requires social.list on pcft:agora:community/<communityId>.