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
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{
"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.
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>/posts3
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.
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>/posts4
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
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
# 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>