Social guides
Guides

Notification preferences.

Per-actor, per-kind opt-out. The producer checks the preference before inserting a notification row, so muted kinds never land in the inbox or the unread-count — they're simply not written.

Fifteen notification kinds today: follow, reaction, comment_on_post, reply_to_comment, mention, vote, repost, quote_post, dm_new_message, dm_added_to_group, dm_role_changed, dm_reaction, tag, follow_request, follow_request_approved. The catalogue grows with the product; new kinds default to enabled for every actor.


1

Read the current settings

One row per known kind — all fifteen, every time. Kinds the actor has not explicitly toggled report enabled: true with an epoch updated_at, so your UI renders the full catalogue without a separate metadata call.

bash
curl -H "Authorization: Bearer pcft_live_..." \
  "https://social.productcraft.co/v1/communities/<communityId>/actors/<actorId>/notification-prefs"
json
{
  "data": [
    { "actor_id": "...", "kind": "follow",                  "enabled": true, "updated_at": "1970-01-01T00:00:00.000Z" },
    { "actor_id": "...", "kind": "reaction",                "enabled": true, "updated_at": "1970-01-01T00:00:00.000Z" },
    { "actor_id": "...", "kind": "comment_on_post",         "enabled": true, "updated_at": "1970-01-01T00:00:00.000Z" },
    { "actor_id": "...", "kind": "reply_to_comment",        "enabled": true, "updated_at": "1970-01-01T00:00:00.000Z" },
    { "actor_id": "...", "kind": "mention",                 "enabled": true, "updated_at": "1970-01-01T00:00:00.000Z" },
    { "actor_id": "...", "kind": "vote",                    "enabled": true, "updated_at": "1970-01-01T00:00:00.000Z" },
    { "actor_id": "...", "kind": "repost",                  "enabled": true, "updated_at": "1970-01-01T00:00:00.000Z" },
    { "actor_id": "...", "kind": "quote_post",              "enabled": true, "updated_at": "1970-01-01T00:00:00.000Z" },
    { "actor_id": "...", "kind": "dm_new_message",          "enabled": true, "updated_at": "1970-01-01T00:00:00.000Z" },
    { "actor_id": "...", "kind": "dm_added_to_group",       "enabled": true, "updated_at": "1970-01-01T00:00:00.000Z" },
    { "actor_id": "...", "kind": "dm_role_changed",         "enabled": true, "updated_at": "1970-01-01T00:00:00.000Z" },
    { "actor_id": "...", "kind": "dm_reaction",             "enabled": true, "updated_at": "1970-01-01T00:00:00.000Z" },
    { "actor_id": "...", "kind": "tag",                     "enabled": true, "updated_at": "1970-01-01T00:00:00.000Z" },
    { "actor_id": "...", "kind": "follow_request",          "enabled": true, "updated_at": "1970-01-01T00:00:00.000Z" },
    { "actor_id": "...", "kind": "follow_request_approved", "enabled": true, "updated_at": "1970-01-01T00:00:00.000Z" }
  ]
}

2

Mute a kind

Idempotent upsert keyed on (actor, kind), and it takes effect immediately — the internal preference cache is invalidated on every write. A kind outside the catalogue is rejected with 400 INVALID_NOTIFICATION_KIND.

bash
curl -X PATCH \
  -H "Authorization: Bearer pcft_live_..." \
  -H "Content-Type: application/json" \
  -d '{"enabled": false}' \
  "https://social.productcraft.co/v1/communities/<communityId>/actors/<actorId>/notification-prefs/follow"
json
{
  "actor_id": "aaaaaaaa-0000-0000-0000-000000000001",
  "kind": "follow",
  "enabled": false,
  "updated_at": "2026-05-11T19:42:00.000Z"
}

3

Re-enable

Same endpoint, { "enabled": true }. The row stays — only the boolean flips.

bash
curl -X PATCH \
  -H "Authorization: Bearer pcft_live_..." \
  -H "Content-Type: application/json" \
  -d '{"enabled": true}' \
  "https://social.productcraft.co/v1/communities/<communityId>/actors/<actorId>/notification-prefs/follow"

Behaviour

What changes downstream

  • Write-time suppression. When a kind is muted, the notification row is never inserted — it never appears in the inbox or the unread-count.
  • Stacks with existing suppressions. Self-actions and blocked-pair actions are still skipped first — preferences are an additional gate, not a replacement.
  • Fail-open on a DB hiccup. If the preference lookup itself errors out, the producer defaults to delivering — a transient outage must not silently mute the inbox.
  • Existing notifications are untouched. Muting a kind only stops new ones from being written. Rows already in the inbox stay where they are; clear them via the existing mark-read endpoints.

Permissions

PAK scopes

  • GET requires social.notify.read on pcft:agora:community/<communityId>.
  • PATCH requires social.notify.update on the same URN.

Both scopes are part of the default owner and admin system roles; member does not have social.notify.update by default. If your customer-backend signs in as an admin or an owner-scoped PAK, you're good.