Build a social app
03 · Posts

One primitive, three flavours.

Posts ride a single primitive — set `kind` to text, link, image, or story. Bodies are limited to 10 000 chars after trim. The same endpoint serves a status update, a link share, an image post, or a story (covered in stage 7).


1

Text post

bash
curl -X POST -H "Authorization: Bearer pcft_live_..." \
  -H "content-type: application/json" \
  -d '{
    "actor_id": "<alice-actor-uuid>",
    "kind": "text",
    "body": "Just shipped the Glow style guide v0.4 — color tokens cleaned up, type ramp tightened. Designers, send feedback 🌱"
  }' \
  https://agora.productcraft.co/v1/communities/<community-uuid>/posts
response.json
{
  "id": "85a00e9c-8b11-4cfc-947a-c7214ca65f46",
  "community_id": "4d5f5c9e-b4ee-4401-9275-283feb66c178",
  "actor_id": "d6ee4836-1645-41b0-a1c3-c7e22ad01934",
  "kind": "text",
  "title": null,
  "body": "Just shipped the Glow style guide v0.4 — ...",
  "url": null,
  "attributes": {},
  "visibility": "public",
  "status": "published",
  "reaction_counts": {},
  "comment_count": 0,
  "expires_at": null,
  "pinned": false,
  "created_at": "2026-05-02T19:29:27.762Z",
  "updated_at": "2026-05-02T19:29:27.762Z"
}

2

Link post

kind: "link" with a url and an optional title + body blurb. Your client renders the unfurl card; Agora just stores the metadata.

bash
curl -X POST -H "Authorization: Bearer pcft_live_..." \
  -H "content-type: application/json" \
  -d '{
    "actor_id": "<bob-actor-uuid>",
    "kind": "link",
    "title": "Database schema migrations without downtime",
    "body": "A pattern we use at Glow for zero-downtime schema changes. Uses partial indexes + a 3-step rollout.",
    "url": "https://example.com/blog/db-migrations"
  }' \
  https://agora.productcraft.co/v1/communities/<community-uuid>/posts

3

Image post

Agora doesn't store the binary — your media pipeline does (R2, S3, your CDN). Pass the URL on attributes.media_url + an alt for accessibility. The kind tag is what your client switches on to choose the renderer.

bash
curl -X POST -H "Authorization: Bearer pcft_live_..." \
  -H "content-type: application/json" \
  -d '{
    "actor_id": "<carol-actor-uuid>",
    "kind": "image",
    "title": "Mercado Benito Juárez at golden hour",
    "body": "A study in warm light + cobalt tilework. Oaxaca, this morning.",
    "attributes": {
      "media_url": "https://images.unsplash.com/photo-mercado.jpg",
      "alt": "A market stall under amber light"
    }
  }' \
  https://agora.productcraft.co/v1/communities/<community-uuid>/posts

4

Visibility

visibility is public by default. Other values:

  • followers — visible to author + their followers.
  • close_friends — visible to author + members of the author's close-friends list. Stories use this heavily; covered in stage 7.
  • private — visible to author only. Useful for drafts surfaced on a profile-edit screen.

5

List + filter by author

bash
curl -H "Authorization: Bearer pcft_live_..." \
  "https://agora.productcraft.co/v1/communities/<community-uuid>/posts?author_id=<carol-actor-uuid>&limit=20"

Drop author_id to list every post in the community (still cursor-paginated). For an end-user's home tab, you almost certainly want the feed endpoint in stage 6, not this raw list.


6

Edit + soft-delete

bash
# PATCH — partial update; missing fields are left alone.
curl -X PATCH -H "Authorization: Bearer pcft_live_..." \
  -H "content-type: application/json" \
  -d '{ "body": "Updated copy with a new hashtag #cdmx #goldenhour" }' \
  https://agora.productcraft.co/v1/communities/<community-uuid>/posts/<post-uuid>

# DELETE — soft-delete (status flips to "removed"; rows persist for moderation audit)
curl -X DELETE -H "Authorization: Bearer pcft_live_..." \
  https://agora.productcraft.co/v1/communities/<community-uuid>/posts/<post-uuid>