Social guides
Guides

Hashtags.

Write #storymode in a post body and Social indexes it. The directory is per-community, the parser lowercases + dedupes + caps at 30 per body. Indexed inside the same DB transaction as the post insert — a failed post never leaves orphan tag rows.


1

Use #tags in a post

Just write #whatever in body. The parser pulls every #[a-z0-9_]{1,32} match (case-insensitive, lowercased on store), dedupes, and caps at 30 per body. Anything beyond 30 is silently dropped.

bash
curl -X POST \
  -H "Authorization: Bearer pcft_live_..." \
  -H "Content-Type: application/json" \
  -d '{"actor_id":"<actorId>","body":"big day! #storymode #v2 launch is live"}' \
  "https://social.productcraft.co/v1/communities/<communityId>/posts"

2

Browse the directory

Per-community, ordered by first_used_at desc — newest tags first. Cursor-paginated.

bash
curl -H "Authorization: Bearer pcft_live_..." \
  "https://social.productcraft.co/v1/communities/<communityId>/hashtags?limit=20"

3

Posts for a tag

Cursor-paginated by post created_at desc. Filters out drafts, removed, hidden, and expired posts — matches what you'd see in feeds.

bash
curl -H "Authorization: Bearer pcft_live_..." \
  "https://social.productcraft.co/v1/communities/<communityId>/hashtags/storymode/posts?limit=20"

4

Prefix autocomplete

Backed by a text_pattern_ops partial index — fast even on millions of tags. Pass a lowercase prefix.

bash
curl -H "Authorization: Bearer pcft_live_..." \
  "https://social.productcraft.co/v1/communities/<communityId>/search/hashtags?q=sto&limit=10"

5

Tag detail with post count

Returns the directory row plus a live count of currently- published posts using the tag.

bash
curl -H "Authorization: Bearer pcft_live_..." \
  "https://social.productcraft.co/v1/communities/<communityId>/hashtags/storymode"
json
{
  "community_id": "...",
  "tag": "storymode",
  "first_used_at": "2026-04-01T12:00:00.000Z",
  "post_count": 17
}

Internals

Why the transaction matters

Hashtag rows are inserted INSIDE the same DB transaction as the post insert. The naive approach — insert the post, then insert the hashtags — produces orphan directory entries when the post insert fails: the hashtag row sits in the directory pointing at a post that never came into existence.

The TX-aware path also covers updates: editing a post body re-runs the parser, replaces the post'spost_hashtag rows, and ensures any new tags appear in the directory — all in one TX. If the update rolls back, the index doesn't drift.


Permissions

PAK scopes

  • List + posts-for-tag + autocomplete: social.list on pcft:agora:community/<communityId>.
  • Tag detail: social.read on the same URN.
  • Writes ride the post-create / post-update endpoints and use those scopes; no extra grant needed for the hashtag side-effect.