{"openapi":"3.0.0","paths":{"/v1/workspaces/{workspaceId}/communities":{"get":{"operationId":"CommunityController_list","parameters":[{"name":"workspaceId","required":true,"in":"path","description":"ProductCraft workspace UUID owning the community.","schema":{"example":"11111111-1111-1111-1111-111111111111","type":"string"}}],"responses":{"200":{"description":"Bare array of communities; not paginated (workspaces have a small handful).","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CommunityResponseDto"}}}}},"403":{"description":"Caller lacks `agora.read`, or the workspace has not enabled the `agora` service.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List communities in a workspace","tags":["agora-communities"]},"post":{"operationId":"CommunityController_create","parameters":[{"name":"workspaceId","required":true,"in":"path","description":"ProductCraft workspace UUID owning the community.","schema":{"example":"11111111-1111-1111-1111-111111111111","type":"string"}},{"name":"authorization","required":true,"in":"header","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCommunityDto"}}}},"responses":{"201":{"description":"Community created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommunityResponseDto"}}}},"400":{"description":"Validation failure (slug shape, display_name length).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"`app_id` was supplied but no bearer/cookie was forwarded for the upstream Heimdall fetch.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `agora.create` or workspace has not enabled the `agora` service.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`app_id` supplied but the Heimdall app was not found in this workspace (opaque cross-tenant 404).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"Community slug already taken in this workspace, or the supplied `app_id` is already connected to a community.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Create an agora community. `app_id` is optional — when supplied agora connects the community to a Heimdall app and refuses if the app belongs to a different workspace (cross-tenant gate) or is already connected. Omit `app_id` for a standalone community (no Heimdall integration; the customer manages actor identity).","tags":["agora-communities"]}},"/v1/workspaces/{workspaceId}/communities/{communityId}":{"get":{"operationId":"CommunityController_get","parameters":[{"name":"workspaceId","required":true,"in":"path","description":"ProductCraft workspace UUID owning the community.","schema":{"example":"11111111-1111-1111-1111-111111111111","type":"string"}},{"name":"communityId","required":true,"in":"path","description":"Agora community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}}],"responses":{"200":{"description":"Community found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommunityResponseDto"}}}},"403":{"description":"Caller lacks `agora.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community not found in this workspace (opaque cross-tenant 404).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Get one community by id.","tags":["agora-communities"]},"patch":{"operationId":"CommunityController_update","parameters":[{"name":"workspaceId","required":true,"in":"path","description":"ProductCraft workspace UUID owning the community.","schema":{"example":"11111111-1111-1111-1111-111111111111","type":"string"}},{"name":"communityId","required":true,"in":"path","description":"Agora community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCommunityDto"}}}},"responses":{"200":{"description":"Community updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommunityResponseDto"}}}},"400":{"description":"Validation failure or invalid status value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `agora.update`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community not found in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Update community display name, description, settings, or status. Status transitions are unrestricted (active ↔ suspended; archived is terminal in v1).","tags":["agora-communities"]},"delete":{"operationId":"CommunityController_delete","parameters":[{"name":"workspaceId","required":true,"in":"path","description":"ProductCraft workspace UUID owning the community.","schema":{"example":"11111111-1111-1111-1111-111111111111","type":"string"}},{"name":"communityId","required":true,"in":"path","description":"Agora community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}}],"responses":{"204":{"description":"Community deleted."},"403":{"description":"Caller lacks `agora.delete`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community not found in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Delete a community. Cascades to every actor, post, comment, edge, flag, and notification it owns. Not reversible.","tags":["agora-communities"]}},"/v1/communities/{communityId}/actors":{"post":{"operationId":"ActorController_upsert","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertActorDto"}}}},"responses":{"201":{"description":"Created (`created: true`) or upserted (`created: false`); the actor row is returned in both cases.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertActorResponseDto"}}}},"400":{"description":"Validation failure (external_id, display_name, avatar_url length).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.create` for this community.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community not found in the PAK's workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Upsert an actor by external_id. Creates on first call, updates on subsequent calls with the same external_id. Idempotent on `(community, external_id)`. Note: omitted fields on a follow-up call are overwritten with the new body — pass every field you want to keep on each call.","tags":["agora-actors"]},"get":{"operationId":"ActorController_list","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"description":"Page size; capped at 200."},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"},"description":"Opaque cursor from the previous page's `pagination.next_cursor`."}],"responses":{"200":{"description":"Page of actors with `next_cursor` + `has_more`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActorListResponseDto"}}}},"403":{"description":"PAK lacks `agora.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community not found in the PAK's workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List actors in a community. 50 per page by default (max 200) — paginate with the cursor returned in `pagination.next_cursor` while `pagination.has_more` is true.","tags":["agora-actors"]}},"/v1/communities/{communityId}/actors/by-external/{externalId}":{"get":{"operationId":"ActorController_getByExternal","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"externalId","required":true,"in":"path","description":"Customer-supplied identifier for the user (the `external_id` you upserted with).","schema":{"example":"user_abc123","type":"string"}}],"responses":{"200":{"description":"Actor row.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActorResponseDto"}}}},"403":{"description":"PAK lacks `agora.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"No actor with that external_id in this community.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Look up an actor by the customer-supplied external_id.","tags":["agora-actors"]}},"/v1/communities/{communityId}/actors/{actorId}":{"get":{"operationId":"ActorController_getById","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID (the id returned by upsert).","schema":{"example":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa","type":"string"}}],"responses":{"200":{"description":"Actor row.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActorResponseDto"}}}},"404":{"description":"Actor not found in this community.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Get an actor by its agora UUID.","tags":["agora-actors"]},"patch":{"operationId":"ActorController_update","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID.","schema":{"example":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateActorDto"}}}},"responses":{"200":{"description":"Actor updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActorResponseDto"}}}},"400":{"description":"Validation failure or invalid status value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.update`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Actor not found in this community.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Update actor profile fields. Pass only the fields you want to change.","tags":["agora-actors"]},"delete":{"operationId":"ActorController_delete","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID.","schema":{"example":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa","type":"string"}}],"responses":{"204":{"description":"Actor deleted."},"403":{"description":"PAK lacks `agora.delete`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Actor not found in this community.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Delete an actor. Hard delete — cascades to every post, comment, edge, flag, and notification it owns.","tags":["agora-actors"]}},"/v1/communities/{communityId}/posts":{"post":{"operationId":"PostController_create","parameters":[{"name":"communityId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePostDto"}}}},"responses":{"201":{"description":"Post created. Defaults: `kind=text`, `visibility=public`, `status=published`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostResponseDto"}}}},"400":{"description":"Validation failure (missing actor_id; body/title/url all empty; bad URL; bad ISO timestamp).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.create`, or actor is suspended.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or actor not found in the PAK's workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"summary":"Create a post on behalf of an actor.","tags":["Post"]},"get":{"operationId":"PostController_list","parameters":[{"name":"communityId","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"},"description":"Opaque cursor from `pagination.next_cursor`."},{"name":"author_id","required":false,"in":"query","schema":{"type":"string","format":"uuid"},"description":"Limit results to this actor."}],"responses":{"200":{"description":"Page of posts (only `published`, non-expired, public-visibility unless filtered by author).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostListResponseDto"}}}},"403":{"description":"PAK lacks `agora.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"summary":"List posts. Pass `author_id` to filter to a single author. 50/page (max 200), cursor-paginated.","tags":["Post"]}},"/v1/communities/{communityId}/posts/{postId}":{"get":{"description":"Without `requester_id`, only `public` posts return. With one, `followers` / `close_friends` / the requester's own `private` posts surface per the visibility matrix. Returns 404 (opacity) when the requester is not allowed to read the post — never 403.","operationId":"PostController_getById","parameters":[{"name":"communityId","required":true,"in":"path","schema":{"type":"string"}},{"name":"postId","required":true,"in":"path","description":"Agora post UUID.","schema":{"example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb","type":"string"}},{"name":"requester_id","required":false,"in":"query","schema":{"type":"string","format":"uuid"},"description":"Agora actor UUID of the viewer. Surfaces non-public posts they are entitled to see."}],"responses":{"200":{"description":"Post.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostResponseDto"}}}},"403":{"description":"PAK lacks `agora.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Post not found, removed, expired, or not visible to the requester.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"summary":"Get a post by id. Visibility-aware: pass `requester_id` for non-public posts.","tags":["Post"]},"patch":{"operationId":"PostController_update","parameters":[{"name":"communityId","required":true,"in":"path","schema":{"type":"string"}},{"name":"postId","required":true,"in":"path","description":"Agora post UUID.","schema":{"example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePostDto"}}}},"responses":{"200":{"description":"Post updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostResponseDto"}}}},"400":{"description":"Validation failure or invalid status / visibility / timestamp.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.update`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Post not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"summary":"Update post fields. `pinned` is the highlights toggle for stories; status transitions to `hidden`/`removed` should usually go through the moderation lane.","tags":["Post"]},"delete":{"operationId":"PostController_delete","parameters":[{"name":"communityId","required":true,"in":"path","schema":{"type":"string"}},{"name":"postId","required":true,"in":"path","description":"Agora post UUID.","schema":{"example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb","type":"string"}}],"responses":{"204":{"description":"Post soft-deleted."},"403":{"description":"PAK lacks `agora.delete`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Post not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"summary":"Soft-delete a post. The row is marked `removed`; viewers see 404. Comments and reactions are kept for moderation audit.","tags":["Post"]}},"/v1/communities/{communityId}/posts/{postId}/revisions":{"get":{"description":"Each entry is a snapshot of body/title/attributes from BEFORE the edit landed. The post row itself carries the current state plus `edited_at` + `edit_count`. Drafts are not snapshotted — only published posts incur revisions.","operationId":"PostController_listRevisions","parameters":[{"name":"communityId","required":true,"in":"path","schema":{"type":"string"}},{"name":"postId","required":true,"in":"path","description":"Agora post UUID.","schema":{"example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb","type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Page of revisions, newest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostRevisionListResponseDto"}}}},"403":{"description":"PAK lacks `agora.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or post not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"summary":"List a post's edit history. Cursor-paginated by revision_n desc (newest edit first).","tags":["Post"]}},"/v1/communities/{communityId}/posts/{postId}/quotes":{"get":{"description":"Cursor-paginated by `(created_at DESC, id DESC)`. Each entry is the full quote post row — body, attributes, the quoter's actor_id, the source_post_id back-reference. Filters to status=published.","operationId":"PostController_listQuotes","parameters":[{"name":"communityId","required":true,"in":"path","schema":{"type":"string"}},{"name":"postId","required":true,"in":"path","description":"Source post UUID.","schema":{"example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb","type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Page of quote posts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostListResponseDto"}}}},"403":{"description":"PAK lacks `agora.list`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or source post not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"summary":"List the quote posts of a source post (X-style \"show quotes\" link).","tags":["Post"]}},"/v1/communities/{communityId}/posts/views":{"post":{"operationId":"PostController_recordViews","parameters":[{"name":"communityId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordViewsDto"}}}},"responses":{"204":{"description":"Views recorded (or no-op if every id was missing)."},"400":{"description":"Empty `post_ids`, more than 200 ids, or a non-UUID in the list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.list` on the community URN.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"summary":"Batch-increment view counts on a list of posts. Client-side debounced; missing post ids are silently skipped (so a deleted post mid-batch doesn’t fail the rest).","tags":["Post"]}},"/v1/communities/{communityId}/actors/{actorId}/drafts":{"get":{"description":"Drafts are visible only via this endpoint — they're stripped from public feeds, lists, and `getById`. The PAK lane has no end-user principal, so the customer's backend is responsible for ensuring `actorId` matches the end-user behind the request before exposing the response.","operationId":"DraftController_listDrafts","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID whose drafts to list.","schema":{"example":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa","type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"},"description":"Opaque cursor from `pagination.next_cursor`."}],"responses":{"200":{"description":"Page of draft posts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DraftListResponseDto"}}}},"403":{"description":"PAK lacks `agora.list`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or actor not found (or belongs to a different community).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List the actor's drafts (status='draft'). Cursor-paginated newest-first.","tags":["agora-drafts"]}},"/v1/communities/{communityId}/actors/{actorId}/scheduled-posts":{"get":{"description":"Scheduled posts are invisible to everyone except the author until the per-minute PostSchedulerCron promotes them. Same end-user authorization rules as drafts: the PAK lane trusts the customer-backend to gate the caller.","operationId":"DraftController_listScheduledPosts","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID whose scheduled posts to list.","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Page of scheduled posts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DraftListResponseDto"}}}},"403":{"description":"PAK lacks `agora.list`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List the actor's scheduled posts (status='scheduled'), ordered soonest-to-publish first. Cursor-paginated.","tags":["agora-drafts"]}},"/v1/communities/{communityId}/posts/{postId}/repost":{"post":{"description":"Visibility is inherited from the source. The reposter must be allowed to read the source (else 404). Self-repost 422; private-source repost 422. Bumps `source.repost_count` and the reposter's `post_count`. Fires a `repost` notification to the source author (subject to per-kind preferences from task 021).","operationId":"RepostController_create","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"postId","required":true,"in":"path","description":"Source post UUID.","schema":{"example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRepostDto"}}}},"responses":{"201":{"description":"Repost created (or returned via idempotent retry).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRepostResponseDto"}}}},"400":{"description":"Validation failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.create`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community / source / actor not found, or source invisible to the actor.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"CANNOT_REPOST_SELF (reposting your own post) or CANNOT_REPOST_PRIVATE (source is visibility='private').","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Repost a source post on behalf of an actor. Idempotent — returns the existing repost row with `created: false` on retry.","tags":["agora-reposts"]},"delete":{"operationId":"RepostController_delete","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"postId","required":true,"in":"path","description":"Source post UUID.","schema":{"example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRepostDto"}}}},"responses":{"204":{"description":"Repost soft-deleted (or was never present)."},"400":{"description":"Validation failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.delete`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community / source / actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Undo my repost of a source post. Idempotent — 204 either way.","tags":["agora-reposts"]}},"/v1/communities/{communityId}/posts/{postId}/reposts":{"get":{"operationId":"RepostController_list","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"postId","required":true,"in":"path","description":"Source post UUID.","schema":{"example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb","type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Page of repost rows.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RepostListResponseDto"}}}},"403":{"description":"PAK lacks `agora.list`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or source not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List the actors who have reposted a source post. Cursor-paginated newest-first. Useful for \"X reposted this\" UI.","tags":["agora-reposts"]}},"/v1/communities/{communityId}/actors/{actorId}/counters":{"get":{"operationId":"CounterController_getCounters","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID (returned from POST /actors).","schema":{"example":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa","type":"string"}}],"responses":{"200":{"description":"Counters for the actor.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActorCountersResponseDto"}}}},"403":{"description":"PAK lacks `agora.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Not found (or belongs to a different community / workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Denormalised counters for one actor (followers, following, posts, comments, blocks).","tags":["agora-counters"]}},"/v1/workspaces/{workspaceId}/communities/{communityId}/analytics":{"get":{"operationId":"AnalyticsController_getCommunityAnalytics","parameters":[{"name":"workspaceId","required":true,"in":"path","description":"ProductCraft workspace UUID.","schema":{"example":"11111111-1111-1111-1111-111111111111","type":"string"}},{"name":"communityId","required":true,"in":"path","description":"Agora community UUID owned by the workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}}],"responses":{"200":{"description":"Computed analytics rollup.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommunityAnalyticsResponseDto"}}}},"403":{"description":"Caller lacks `agora.analytics.read`, or the workspace has not enabled the `agora` service.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community not found in this workspace (opaque cross-tenant 404).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Community-level analytics rollup: total active actors, published posts, published comments, open flags, and last-7-day windows for each.","tags":["agora-analytics"]}},"/v1/communities/{communityId}/follows":{"post":{"operationId":"EdgeController_follow","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActorEdgeDto"}}}},"responses":{"201":{"description":"Follow edge created (or returned via idempotent retry).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEdgeResponseDto"}}}},"400":{"description":"Missing / invalid src_actor_id / dst_actor_id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.create`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or one of the actors not found in the PAK's workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"A block exists between the pair (in either direction).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Follow another actor. Idempotent: returns existing edge with `created: false` if the follow already exists. Refused if either side has a block.","tags":["agora-edges"]}},"/v1/communities/{communityId}/follows/{srcActorId}/{dstActorId}":{"delete":{"operationId":"EdgeController_unfollow","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"srcActorId","required":true,"in":"path","description":"Follower actor UUID.","schema":{"example":"aaaaaaaa-0000-0000-0000-000000000001","type":"string"}},{"name":"dstActorId","required":true,"in":"path","description":"Followee actor UUID.","schema":{"example":"aaaaaaaa-0000-0000-0000-000000000002","type":"string"}}],"responses":{"204":{"description":"Unfollowed (or no edge to remove)."},"403":{"description":"PAK lacks `agora.delete`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Unfollow. Idempotent — 204 even if the follow didn't exist.","tags":["agora-edges"]}},"/v1/communities/{communityId}/actors/{actorId}/following":{"get":{"operationId":"EdgeController_listFollowing","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID.","schema":{"example":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa","type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Page of follow edges.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EdgeListResponseDto"}}}},"403":{"description":"PAK lacks `agora.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List the actors this actor follows.","tags":["agora-edges"]}},"/v1/communities/{communityId}/actors/{actorId}/followers":{"get":{"operationId":"EdgeController_listFollowers","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID.","schema":{"example":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa","type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Page of follow edges.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EdgeListResponseDto"}}}},"403":{"description":"PAK lacks `agora.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List the actors who follow this actor.","tags":["agora-edges"]}},"/v1/communities/{communityId}/mutes":{"post":{"operationId":"EdgeController_mute","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActorEdgeDto"}}}},"responses":{"201":{"description":"Mute edge created (or returned via idempotent retry).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEdgeResponseDto"}}}},"400":{"description":"Missing / invalid src_actor_id / dst_actor_id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.create`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or actors not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"Block exists between the pair.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Mute another actor. One-directional and silent — the muted actor isn't notified. Hides their content from the muter's feed.","tags":["agora-edges"]}},"/v1/communities/{communityId}/mutes/{srcActorId}/{dstActorId}":{"delete":{"operationId":"EdgeController_unmute","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"srcActorId","required":true,"in":"path","description":"Muter actor UUID.","schema":{"type":"string"}},{"name":"dstActorId","required":true,"in":"path","description":"Mutee actor UUID.","schema":{"type":"string"}}],"responses":{"204":{"description":"Unmuted."},"403":{"description":"PAK lacks `agora.delete`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Unmute. Idempotent — 204 either way.","tags":["agora-edges"]}},"/v1/communities/{communityId}/actors/{actorId}/mutes":{"get":{"operationId":"EdgeController_listMutes","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID.","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Page of mute edges.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EdgeListResponseDto"}}}},"403":{"description":"PAK lacks `agora.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List actors this actor has muted.","tags":["agora-edges"]}},"/v1/communities/{communityId}/blocks":{"post":{"operationId":"EdgeController_block","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActorEdgeDto"}}}},"responses":{"201":{"description":"Block edge created (or returned via idempotent retry); follows + mutes between the pair are torn down in the same transaction.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEdgeResponseDto"}}}},"400":{"description":"Missing / invalid src_actor_id / dst_actor_id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.create`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or actors not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Block another actor. Removes any existing follows/mutes between the two in either direction (one transaction).","tags":["agora-edges"]}},"/v1/communities/{communityId}/blocks/{srcActorId}/{dstActorId}":{"delete":{"operationId":"EdgeController_unblock","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"srcActorId","required":true,"in":"path","description":"Blocker actor UUID.","schema":{"type":"string"}},{"name":"dstActorId","required":true,"in":"path","description":"Blockee actor UUID.","schema":{"type":"string"}}],"responses":{"204":{"description":"Unblocked."},"403":{"description":"PAK lacks `agora.delete`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Unblock. Idempotent — 204 either way. Does NOT restore prior follows / mutes (the block tear-down is one-way).","tags":["agora-edges"]}},"/v1/communities/{communityId}/actors/{actorId}/blocks":{"get":{"operationId":"EdgeController_listBlocks","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID.","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Page of block edges.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EdgeListResponseDto"}}}},"403":{"description":"PAK lacks `agora.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List actors this actor has blocked.","tags":["agora-edges"]}},"/v1/communities/{communityId}/restricts":{"post":{"operationId":"EdgeController_restrict","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActorEdgeDto"}}}},"responses":{"201":{"description":"Restrict edge created (or returned via idempotent retry).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEdgeResponseDto"}}}},"400":{"description":"Missing / invalid src_actor_id / dst_actor_id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.create`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or actors not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Restrict another actor (Instagram-style). One-directional. Restricted actors can still see content, but new comments by them are pre-moderated as `pending_approval` until the post-author approves.","tags":["agora-edges"]}},"/v1/communities/{communityId}/restricts/{srcActorId}/{dstActorId}":{"delete":{"operationId":"EdgeController_unrestrict","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"srcActorId","required":true,"in":"path","description":"Restrictor actor UUID.","schema":{"type":"string"}},{"name":"dstActorId","required":true,"in":"path","description":"Restricted actor UUID.","schema":{"type":"string"}}],"responses":{"204":{"description":"Unrestricted."},"403":{"description":"PAK lacks `agora.delete`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Unrestrict. Idempotent — 204 either way.","tags":["agora-edges"]}},"/v1/communities/{communityId}/actors/{actorId}/restricts":{"get":{"operationId":"EdgeController_listRestricts","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID.","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Page of restrict edges.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EdgeListResponseDto"}}}},"403":{"description":"PAK lacks `agora.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List actors this actor has restricted.","tags":["agora-edges"]}},"/v1/communities/{communityId}/hashtags":{"get":{"operationId":"HashtagController_list","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Page of hashtags.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HashtagListResponseDto"}}}},"403":{"description":"PAK lacks `agora.list`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community not found (or belongs to a different community).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List hashtags in this community. Cursor-paginated by `first_used_at` desc.","tags":["agora-hashtags"]}},"/v1/communities/{communityId}/hashtags/{tag}":{"get":{"operationId":"HashtagController_getOne","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"tag","required":true,"in":"path","description":"Lowercase tag (without `#`).","schema":{"example":"storymode","type":"string"}}],"responses":{"200":{"description":"Hashtag detail.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HashtagDetailResponseDto"}}}},"400":{"description":"Tag fails the lowercase + alphanum + underscore + 1..32 rule.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or tag not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Get a single hashtag with its current published post count.","tags":["agora-hashtags"]}},"/v1/communities/{communityId}/search/hashtags":{"get":{"description":"Backed by a `text_pattern_ops` partial index — sub-100ms for any prefix length up to communities with millions of tags. Results are alphabetically ordered.","operationId":"HashtagController_autocomplete","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"q","required":true,"in":"query","schema":{"type":"string"},"description":"Lowercase prefix (1..32 chars)."},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}}],"responses":{"200":{"description":"Matching hashtags.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HashtagAutocompleteResponseDto"}}}},"400":{"description":"q is required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.list`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Prefix-search hashtags for autocomplete.","tags":["agora-hashtags"]}},"/v1/communities/{communityId}/actors/{actorId}/hashtag-follows":{"post":{"operationId":"HashtagFollowController_follow","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID who is following.","schema":{"example":"aaaaaaaa-0000-0000-0000-000000000001","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateHashtagFollowDto"}}}},"responses":{"201":{"description":"Follow created (or refreshed).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HashtagFollowResponseDto"}}}},"400":{"description":"Tag fails the lowercase + alphanum + underscore + 1..32 rule.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.create`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community, actor, or hashtag not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Follow a hashtag on behalf of an actor. Idempotent — re-follow refreshes the followed_at timestamp but doesn't error.","tags":["agora-hashtag-follows"]},"get":{"operationId":"HashtagFollowController_list","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID.","schema":{"example":"aaaaaaaa-0000-0000-0000-000000000001","type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Page of followed hashtags.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HashtagFollowListResponseDto"}}}},"403":{"description":"PAK lacks `agora.list`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List the hashtags an actor follows. Cursor-paginated newest-first by followed_at.","tags":["agora-hashtag-follows"]}},"/v1/communities/{communityId}/actors/{actorId}/hashtag-follows/{tag}":{"delete":{"operationId":"HashtagFollowController_unfollow","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID.","schema":{"example":"aaaaaaaa-0000-0000-0000-000000000001","type":"string"}},{"name":"tag","required":true,"in":"path","description":"Lowercase tag.","schema":{"example":"storymode","type":"string"}}],"responses":{"204":{"description":"Unfollowed (or was never followed)."},"400":{"description":"Tag malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.delete`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Unfollow a hashtag. Idempotent — 204 either way.","tags":["agora-hashtag-follows"]}},"/v1/communities/{communityId}/posts/{postId}/comments":{"post":{"operationId":"CommentController_create","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"postId","required":true,"in":"path","description":"Agora post UUID the comments thread under.","schema":{"example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCommentDto"}}}},"responses":{"201":{"description":"Comment created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentResponseDto"}}}},"400":{"description":"Validation failure (missing actor_id, empty body, body too long).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.create`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community, post, or parent comment not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"Block exists between commenter and post author, or depth exceeds community.settings.max_comment_depth.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Add a comment on a post. Pass `parent_id` to reply; the depth is computed and capped by the community config.","tags":["agora-comments"]},"get":{"operationId":"CommentController_list","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"postId","required":true,"in":"path","description":"Agora post UUID the comments thread under.","schema":{"example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb","type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}},{"name":"parent_id","required":false,"in":"query","schema":{"type":"string","format":"uuid"},"description":"List replies of this parent comment instead of top-level."},{"name":"requester_id","required":false,"in":"query","schema":{"type":"string","format":"uuid"},"description":"Viewer actor UUID — surfaces author_only replies they're entitled to see."}],"responses":{"200":{"description":"Page of comments.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentListResponseDto"}}}},"403":{"description":"PAK lacks `agora.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or post not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List comments on a post. Default returns top-level comments newest-first; pass `parent_id` to expand a thread (oldest-first). Pass `requester_id` so DM-style `author_only` replies surface only to the post-author and the comment-author pair.","tags":["agora-comments"]}},"/v1/communities/{communityId}/posts/{postId}/comments/{commentId}":{"get":{"operationId":"CommentController_getById","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"postId","required":true,"in":"path","description":"Agora post UUID the comments thread under.","schema":{"example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb","type":"string"}},{"name":"commentId","required":true,"in":"path","description":"Agora comment UUID.","schema":{"example":"bbbbbbbb-0000-0000-0000-000000000001","type":"string"}},{"name":"requester_id","required":false,"in":"query","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Comment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentResponseDto"}}}},"403":{"description":"PAK lacks `agora.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Comment not found, removed, or not visible to the requester.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Get a single comment by id.","tags":["agora-comments"]},"patch":{"operationId":"CommentController_update","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"postId","required":true,"in":"path","description":"Agora post UUID the comments thread under.","schema":{"example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb","type":"string"}},{"name":"commentId","required":true,"in":"path","description":"Agora comment UUID.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCommentDto"}}}},"responses":{"200":{"description":"Comment updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentResponseDto"}}}},"400":{"description":"Validation failure or invalid status / visibility.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.update`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Comment not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Update a comment's body, status, or visibility.","tags":["agora-comments"]},"delete":{"operationId":"CommentController_delete","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"postId","required":true,"in":"path","description":"Agora post UUID the comments thread under.","schema":{"example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb","type":"string"}},{"name":"commentId","required":true,"in":"path","description":"Agora comment UUID.","schema":{"type":"string"}}],"responses":{"204":{"description":"Comment soft-deleted."},"403":{"description":"PAK lacks `agora.delete`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Comment not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Soft-delete a comment.","tags":["agora-comments"]}},"/v1/communities/{communityId}/posts/{postId}/comments/{commentId}/approve":{"post":{"operationId":"CommentController_approve","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"postId","required":true,"in":"path","description":"Agora post UUID the comments thread under.","schema":{"example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb","type":"string"}},{"name":"commentId","required":true,"in":"path","description":"Agora comment UUID.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApproveCommentDto"}}}},"responses":{"200":{"description":"Comment approved (now published).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentResponseDto"}}}},"400":{"description":"Missing or invalid approver_actor_id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller is not the post-author.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Comment not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"Comment is in a status that cannot be approved (hidden / removed).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Approve a pending_approval comment (restricted-actor pre-moderation). Reserved to the post-author. No-op if already published.","tags":["agora-comments"]}},"/v1/communities/{communityId}/posts/{postId}/comments/{commentId}/reactions":{"post":{"operationId":"CommentReactionController_add","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"postId","required":true,"in":"path","description":"Parent post UUID.","schema":{"example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb","type":"string"}},{"name":"commentId","required":true,"in":"path","description":"Comment UUID.","schema":{"example":"bbbbbbbb-0000-0000-0000-000000000001","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCommentReactionDto"}}}},"responses":{"201":{"description":"Reaction added; response carries live `reaction_counts`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddCommentReactionResponseDto"}}}},"400":{"description":"Validation failure (missing src_actor_id; empty / overlong type).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.create`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community / post / comment / actor not found, or comment is invisible to the actor.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"Block exists between actor and comment author.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"React to a comment. Idempotent: re-posting the same `(actor, type)` returns the existing row with `created: false`. Multiple reactions per `(actor, comment)` are allowed (different `type` values count separately).","tags":["agora-comment-reactions"]},"get":{"operationId":"CommentReactionController_list","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"postId","required":true,"in":"path","description":"Parent post UUID.","schema":{"example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb","type":"string"}},{"name":"commentId","required":true,"in":"path","description":"Comment UUID.","schema":{"example":"bbbbbbbb-0000-0000-0000-000000000001","type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Page of reactions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentReactionListResponseDto"}}}},"403":{"description":"PAK lacks `agora.list`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community / post / comment not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List reactions on a comment, newest-first. Cursor encodes `(created_at, actor_id, type)` so multi-reaction-from-same-actor stays stable.","tags":["agora-comment-reactions"]}},"/v1/communities/{communityId}/posts/{postId}/comments/{commentId}/reactions/{srcActorId}/{type}":{"delete":{"operationId":"CommentReactionController_remove","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"postId","required":true,"in":"path","description":"Parent post UUID.","schema":{"example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb","type":"string"}},{"name":"commentId","required":true,"in":"path","description":"Comment UUID.","schema":{"example":"bbbbbbbb-0000-0000-0000-000000000001","type":"string"}},{"name":"srcActorId","required":true,"in":"path","description":"Reactor actor UUID.","schema":{"type":"string"}},{"name":"type","required":true,"in":"path","description":"Reaction type label (matches the value passed at create time).","schema":{"example":"like","type":"string"}}],"responses":{"200":{"description":"Reaction removed (or already absent); response carries live `reaction_counts`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentReactionCountsResponseDto"}}}},"400":{"description":"Missing path parameter `type`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.delete`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community / post / comment / actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Unreact. Idempotent: returns 200 with current `reaction_counts` even if there was nothing to remove.","tags":["agora-comment-reactions"]}},"/v1/communities/{communityId}/actors/{actorId}/feed":{"get":{"operationId":"FeedController_getFeed","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Viewer agora actor UUID — feed is candidate-pooled to their follow-graph.","schema":{"example":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa","type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"},"description":"Opaque cursor; not interchangeable between `ranked` and `chronological` modes."},{"name":"order","required":false,"in":"query","schema":{"type":"string","enum":["ranked","chronological"],"default":"ranked"}}],"responses":{"200":{"description":"Page of posts (only `published`, non-expired, visibility-filtered).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedResponseDto"}}}},"400":{"description":"INVALID_ORDER (unknown `order` value) or INVALID_CURSOR (cursor shape mismatch).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Feed for an actor: their own posts + posts from actors they follow, with blocks excluded. Default order is `ranked` (recency + engagement + follow-graph signals); pass `order=chronological` to get the time-ordered feed. 50/page (max 200). The cursor shape differs between orders — passing a chronological cursor with `order=ranked` returns 400 INVALID_CURSOR.","tags":["agora-feed"]}},"/v1/communities/{communityId}/discover-feed":{"get":{"operationId":"FeedController_getDiscoverFeed","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"viewer_actor_id","required":false,"in":"query","schema":{"type":"string","format":"uuid"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"},"description":"Opaque ranked cursor; encodes (score, id)."}],"responses":{"200":{"description":"Page of posts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedResponseDto"}}}},"403":{"description":"PAK lacks `agora.list`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or viewer actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Discover / explore feed (task 019). Ranked surface over every public post in the community — Instagram Explore / X \"For You without follow constraint\". Optional `viewer_actor_id` filters the viewer's blocks and applies a small self-boost; without one, the result is the global discovery slice.","tags":["agora-feed"]}},"/v1/communities/{communityId}/lists/{listId}/feed":{"get":{"operationId":"FeedController_getListFeed","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"listId","required":true,"in":"path","description":"List UUID.","schema":{"type":"string"}},{"name":"viewer_actor_id","required":true,"in":"query","schema":{"type":"string","format":"uuid"},"description":"Viewer — required for visibility checks + block-filter."},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"},"description":"Opaque cursor; differs in shape between ranked and chronological."},{"name":"order","required":false,"in":"query","schema":{"type":"string","enum":["ranked","chronological"],"default":"chronological"}}],"responses":{"200":{"description":"Page of posts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedResponseDto"}}}},"400":{"description":"INVALID_ORDER or INVALID_CURSOR.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.list`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"List not found, or private and viewer is not owner.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List-scoped feed (task 018). Returns posts authored by members of the list, filtered by what the viewer can see. Default order is `chronological`; pass `order=ranked` for the same engagement-blended scoring used by the actor feed.","tags":["agora-feed"]}},"/v1/communities/{communityId}/lists":{"post":{"operationId":"ActorListController_create","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateActorListDto"}}}},"responses":{"201":{"description":"List created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActorListResponseDto"}}}},"400":{"description":"Validation failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.create`, or owner is suspended.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or owner not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Create a new list. Default visibility `private`.","tags":["agora-lists"]},"get":{"operationId":"ActorListController_list","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"viewer_actor_id","required":false,"in":"query","schema":{"type":"string","format":"uuid"},"description":"Surface the viewer's own private lists alongside the public catalog."},{"name":"owner_actor_id","required":false,"in":"query","schema":{"type":"string","format":"uuid"},"description":"Filter to lists owned by this actor."},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Page of lists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActorListListResponseDto"}}}},"403":{"description":"PAK lacks `agora.list`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List discoverable lists in this community. Public lists are visible to anyone; private lists surface only when `viewer_actor_id` is their owner.","tags":["agora-lists"]}},"/v1/communities/{communityId}/lists/{listId}":{"get":{"operationId":"ActorListController_getById","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"listId","required":true,"in":"path","description":"List UUID.","schema":{"type":"string"}},{"name":"viewer_actor_id","required":false,"in":"query","schema":{"type":"string","format":"uuid"},"description":"Required to see a private list (must equal owner)."}],"responses":{"200":{"description":"List.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActorListResponseDto"}}}},"403":{"description":"PAK lacks `agora.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"List not found, or private and viewer is not owner.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Get one list by id.","tags":["agora-lists"]},"patch":{"operationId":"ActorListController_update","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"listId","required":true,"in":"path","description":"List UUID.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateActorListDto"}}}},"responses":{"200":{"description":"List updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActorListResponseDto"}}}},"400":{"description":"Validation failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller is not the owner, or PAK lacks `agora.update`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"List not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Update list name / description / visibility. Owner-only.","tags":["agora-lists"]},"delete":{"operationId":"ActorListController_delete","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"listId","required":true,"in":"path","description":"List UUID.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteActorListDto"}}}},"responses":{"204":{"description":"List deleted."},"403":{"description":"Caller is not the owner.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"List not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Delete a list. Owner-only.","tags":["agora-lists"]}},"/v1/communities/{communityId}/lists/{listId}/members":{"post":{"operationId":"ActorListController_addMember","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"listId","required":true,"in":"path","description":"List UUID.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddMemberDto"}}}},"responses":{"201":{"description":"Member added (or returned via idempotent retry).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActorListMemberResponseDto"}}}},"403":{"description":"Caller is not the owner.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"List or actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"A block exists between owner and member.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Add a member to a list. Owner-only.","tags":["agora-lists"]},"get":{"operationId":"ActorListController_listMembers","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"listId","required":true,"in":"path","description":"List UUID.","schema":{"type":"string"}},{"name":"viewer_actor_id","required":false,"in":"query","schema":{"type":"string","format":"uuid"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Page of members.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActorListMemberListResponseDto"}}}},"403":{"description":"PAK lacks `agora.list`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"List not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List members of a list. Public lists are visible to anyone; private lists surface only when viewer_actor_id is the owner.","tags":["agora-lists"]}},"/v1/communities/{communityId}/lists/{listId}/members/{actorId}":{"delete":{"operationId":"ActorListController_removeMember","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"listId","required":true,"in":"path","description":"List UUID.","schema":{"type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Member — agora actor UUID.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveMemberDto"}}}},"responses":{"204":{"description":"Member removed."},"403":{"description":"Caller is not the owner.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"List not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Remove a member from a list. Owner-only. Idempotent — 204 either way.","tags":["agora-lists"]}},"/v1/communities/{communityId}/actors/{actorId}/muted-terms":{"post":{"operationId":"MutedTermController_create","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID.","schema":{"example":"aaaaaaaa-0000-0000-0000-000000000001","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMutedTermDto"}}}},"responses":{"201":{"description":"Muted term applied.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MutedTermResponseDto"}}}},"400":{"description":"Validation failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.create`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Add (or refresh) a muted term for the actor. Re-posting the same `term` updates the scope + expires_at.","tags":["agora-muted-terms"]},"get":{"operationId":"MutedTermController_list","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID.","schema":{"example":"aaaaaaaa-0000-0000-0000-000000000001","type":"string"}}],"responses":{"200":{"description":"Active muted terms.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MutedTermResponseDto"}}}}},"403":{"description":"PAK lacks `agora.list`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List the actor's currently-active muted terms.","tags":["agora-muted-terms"]}},"/v1/communities/{communityId}/actors/{actorId}/muted-terms/{term}":{"delete":{"operationId":"MutedTermController_delete","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID.","schema":{"example":"aaaaaaaa-0000-0000-0000-000000000001","type":"string"}},{"name":"term","required":true,"in":"path","description":"URL-encoded term.","schema":{"type":"string"}}],"responses":{"204":{"description":"Muted term removed."},"403":{"description":"PAK lacks `agora.delete`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Remove a muted term. Idempotent — 204 either way.","tags":["agora-muted-terms"]}},"/v1/communities/{communityId}/flags":{"post":{"operationId":"FlagController_create","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFlagDto"}}}},"responses":{"201":{"description":"Flag created (or de-duplicated to existing open flag).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlagResponseDto"}}}},"400":{"description":"Validation failure (bad reporter / target / reason).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.flag` for this community, or reporter actor is suspended/deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community, reporter actor, or target object not found (or belongs to a different community).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"Cannot flag your own content, or the target post/comment is already removed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"File a moderation report against a post or comment. Idempotent per (reporter, target) while open.","tags":["agora-flags"]}},"/v1/workspaces/{workspaceId}/communities/{communityId}/moderation/flags":{"get":{"operationId":"ModerationAdminController_listFlags","parameters":[{"name":"workspaceId","required":true,"in":"path","schema":{"type":"string"}},{"name":"communityId","required":true,"in":"path","schema":{"type":"string"}},{"name":"status","required":true,"in":"query","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Page of flags with next-cursor and has-more.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlagListResponseDto"}}}},"400":{"description":"Invalid status filter.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `agora.moderation.queue.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community not found in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"summary":"List flags in this community. Filter by ?status=open|dismissed|actioned. 50/page (max 200), cursor-paginated.","tags":["agora-moderation-admin"]}},"/v1/workspaces/{workspaceId}/communities/{communityId}/moderation/flags/{flagId}/actions":{"post":{"operationId":"ModerationAdminController_act","parameters":[{"name":"workspaceId","required":true,"in":"path","schema":{"type":"string"}},{"name":"communityId","required":true,"in":"path","schema":{"type":"string"}},{"name":"flagId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationActionDto"}}}},"responses":{"201":{"description":"Action recorded; target status updated where applicable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationActionResponseDto"}}}},"400":{"description":"Invalid action or notes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `agora.moderate`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or flag not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"Flag already actioned/dismissed and the requested action would be a no-op.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"summary":"Apply a moderation action to a flag. Records an audit entry and updates the target post/comment status.","tags":["agora-moderation-admin"]}},"/v1/workspaces/{workspaceId}/communities/{communityId}/moderation/audit":{"get":{"operationId":"ModerationAdminController_listAudit","parameters":[{"name":"workspaceId","required":true,"in":"path","schema":{"type":"string"}},{"name":"communityId","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Page of audit entries with next-cursor and has-more.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditListResponseDto"}}}},"403":{"description":"Caller lacks `agora.audit.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community not found in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"summary":"Append-only audit of moderator actions in this community. 50/page (max 200), cursor-paginated.","tags":["agora-moderation-admin"]}},"/v1/workspaces/{workspaceId}/communities/{communityId}/moderation/actors/{actorId}/shadow-ban":{"post":{"operationId":"ModerationAdminController_shadowBan","parameters":[{"name":"workspaceId","required":true,"in":"path","schema":{"type":"string"}},{"name":"communityId","required":true,"in":"path","schema":{"type":"string"}},{"name":"actorId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplyShadowBanDto"}}}},"responses":{"200":{"description":"Shadow ban applied.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActorResponseDto"}}}},"400":{"description":"Invalid duration_hours or reason.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `agora.moderate`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"summary":"Apply a shadow ban to an actor. The actor sees their own content normally; everyone else sees nothing from them. Pass `duration_hours` for a timed ban or omit for indefinite.","tags":["agora-moderation-admin"]},"delete":{"operationId":"ModerationAdminController_unshadow","parameters":[{"name":"workspaceId","required":true,"in":"path","schema":{"type":"string"}},{"name":"communityId","required":true,"in":"path","schema":{"type":"string"}},{"name":"actorId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Shadow ban cleared.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActorResponseDto"}}}},"403":{"description":"Caller lacks `agora.moderate`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"summary":"Remove a shadow ban. No-op if not currently shadow-banned.","tags":["agora-moderation-admin"]}},"/v1/workspaces/{workspaceId}/communities/{communityId}/moderation/shadow-banned":{"get":{"operationId":"ModerationAdminController_listShadowBanned","parameters":[{"name":"workspaceId","required":true,"in":"path","schema":{"type":"string"}},{"name":"communityId","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Page of shadow-banned actors.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActorListResponseDto"}}}},"403":{"description":"Caller lacks `agora.audit.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"summary":"List currently shadow-banned actors in this community. 50/page (max 200), cursor-paginated newest-ban first.","tags":["agora-moderation-admin"]}},"/v1/communities/{communityId}/actors/{actorId}/notifications":{"get":{"description":"Reverse-chronological inbox. Pass `unread_only=true` to filter to unread rows only. Cursor encodes `(created_at, id)` and round-trips through the response. Default page size 50, max 200. Suppression of self-actions and blocked-actor pairs happens at write time, so every row returned here has already passed those filters.","operationId":"NotificationController_list","parameters":[{"name":"communityId","required":true,"in":"path","description":"Community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID (returned from POST /actors).","schema":{"example":"aaaaaaaa-0000-0000-0000-000000000001","type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"description":"Items per page (1..200, default 50)."},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"},"description":"Opaque base64url cursor returned by the prior page."},{"name":"unread_only","required":false,"in":"query","schema":{"type":"boolean","default":false},"description":"When `true`, returns only rows where `read_at IS NULL`."},{"name":"status","required":false,"in":"query","schema":{"type":"string","enum":["inbox","requests","all"],"default":"inbox"},"description":"Filter slice. `inbox` (default) hides suppressed rows; `requests` shows only suppressed rows (notifications from actors the viewer has restricted); `all` merges both."}],"responses":{"200":{"description":"Page of notifications.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationListResponse"}}}},"401":{"description":"Missing or invalid PAK.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"PAK lacks `agora.notify.read` for this community.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Community or actor not found (also returned for cross-workspace probes).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"bearer":[]}],"summary":"List notifications for an actor (newest first).","tags":["agora-notifications"]}},"/v1/communities/{communityId}/actors/{actorId}/notifications/unread-count":{"get":{"description":"Cheap badge endpoint backed by a partial index — safe to poll at the cadence of an in-product notification bell.","operationId":"NotificationController_unreadCount","parameters":[{"name":"communityId","required":true,"in":"path","description":"Community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID (returned from POST /actors).","schema":{"example":"aaaaaaaa-0000-0000-0000-000000000001","type":"string"}}],"responses":{"200":{"description":"Count of unread rows.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnreadCountResponse"}}}},"401":{"description":"Missing or invalid PAK.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"PAK lacks `agora.notify.read` for this community.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Community or actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"bearer":[]}],"summary":"Count unread notifications for an actor.","tags":["agora-notifications"]}},"/v1/communities/{communityId}/actors/{actorId}/notifications/{notificationId}":{"patch":{"description":"Idempotent: marking an already-read row read again is a no-op 204. Returns 404 if the notification belongs to a different actor.","operationId":"NotificationController_markRead","parameters":[{"name":"communityId","required":true,"in":"path","description":"Community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID (returned from POST /actors).","schema":{"example":"aaaaaaaa-0000-0000-0000-000000000001","type":"string"}},{"name":"notificationId","required":true,"in":"path","description":"Notification UUID returned from the list endpoint.","schema":{"example":"11111111-1111-1111-1111-111111111111","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateNotificationDto"}}}},"responses":{"204":{"description":"Read state updated."},"400":{"description":"Body validation failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid PAK.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"PAK lacks `agora.notify.update` for this community.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Notification not found, or belongs to a different actor.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"bearer":[]}],"summary":"Mark a single notification as read or unread.","tags":["agora-notifications"]}},"/v1/communities/{communityId}/actors/{actorId}/notifications/read-all":{"post":{"description":"Single bulk write. Optional `before` ISO timestamp scopes to rows older than that — useful for \"mark all older than today as read\" UX. Returns the number of rows that flipped.","operationId":"NotificationController_markAllRead","parameters":[{"name":"communityId","required":true,"in":"path","description":"Community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID (returned from POST /actors).","schema":{"example":"aaaaaaaa-0000-0000-0000-000000000001","type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkAllReadDto"}}}},"responses":{"200":{"description":"Bulk update applied; `updated` is the row count.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkAllReadResponse"}}}},"400":{"description":"`before` is not a valid ISO timestamp.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid PAK.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"PAK lacks `agora.notify.update` for this community.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Community or actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"bearer":[]}],"summary":"Mark every unread notification for the actor as read.","tags":["agora-notifications"]}},"/v1/communities/{communityId}/actors/{actorId}/notification-prefs":{"get":{"description":"Returns one row per known NotificationKind so the client UI can render every toggle without a separate catalogue lookup. Synthesised rows carry an epoch `updated_at`; explicit rows carry the real timestamp.","operationId":"NotificationPrefController_list","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID (returned from POST /actors).","schema":{"example":"aaaaaaaa-0000-0000-0000-000000000001","type":"string"}}],"responses":{"200":{"description":"Per-kind preferences.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationPrefListResponseDto"}}}},"403":{"description":"PAK lacks `agora.notify.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or actor not found (or belongs to a different community).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List the actor's per-kind notification preferences. Synthesises `enabled=true` defaults for kinds the actor has not explicitly toggled.","tags":["agora-notification-preferences"]}},"/v1/communities/{communityId}/actors/{actorId}/notification-prefs/{kind}":{"patch":{"description":"Idempotent upsert. Pass `{ enabled: false }` to mute, `{ enabled: true }` to re-enable. Cache is invalidated on write so the next NotificationService.tryEmit picks up the change immediately. Returns the resulting preference row.","operationId":"NotificationPrefController_set","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID.","schema":{"example":"aaaaaaaa-0000-0000-0000-000000000001","type":"string"}},{"name":"kind","required":true,"in":"path","description":"NotificationKind label (`follow` | `reaction` | `comment_on_post` | `reply_to_comment` | `mention` | `vote`).","schema":{"example":"follow","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateNotificationPrefDto"}}}},"responses":{"200":{"description":"Preference upserted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationPrefResponseDto"}}}},"400":{"description":"Body validation failed, or `kind` is not a known NotificationKind.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.notify.update`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or actor not found (or belongs to a different community).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Set a single notification-kind preference for the actor.","tags":["agora-notification-preferences"]}},"/v1/communities/{communityId}/actors/{actorId}/suggested-follows":{"get":{"description":"Friend-of-a-friend signal first (people the requester's followees also follow), with a cold-start fallback to top actors in the community by follower count when FoF underfills the limit. Excludes self, existing follows / mutes / blocks, and actors who have blocked the requester. Cached 1h per (actor, limit).","operationId":"SuggestionController_list","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID for whom to compute suggestions.","schema":{"example":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa","type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":50,"default":20},"description":"Items to return; capped at 50."}],"responses":{"200":{"description":"Ordered candidates.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuggestedFollowsListResponseDto"}}}},"403":{"description":"PAK lacks `agora.list`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or actor not found (or belongs to a different community).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Suggested follows for an actor.","tags":["agora-suggestions"]}},"/v1/communities/{communityId}/posts/{postId}/bookmark":{"post":{"description":"Visibility-aware: the actor must be allowed to read the post. If they can't, returns 404 (opaque), matching the read-path opacity rule used everywhere else in agora.","operationId":"BookmarkController_create","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"postId","required":true,"in":"path","description":"Agora post UUID.","schema":{"example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBookmarkDto"}}}},"responses":{"201":{"description":"Bookmark created (or returned via idempotent retry).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookmarkToggleResponseDto"}}}},"400":{"description":"Validation failure (missing/invalid actor_id).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.create`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community, post, or actor not found, or the post is invisible to the actor.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Bookmark a post on behalf of an actor. Idempotent — returns the existing row with `created: false` on retry.","tags":["agora-bookmarks"]},"delete":{"operationId":"BookmarkController_delete","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"postId","required":true,"in":"path","description":"Agora post UUID.","schema":{"example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBookmarkDto"}}}},"responses":{"204":{"description":"Bookmark removed (or was already absent)."},"400":{"description":"Validation failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.delete`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community, post, or actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Remove a bookmark on behalf of an actor. Idempotent — 204 either way.","tags":["agora-bookmarks"]}},"/v1/communities/{communityId}/actors/{actorId}/bookmarks":{"get":{"description":"Bookmarks are private — only the bookmarker should see their own list. The PAK lane has no end-user principal; the customer's backend must scope this call to the right end-user before exposing the response. 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.","operationId":"BookmarkController_list","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID.","schema":{"example":"aaaaaaaa-0000-0000-0000-000000000001","type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"},"description":"Opaque cursor from `pagination.next_cursor`."}],"responses":{"200":{"description":"Page of bookmarks.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookmarkListResponseDto"}}}},"403":{"description":"PAK lacks `agora.list`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or actor not found (or belongs to a different community).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List the actor's bookmarks. Cursor-paginated newest-first.","tags":["agora-bookmarks"]}},"/v1/communities/{communityId}/actors/{actorId}/mentions":{"get":{"description":"Cursor-paginated, newest-first. Interleaves post + comment mentions in a single feed; the `source` field discriminates which surface each row came from. The `post_id` field is always set so the client can deep-link, even for comment mentions.","operationId":"MentionController_list","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID whose mentions to list.","schema":{"example":"aaaaaaaa-0000-0000-0000-000000000001","type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"},"description":"Opaque cursor from `pagination.next_cursor`."}],"responses":{"200":{"description":"Page of mention entries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MentionListResponseDto"}}}},"403":{"description":"PAK lacks `agora.list`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or actor not found (or belongs to a different community).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List every place this actor has been @-mentioned (posts + comments).","tags":["agora-mentions"]}},"/v1/communities/{communityId}/search/actors":{"get":{"description":"Postgres FTS over a generated tsvector column (display_name A + external_id B + bio C, `simple` locale). Returns up to `limit` (default 20, max 50) hits ordered by `ts_rank`. Pass `requester_id` to filter out actors the requester has muted or blocked, and actors who have blocked the requester. Trigram fuzzy matching is intentionally out of scope — that's a follow-up.","operationId":"SearchController_searchActors","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"q","required":true,"in":"query","schema":{"type":"string","minLength":1,"maxLength":120},"description":"Free-text query (1..120 chars)."},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":50,"default":20},"description":"Max hits to return."},{"name":"requester_id","required":false,"in":"query","schema":{"type":"string","format":"uuid"},"description":"Viewer agora actor UUID — filters out blocked / muted actors."}],"responses":{"200":{"description":"Ranked actor hits.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActorSearchResponseDto"}}}},"400":{"description":"q missing or out of bounds (1..120 chars).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.list`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community not found (or belongs to a different community).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Search actors by display name, external_id (username), and bio.","tags":["agora-search"]}},"/v1/communities/{communityId}/search/posts":{"get":{"description":"Postgres FTS over a generated tsvector column on the post table (title A + body B, simple locale). `order='relevance'` (default) ranks by ts_rank; `order='recent'` keeps the FTS filter but orders by created_at desc. Filters out drafts / removed / hidden / expired posts. Pass `requester_id` to drop posts authored by anyone the requester has muted / blocked, or who has blocked the requester. Without `requester_id`, only posts with `visibility='public'` surface.","operationId":"SearchController_searchPosts","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"q","required":true,"in":"query","schema":{"type":"string","minLength":1,"maxLength":120}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}},{"name":"order","required":false,"in":"query","schema":{"type":"string","enum":["relevance","recent"],"default":"relevance"}},{"name":"requester_id","required":false,"in":"query","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Ranked post hits.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostSearchResponseDto"}}}},"400":{"description":"q missing / overlong, or invalid `order` value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.list`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Search post content (title + body) by full-text.","tags":["agora-search"]}},"/v1/communities/{communityId}/actors/{actorId}/story-tray":{"get":{"description":"Includes the requester themselves and every actor they follow. Ordered by latest story timestamp. Capped at 200 entries (no cursor in v1; tray is bounded by follow-graph in normal usage). Returns `has_unviewed=true` when the requester has not recorded a `view` edge against the author's most recent story.","operationId":"StoryController_getTray","parameters":[{"name":"communityId","required":true,"in":"path","description":"Community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID (returned from POST /actors).","schema":{"example":"aaaaaaaa-0000-0000-0000-000000000001","type":"string"}}],"responses":{"200":{"description":"Story tray entries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoryTrayResponse"}}}},"401":{"description":"Missing or invalid PAK.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"PAK lacks `agora.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Community or actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"bearer":[]}],"summary":"Story tray for an actor — one entry per author with at least one unexpired or pinned story.","tags":["agora-stories"]}},"/v1/communities/{communityId}/actors/{actorId}/highlights":{"get":{"description":"Pinned posts appear here regardless of their `expires_at`. Visibility still applies — pass `requester_id` to surface non-public pinned posts the requester is allowed to see. Without that param, only `public` pinned rows are returned (legacy-safe default). Cursor-paginated on `(created_at DESC, id DESC)`.","operationId":"StoryController_getHighlights","parameters":[{"name":"communityId","required":true,"in":"path","description":"Community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Agora actor UUID whose highlights to list.","schema":{"example":"aaaaaaaa-0000-0000-0000-000000000001","type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"description":"Items per page."},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"},"description":"Cursor from the prior page."},{"name":"requester_id","required":false,"in":"query","schema":{"type":"string"},"description":"Requester's external_id. Without it, only public pinned posts surface."}],"responses":{"200":{"description":"Page of highlights.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HighlightsResponse"}}}},"401":{"description":"Missing or invalid PAK.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"PAK lacks `agora.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Community or actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"bearer":[]}],"summary":"List the actor's pinned posts (highlights).","tags":["agora-stories"]}},"/v1/communities/{communityId}/posts/{postId}/views":{"post":{"description":"Idempotent on `(community, viewer, post)`. 204 on success or no-op duplicate. 422 on self-view (you cannot view your own story). 404 if the post is hidden by visibility, expired, removed, or if a block exists between viewer and author. Does NOT fire a notification — the author sees views via the viewers endpoint instead.","operationId":"StoryController_recordView","parameters":[{"name":"communityId","required":true,"in":"path","description":"Community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"postId","required":true,"in":"path","description":"Story (post) UUID.","schema":{"example":"bbbbbbbb-0000-0000-0000-000000000001","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordViewDto"}}}},"responses":{"204":{"description":"View recorded (or already existed)."},"400":{"description":"Body validation failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid PAK.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"PAK lacks `agora.view`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Community / post / viewer not found, or the post is invisible to the viewer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Cannot view your own story.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"bearer":[]}],"summary":"Record a view of a post (idempotent).","tags":["agora-stories"]}},"/v1/communities/{communityId}/posts/{postId}/viewers":{"get":{"description":"Only the post author can list viewers. The PAK passes `requester_id`; if that resolves to an actor whose id != post.actor_id, returns 403. Viewers are ordered by view timestamp, newest-first.","operationId":"StoryController_listViewers","parameters":[{"name":"communityId","required":true,"in":"path","description":"Community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"postId","required":true,"in":"path","description":"Story (post) UUID.","schema":{"example":"bbbbbbbb-0000-0000-0000-000000000001","type":"string"}},{"name":"requester_id","required":true,"in":"query","schema":{"type":"string"},"description":"Must resolve to the post author."},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"},"description":"Cursor from the prior page."}],"responses":{"200":{"description":"Page of viewers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoryViewersResponse"}}}},"401":{"description":"Missing or invalid PAK.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"PAK lacks `agora.read` OR requester is not the post author.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Community or post not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"bearer":[]}],"summary":"List viewers of a post (author-only).","tags":["agora-stories"]}},"/v1/communities/{communityId}/close-friends":{"post":{"description":"Idempotent on `(community, owner, member)`. 422 on self-add. 409 if a block exists between the pair in either direction. Visibility-`close_friends` posts authored by `owner` become visible to `member` after this call.","operationId":"StoryController_addCloseFriend","parameters":[{"name":"communityId","required":true,"in":"path","description":"Community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCloseFriendDto"}}}},"responses":{"204":{"description":"Member added (or already present)."},"400":{"description":"Body validation failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid PAK.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"PAK lacks `agora.close-friend.manage`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Community / owner / member not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Block in place between owner and member.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Cannot add yourself as a close friend.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"bearer":[]}],"summary":"Add a member to an actor's close-friends list.","tags":["agora-stories"]}},"/v1/communities/{communityId}/close-friends/{ownerActorId}/{memberActorId}":{"delete":{"description":"Idempotent: 204 whether or not the edge exists. After removal, `close_friends`-visibility posts by `owner` no longer surface to `member`.","operationId":"StoryController_removeCloseFriend","parameters":[{"name":"communityId","required":true,"in":"path","description":"Community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"ownerActorId","required":true,"in":"path","description":"Owner agora actor UUID.","schema":{"example":"aaaaaaaa-0000-0000-0000-000000000001","type":"string"}},{"name":"memberActorId","required":true,"in":"path","description":"Member agora actor UUID.","schema":{"example":"aaaaaaaa-0000-0000-0000-000000000002","type":"string"}}],"responses":{"204":{"description":"Removed (or was not in the list)."},"401":{"description":"Missing or invalid PAK.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"PAK lacks `agora.close-friend.manage`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Community not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"bearer":[]}],"summary":"Remove a member from an actor's close-friends list.","tags":["agora-stories"]}},"/v1/communities/{communityId}/actors/{actorId}/close-friends":{"get":{"description":"Returns the actors on `externalId`'s close-friends list, with denormalised actor metadata. Cursor paginated.","operationId":"StoryController_listCloseFriends","parameters":[{"name":"communityId","required":true,"in":"path","description":"Community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Owner agora actor UUID.","schema":{"example":"aaaaaaaa-0000-0000-0000-000000000001","type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Page of close friends.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CloseFriendsListResponse"}}}},"401":{"description":"Missing or invalid PAK.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"PAK lacks `agora.close-friend.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Community or actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"bearer":[]}],"summary":"List the close-friends members of an actor.","tags":["agora-stories"]}},"/v1/communities/{communityId}/posts/{postId}/votes":{"post":{"description":"Last-vote-wins: an actor changing their vote replaces the prior one. 422 if the post has no poll, if `option_index` is out of range, or on self-vote of own poll. 409 if a block exists between voter and post author. Fires a `vote` notification to the post author.","operationId":"StoryController_castVote","parameters":[{"name":"communityId","required":true,"in":"path","description":"Community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"postId","required":true,"in":"path","description":"Post UUID containing the poll.","schema":{"example":"bbbbbbbb-0000-0000-0000-000000000001","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CastVoteDto"}}}},"responses":{"204":{"description":"Vote recorded or replaced."},"400":{"description":"Body validation failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid PAK.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"PAK lacks `agora.vote`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Community / post / voter not found, or post invisible.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Block in place between voter and poll author.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Self-vote / no poll / option_index out of range.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"bearer":[]}],"summary":"Cast or change a vote on a poll.","tags":["agora-stories"]}},"/v1/communities/{communityId}/posts/{postId}/poll-results":{"get":{"description":"Returns counts per option, zero-filling options that received no votes, plus `total_votes`. The `question` and option `label` strings are echoed from `post.attributes.poll`.","operationId":"StoryController_getPollResults","parameters":[{"name":"communityId","required":true,"in":"path","description":"Community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"postId","required":true,"in":"path","description":"Post UUID containing the poll.","schema":{"example":"bbbbbbbb-0000-0000-0000-000000000001","type":"string"}}],"responses":{"200":{"description":"Aggregated poll results.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PollResultsResponse"}}}},"401":{"description":"Missing or invalid PAK.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"PAK lacks `agora.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Community or post not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Post does not contain a poll.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"bearer":[]}],"summary":"Aggregate poll results for a post.","tags":["agora-stories"]}},"/v1/communities/{communityId}/conversations/unread-count":{"get":{"operationId":"ConversationController_unreadCount","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actor_id","required":true,"in":"query","schema":{"type":"string","format":"uuid"},"description":"The viewer actor UUID."}],"responses":{"200":{"description":"Unread count.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnreadCountResponseDto"}}}},"403":{"description":"PAK lacks `agora.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Global unread message count for the caller across every conversation in this community.","tags":["agora-conversations"]}},"/v1/communities/{communityId}/conversations":{"post":{"operationId":"ConversationController_create","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateConversationDto"}}}},"responses":{"201":{"description":"Conversation created or (1-on-1 only) returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationResponseDto"}}}},"400":{"description":"Validation failure (missing recipient for direct, missing actor_ids for group, two equal actor ids).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.create`, or a participant is suspended.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"A block exists, or the group exceeds `community.settings.max_group_conversation_size`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Open a conversation. Default `kind=\"direct\"` is the idempotent 1-on-1 path. Pass `kind=\"group\"` with `actor_ids` for a multi-participant chat (always creates a new row; no idempotency for groups).","tags":["agora-conversations"]},"get":{"operationId":"ConversationController_list","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"actor_id","required":true,"in":"query","schema":{"type":"string","format":"uuid"},"description":"The viewer actor UUID."},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}},{"name":"status","required":false,"in":"query","schema":{"type":"string","enum":["inbox","requests","all"],"default":"inbox"},"description":"Filter slice. `inbox` (default) excludes conversations whose other participant the viewer has restricted; `requests` shows only those; `all` merges both."}],"responses":{"200":{"description":"Page of conversations.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationListResponseDto"}}}},"403":{"description":"PAK lacks `agora.list`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List the caller's conversations in this community, most-recent-activity first.","tags":["agora-conversations"]}},"/v1/communities/{communityId}/conversations/{conversationId}/members":{"post":{"operationId":"ConversationController_addMember","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"conversationId","required":true,"in":"path","description":"Conversation UUID.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddMemberDto"}}}},"responses":{"201":{"description":"Member added.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationResponseDto"}}}},"400":{"description":"Validation failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller is not an admin, or PAK lacks `agora.update`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Conversation or actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"Already a member, group full, or block exists with an existing participant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Add a member to a group conversation. Admin-only. Refuses if the new member has a block with any existing participant.","tags":["agora-conversations"]}},"/v1/communities/{communityId}/conversations/{conversationId}/members/{actorId}":{"patch":{"operationId":"ConversationController_updateMemberRole","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"conversationId","required":true,"in":"path","description":"Conversation UUID.","schema":{"type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Target participant — agora actor UUID.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMemberRoleDto"}}}},"responses":{"200":{"description":"Role updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationResponseDto"}}}},"403":{"description":"Caller is not an admin.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Conversation or participant not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"Attempting to demote the last admin.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Promote / demote a participant. Admin-only.","tags":["agora-conversations"]},"delete":{"operationId":"ConversationController_removeMember","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"conversationId","required":true,"in":"path","description":"Conversation UUID.","schema":{"type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Target participant — agora actor UUID.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveMemberDto"}}}},"responses":{"204":{"description":"Participant removed."},"403":{"description":"Caller is not an admin and not the target.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Conversation or participant not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"Attempting to remove the last admin (or self-leave when last admin in a non-empty group).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Remove a participant. Admin-only unless caller_actor_id equals the target (self-leave).","tags":["agora-conversations"]}},"/v1/communities/{communityId}/conversations/{conversationId}/name":{"patch":{"operationId":"ConversationController_renameGroup","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"conversationId","required":true,"in":"path","description":"Conversation UUID.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenameGroupDto"}}}},"responses":{"200":{"description":"Group renamed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationResponseDto"}}}},"400":{"description":"Validation failure (name too long).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller is not an admin.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Conversation not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"Conversation is not a group.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Rename a group conversation. Admin-only.","tags":["agora-conversations"]}},"/v1/communities/{communityId}/conversations/{conversationId}":{"get":{"operationId":"ConversationController_getById","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"conversationId","required":true,"in":"path","description":"Conversation UUID.","schema":{"type":"string"}},{"name":"actor_id","required":true,"in":"query","schema":{"type":"string","format":"uuid"},"description":"The viewer actor UUID — must be a participant."}],"responses":{"200":{"description":"Conversation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationResponseDto"}}}},"403":{"description":"PAK lacks `agora.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Conversation not found or caller not a participant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Get a single conversation by id.","tags":["agora-conversations"]},"patch":{"operationId":"ConversationController_update","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"conversationId","required":true,"in":"path","description":"Conversation UUID.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateConversationDto"}}}},"responses":{"200":{"description":"Conversation after update.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationResponseDto"}}}},"400":{"description":"Validation failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.update`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Conversation not found or caller not a participant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Update the caller's participant row — toggle muted today.","tags":["agora-conversations"]},"delete":{"operationId":"ConversationController_leave","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"conversationId","required":true,"in":"path","description":"Conversation UUID.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeaveConversationDto"}}}},"responses":{"204":{"description":"Caller removed from the conversation."},"403":{"description":"PAK lacks `agora.delete`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Conversation not found or caller not a participant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Leave the conversation. Drops the caller's participant row; the conversation is hard-deleted when the last participant leaves.","tags":["agora-conversations"]}},"/v1/communities/{communityId}/conversations/{conversationId}/messages":{"post":{"operationId":"ConversationController_send","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"conversationId","required":true,"in":"path","description":"Conversation UUID.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageDto"}}}},"responses":{"201":{"description":"Message sent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DirectMessageResponseDto"}}}},"400":{"description":"Validation failure (empty body for text, body too long).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.create`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Conversation, reply target, or sender not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"A block exists between the sender and another participant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Send a message in the conversation.","tags":["agora-conversations"]},"get":{"operationId":"ConversationController_listMessages","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"conversationId","required":true,"in":"path","description":"Conversation UUID.","schema":{"type":"string"}},{"name":"actor_id","required":true,"in":"query","schema":{"type":"string","format":"uuid"},"description":"The viewer actor UUID — must be a participant."},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}},{"name":"since","required":false,"in":"query","schema":{"type":"string"},"description":"Polling cursor — encoded last-seen message id; returns newer messages in ascending order."}],"responses":{"200":{"description":"Page of messages.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DirectMessageListResponseDto"}}}},"403":{"description":"PAK lacks `agora.list`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Conversation not found or caller not a participant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List messages in the conversation. Default newest-first cursor. Pass `since` (encoded cursor of the last-seen message) to poll for newer messages instead.","tags":["agora-conversations"]}},"/v1/communities/{communityId}/conversations/{conversationId}/messages/{messageId}":{"patch":{"operationId":"ConversationController_editMessage","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"conversationId","required":true,"in":"path","description":"Conversation UUID.","schema":{"type":"string"}},{"name":"messageId","required":true,"in":"path","description":"Direct message UUID.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMessageDto"}}}},"responses":{"200":{"description":"Message updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DirectMessageResponseDto"}}}},"400":{"description":"Validation failure (empty body, body too long).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller is not the sender, or PAK lacks `agora.update`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Conversation or message not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"Edit window expired (5 minutes after send).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Edit a message body. Allowed within 5 minutes of send and only by the sender.","tags":["agora-conversations"]},"delete":{"operationId":"ConversationController_deleteMessage","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"conversationId","required":true,"in":"path","description":"Conversation UUID.","schema":{"type":"string"}},{"name":"messageId","required":true,"in":"path","description":"Direct message UUID.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteMessageDto"}}}},"responses":{"204":{"description":"Message soft-deleted."},"403":{"description":"Caller is not the sender, or PAK lacks `agora.delete`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Conversation or message not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Soft-delete a message. Reserved to the sender; the row stays so the conversation timeline doesn't orphan replies. Reads return body `\"(deleted)\"`.","tags":["agora-conversations"]}},"/v1/communities/{communityId}/conversations/{conversationId}/read":{"post":{"operationId":"ConversationController_markRead","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID. Must belong to the PAK's workspace.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"conversationId","required":true,"in":"path","description":"Conversation UUID.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkConversationReadDto"}}}},"responses":{"204":{"description":"Read marker updated."},"400":{"description":"Validation failure (read_through not ISO).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.update`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Conversation not found or caller not a participant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Mark messages as read up to a given timestamp (default: now()). Sets the caller's `last_read_at`.","tags":["agora-conversations"]}},"/v1/workspaces/{workspaceId}/communities/{communityId}/moderation/conversations":{"get":{"operationId":"ConversationModerationController_listConversations","parameters":[{"name":"workspaceId","required":true,"in":"path","schema":{"type":"string"}},{"name":"communityId","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Page of conversations.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationAdminListResponseDto"}}}},"403":{"description":"Caller lacks `agora.audit.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community not found in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"summary":"List every conversation in the community, most-recent-activity first. Moderation-only.","tags":["agora-conversations-admin"]}},"/v1/workspaces/{workspaceId}/communities/{communityId}/moderation/conversations/{conversationId}/messages":{"get":{"operationId":"ConversationModerationController_listMessages","parameters":[{"name":"workspaceId","required":true,"in":"path","schema":{"type":"string"}},{"name":"communityId","required":true,"in":"path","schema":{"type":"string"}},{"name":"conversationId","required":true,"in":"path","description":"Conversation UUID.","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Page of messages.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DirectMessageAdminListResponseDto"}}}},"403":{"description":"Caller lacks `agora.audit.read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or conversation not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"summary":"List every message in a conversation. Moderation-only.","tags":["agora-conversations-admin"]}},"/v1/communities/{communityId}/conversations/{conversationId}/messages/{messageId}/reactions":{"post":{"operationId":"DirectMessageReactionController_add","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"conversationId","required":true,"in":"path","description":"Conversation UUID.","schema":{"example":"11111111-2222-3333-4444-555555555555","type":"string"}},{"name":"messageId","required":true,"in":"path","description":"Direct message UUID.","schema":{"example":"mmmmmmmm-0000-0000-0000-000000000001","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDirectMessageReactionDto"}}}},"responses":{"201":{"description":"Reaction applied (created or pre-existing).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DirectMessageReactionCreatedResponseDto"}}}},"400":{"description":"Validation failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.create`, or reactor is suspended.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Conversation, message, or actor not found, or caller not a participant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"Block exists between reactor and sender.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Add a reaction to a direct message. Idempotent — re-posting the same (actor, type) is a no-op.","tags":["agora-dm-reactions"]},"get":{"operationId":"DirectMessageReactionController_list","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"conversationId","required":true,"in":"path","description":"Conversation UUID.","schema":{"example":"11111111-2222-3333-4444-555555555555","type":"string"}},{"name":"messageId","required":true,"in":"path","description":"Direct message UUID.","schema":{"example":"mmmmmmmm-0000-0000-0000-000000000001","type":"string"}},{"name":"actor_id","required":true,"in":"query","schema":{"type":"string","format":"uuid"},"description":"The viewer — must be a participant."},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Page of reactions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DirectMessageReactionListResponseDto"}}}},"403":{"description":"PAK lacks `agora.list`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Conversation or message not found, or caller not a participant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List the reactions on a direct message. Cursor-paginated, newest-first.","tags":["agora-dm-reactions"]}},"/v1/communities/{communityId}/conversations/{conversationId}/messages/{messageId}/reactions/{actorId}/{type}":{"delete":{"operationId":"DirectMessageReactionController_remove","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"conversationId","required":true,"in":"path","description":"Conversation UUID.","schema":{"example":"11111111-2222-3333-4444-555555555555","type":"string"}},{"name":"messageId","required":true,"in":"path","description":"Direct message UUID.","schema":{"example":"mmmmmmmm-0000-0000-0000-000000000001","type":"string"}},{"name":"actorId","required":true,"in":"path","description":"Reactor actor UUID.","schema":{"type":"string"}},{"name":"type","required":true,"in":"path","description":"Reaction type to remove.","schema":{"type":"string"}}],"responses":{"200":{"description":"Reaction removed (or already absent).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DirectMessageReactionRemovedResponseDto"}}}},"403":{"description":"PAK lacks `agora.delete`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Conversation, message, or actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Remove the (actor, type) reaction. Idempotent — calling on a missing reaction returns the current counts.","tags":["agora-dm-reactions"]}},"/v1/communities/{communityId}/posts/{postId}/reactions":{"post":{"operationId":"PostReactionController_react","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"postId","required":true,"in":"path","description":"Agora post UUID.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateReactionDto"}}}},"responses":{"201":{"description":"Reaction added.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReactionResponseDto"}}}},"400":{"description":"Validation failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.create`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community, post, or actor not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"Block exists between actor and post author.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"React to a post. Idempotent: re-posting the same (actor, type) returns the existing edge with `created: false`.","tags":["agora-post-reactions"]}},"/v1/communities/{communityId}/posts/{postId}/reactions/{srcActorId}/{type}":{"delete":{"operationId":"PostReactionController_unreact","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"postId","required":true,"in":"path","description":"Agora post UUID.","schema":{"type":"string"}},{"name":"srcActorId","required":true,"in":"path","description":"Reactor actor UUID.","schema":{"type":"string"}},{"name":"type","required":true,"in":"path","description":"Reaction type label.","schema":{"type":"string"}}],"responses":{"200":{"description":"Reaction removed (or already absent).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReactionCountsResponseDto"}}}},"400":{"description":"Missing path parameter.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.delete`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or post not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Unreact. Idempotent: returns 200 with current `reaction_counts` even if there was nothing to remove.","tags":["agora-post-reactions"]}},"/v1/communities/{communityId}/hashtags/{tag}/posts":{"get":{"operationId":"HashtagFeedController_list","parameters":[{"name":"communityId","required":true,"in":"path","description":"Community UUID.","schema":{"type":"string"}},{"name":"tag","required":true,"in":"path","description":"Lowercase tag (without `#`).","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Page of posts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HashtagPostListResponseDto"}}}},"403":{"description":"PAK lacks `agora.list`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community or tag not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List posts using a hashtag. Same wire shape as the regular post list.","tags":["agora-hashtag-posts"]}},"/v1/communities/{communityId}/webhooks":{"post":{"operationId":"WebhookController_create","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookDto"}}}},"responses":{"201":{"description":"Webhook created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedWebhookResponseDto"}}}},"400":{"description":"Validation failure (bad url, empty event_types).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"PAK lacks `agora.create`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Community not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"Community already has an active webhook.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Create the community webhook. One active subscriber per community. Returns the signing secret ONCE — store it.","tags":["agora-webhooks"]},"get":{"operationId":"WebhookController_getActive","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}}],"responses":{"200":{"description":"Webhook or null.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookResponseDto"}}}},"404":{"description":"Community not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Read the community's active webhook (or null).","tags":["agora-webhooks"]}},"/v1/communities/{communityId}/webhooks/{webhookId}":{"get":{"operationId":"WebhookController_getById","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"webhookId","required":true,"in":"path","description":"Webhook UUID.","schema":{"type":"string"}}],"responses":{"200":{"description":"Webhook.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookResponseDto"}}}},"404":{"description":"Webhook not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Read a single webhook by id.","tags":["agora-webhooks"]},"patch":{"operationId":"WebhookController_update","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"webhookId","required":true,"in":"path","description":"Webhook UUID.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebhookDto"}}}},"responses":{"200":{"description":"Webhook updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookResponseDto"}}}},"400":{"description":"Validation failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Webhook not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Update webhook url + subscribed event types.","tags":["agora-webhooks"]},"delete":{"operationId":"WebhookController_delete","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"webhookId","required":true,"in":"path","description":"Webhook UUID.","schema":{"type":"string"}}],"responses":{"204":{"description":"Webhook deleted."},"404":{"description":"Webhook not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Delete the webhook.","tags":["agora-webhooks"]}},"/v1/communities/{communityId}/webhooks/{webhookId}/rotate-secret":{"post":{"operationId":"WebhookController_rotateSecret","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"webhookId","required":true,"in":"path","description":"Webhook UUID.","schema":{"type":"string"}}],"responses":{"200":{"description":"New secret.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedWebhookResponseDto"}}}},"404":{"description":"Webhook not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Rotate the signing secret. Also clears any auto-disable flag. Returns the new secret ONCE.","tags":["agora-webhooks"]}},"/v1/communities/{communityId}/webhooks/{webhookId}/deliveries":{"get":{"operationId":"WebhookController_deliveries","parameters":[{"name":"communityId","required":true,"in":"path","description":"Agora community UUID.","schema":{"example":"cccccccc-cccc-cccc-cccc-cccccccccccc","type":"string"}},{"name":"webhookId","required":true,"in":"path","description":"Webhook UUID.","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Page of deliveries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryListResponseDto"}}}},"404":{"description":"Webhook not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List delivery attempts (newest first).","tags":["agora-webhooks"]}}},"info":{"title":"Agora","description":"Social-as-a-Service backend. Generic actor / object / edge primitives. Two callers: PlatformUser admin (cookie/JWT) on /v1/workspaces/..., and customer-backend (PAK + X-Acting-As) on /v1/apps/:appSlug/...","version":"0.1.0","contact":{}},"tags":[],"servers":[],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"}},"schemas":{"CommunityResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"workspace_id":{"type":"string","description":"Owning workspace UUID.","example":"11111111-1111-1111-1111-111111111111"},"app_id":{"type":"string","description":"Heimdall app UUID this community wraps; `null` for standalone communities.","example":"33333333-3333-3333-3333-333333333333","nullable":true},"slug":{"type":"string","description":"URL-friendly community slug.","example":"my-product-community"},"display_name":{"type":"string","description":"Display name.","example":"My Product Community"},"description":{"type":"string","description":"Optional human-readable description.","example":null,"nullable":true},"settings":{"type":"object","description":"Per-community settings blob (ranking, max_comment_depth, auto_hide_flag_threshold, reaction_types).","example":{"max_comment_depth":3}},"status":{"type":"string","description":"Lifecycle status.","enum":["active","suspended","archived"],"example":"active"},"created_by":{"type":"string","description":"Account UUID of the creator.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"created_at":{"type":"string","description":"ISO timestamp the row was inserted.","example":"2026-05-01T12:00:00.000Z"},"updated_at":{"type":"string","description":"ISO timestamp of the last update.","example":"2026-05-01T12:00:00.000Z"}},"required":["id","workspace_id","app_id","slug","display_name","description","settings","status","created_by","created_at","updated_at"]},"ErrorResponseDto":{"type":"object","properties":{"statusCode":{"type":"number","description":"HTTP status code echoed in the body.","example":404},"message":{"type":"object","description":"Human-readable error message. May be a class-validator array on 400.","example":"Post not found"},"code":{"type":"string","description":"Stable error code for programmatic handling. Optional — newer routes set it where the message alone is ambiguous.","example":"NOT_FOUND"}},"required":["statusCode","message"]},"CreateCommunityDto":{"type":"object","properties":{"app_id":{"type":"string","description":"Optional Heimdall App UUID this community wraps. When supplied, the app must be owned by the calling workspace (cross-tenant gate). Omit to create a standalone community with no Heimdall integration — actor identity is then managed entirely by the customer.","example":"33333333-3333-3333-3333-333333333333"},"slug":{"type":"string","description":"URL-friendly community slug. Lowercase letters, digits, and hyphens (3-64 chars).","example":"my-product-community","minLength":3,"maxLength":64},"display_name":{"type":"string","description":"Display name shown in the console and on responses.","example":"My Product Community","minLength":1,"maxLength":120},"description":{"type":"string","description":"Optional human-readable description (up to 500 chars).","example":"Where our customers post stories and threads.","maxLength":500},"settings":{"type":"object","description":"Per-community settings blob (ranking, max_comment_depth, auto_hide_flag_threshold)."}},"required":["slug","display_name"]},"UpdateCommunityDto":{"type":"object","properties":{"display_name":{"type":"string","minLength":1,"maxLength":120},"description":{"type":"string","nullable":true,"maxLength":500},"settings":{"type":"object"},"status":{"type":"string","enum":["active","suspended","archived"]}}},"UpsertActorDto":{"type":"object","properties":{"external_id":{"type":"string","description":"Customer-side identifier (your user id). Idempotency key for upsert; unique per community.","example":"user_abc123","minLength":1,"maxLength":256},"display_name":{"type":"string","description":"Display name shown in feeds / mentions.","example":"Ada Lovelace","nullable":true,"maxLength":120},"avatar_url":{"type":"string","description":"Avatar URL.","example":"https://cdn.example.com/avatars/ada.png","nullable":true,"maxLength":2048},"metadata":{"type":"object","description":"Free-form metadata blob (e.g. plan tier, internal flags).","example":{"tier":"pro"}}},"required":["external_id"]},"ActorResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"external_id":{"type":"string","description":"Customer-supplied stable id.","example":"user-42"},"display_name":{"type":"string","description":"Display name.","example":"Alice","nullable":true},"avatar_url":{"type":"string","description":"Avatar URL.","nullable":true},"metadata":{"type":"object","description":"Free-form metadata."},"status":{"type":"string","enum":["active","suspended","deleted"]},"shadow_banned_at":{"type":"string","description":"ISO timestamp the shadow ban was applied, or null.","nullable":true},"shadow_banned_until":{"type":"string","description":"ISO timestamp the shadow ban auto-expires, or null for indefinite.","nullable":true},"shadow_banned_reason":{"type":"string","description":"Moderator note attached to the ban.","nullable":true},"created_at":{"type":"string","description":"ISO timestamp.","example":"2026-05-01T12:00:00.000Z"},"updated_at":{"type":"string","description":"ISO timestamp.","example":"2026-05-01T12:00:00.000Z"}},"required":["id","community_id","external_id","display_name","avatar_url","metadata","status","shadow_banned_at","shadow_banned_until","shadow_banned_reason","created_at","updated_at"]},"UpsertActorResponseDto":{"type":"object","properties":{"actor":{"$ref":"#/components/schemas/ActorResponseDto"},"created":{"type":"boolean","description":"`true` on first call (row was inserted), `false` on subsequent upserts that updated an existing row.","example":true}},"required":["actor","created"]},"PaginationResponseDto":{"type":"object","properties":{"next_cursor":{"type":"string","description":"Opaque base64url cursor for the next page; `null` when no further pages.","example":"eyJjcmVhdGVkX2F0IjoiMjAyNi0wNS0wMVQxMjowMDowMC4wMDBaIiwiaWQiOiJiYmJiIn0","nullable":true},"has_more":{"type":"boolean","description":"`true` when at least one more page exists. Mirrors `next_cursor !== null`; published as a separate field so SDKs can drive a `while` loop without parsing the cursor.","example":true}},"required":["next_cursor","has_more"]},"ActorListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ActorResponseDto"}},"pagination":{"$ref":"#/components/schemas/PaginationResponseDto"}},"required":["data","pagination"]},"UpdateActorDto":{"type":"object","properties":{"display_name":{"type":"string","nullable":true,"maxLength":120},"avatar_url":{"type":"string","nullable":true,"maxLength":2048},"metadata":{"type":"object"},"status":{"type":"string","enum":["active","suspended","deleted"]}}},"CreatePostDto":{"type":"object","properties":{"actor_id":{"type":"string","description":"Agora actor UUID who authors the post (returned from POST /actors).","example":"aaaaaaaa-0000-0000-0000-000000000001"},"kind":{"type":"string","description":"Post kind. Free-form short label; common values: text, link, image, story.","example":"text","maxLength":64},"title":{"type":"string","description":"Optional title (1-240 chars).","example":"Shipping Phase 8","nullable":true,"maxLength":240},"body":{"type":"string","description":"Body markdown / plain text (up to 50000 chars).","example":"Stories are live!","nullable":true,"maxLength":50000},"url":{"type":"string","description":"Optional URL (for link / image kinds).","example":"https://cdn.example.com/img.png","nullable":true,"maxLength":2048},"attributes":{"type":"object","description":"Free-form attributes blob. Polls live here at `attributes.poll`.","example":{"media_url":"https://cdn.example.com/stories/1.jpg"}},"visibility":{"type":"string","description":"Visibility scope. Defaults to `public`.","enum":["public","followers","close_friends","private"],"example":"public"},"expires_at":{"type":"string","description":"ISO-8601 expiry timestamp. Used by stories (24h disappearing).","example":"2026-05-02T12:00:00Z","nullable":true},"status":{"type":"string","description":"Initial status. Defaults to `published`. Pass `draft` to save unpublished; pass `scheduled` with a future `scheduled_for` to defer publication. Scheduled posts are invisible to everyone except the author until the per-minute scheduler promotes them.","enum":["published","draft","scheduled"]},"scheduled_for":{"type":"string","description":"Required when status='scheduled'. ISO timestamp at least 30 seconds in the future. The PostSchedulerCron promotes scheduled posts to published once now() >= scheduled_for.","nullable":true},"source_post_id":{"type":"string","description":"For `kind='quote'` ONLY: the source post being quoted. Reposts use the dedicated POST /posts/:postId/repost route instead. Validation: when `kind='quote'`, both `source_post_id` and a non-empty `body` are required.","example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}},"required":["actor_id"]},"PostResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Post UUID.","example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"},"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"actor_id":{"type":"string","description":"Author actor UUID.","example":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"},"kind":{"type":"string","description":"Free-form post kind. Common: text, link, image, story.","example":"text"},"title":{"type":"string","description":"Optional title (1-240 chars).","example":"Shipping Phase 8","nullable":true},"body":{"type":"string","description":"Body markdown / plain text (up to 50000 chars).","example":"Stories are live!","nullable":true},"url":{"type":"string","description":"Optional URL.","example":"https://cdn.example.com/img.png","nullable":true},"attributes":{"type":"object","description":"Free-form attributes blob (polls live at `attributes.poll`).","example":{}},"visibility":{"type":"string","description":"Visibility scope.","enum":["public","followers","close_friends","private"],"example":"public"},"status":{"type":"string","description":"Lifecycle status. `removed` rows are hidden from public reads.","enum":["published","hidden","removed"],"example":"published"},"reaction_counts":{"type":"object","description":"Map of reaction type to count, denormalised on the post row for cheap feed reads.","example":{"like":12,"fire":3}},"comment_count":{"type":"number","description":"Comment count, denormalised on the post row.","example":4},"source_post_id":{"type":"string","description":"For kind='repost' / 'quote': the source post UUID.","example":null,"nullable":true},"repost_count":{"type":"number","description":"Denormalised count of currently-published reposts.","example":0},"quote_count":{"type":"number","description":"Denormalised count of currently-published quote posts.","example":0},"view_count":{"type":"number","description":"Denormalised impression count, bumped via POST /posts/views.","example":0},"edited_at":{"type":"string","description":"ISO timestamp of the most recent edit (body/title/attributes change). Null when never edited.","example":null,"nullable":true},"edit_count":{"type":"number","description":"Number of edits applied; equals the count of post_revision rows.","example":0},"expires_at":{"type":"string","description":"ISO timestamp; `null` = no expiry. Stories typically set this 24h ahead.","example":null,"nullable":true},"pinned":{"type":"boolean","description":"When `true`, surfaces in the author's highlights endpoint regardless of expiry.","example":false},"created_at":{"type":"string","description":"ISO timestamp the row was inserted.","example":"2026-05-01T12:00:00.000Z"},"updated_at":{"type":"string","description":"ISO timestamp of the last update.","example":"2026-05-01T12:00:00.000Z"}},"required":["id","community_id","actor_id","kind","title","body","url","attributes","visibility","status","reaction_counts","comment_count","source_post_id","repost_count","quote_count","view_count","edited_at","edit_count","expires_at","pinned","created_at","updated_at"]},"PostListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PostResponseDto"}},"pagination":{"$ref":"#/components/schemas/PaginationResponseDto"}},"required":["data","pagination"]},"UpdatePostDto":{"type":"object","properties":{"title":{"type":"string","description":"Updated title.","nullable":true,"maxLength":240},"body":{"type":"string","description":"Updated body.","nullable":true,"maxLength":50000},"url":{"type":"string","description":"Updated URL.","nullable":true,"maxLength":2048},"attributes":{"type":"object","description":"Replaces the attributes blob entirely."},"visibility":{"type":"string","enum":["public","followers","close_friends","private"]},"status":{"type":"string","description":"Direct status transition. Pass `published` to promote a `draft` or `scheduled`. Prefer the moderation lane for `hidden`/`removed`. Transitions to/from `draft`/`scheduled` other than to `published` are 422.","enum":["published","hidden","removed","draft","scheduled"]},"expires_at":{"type":"string","description":"ISO-8601 expiry timestamp; pass null to clear.","nullable":true},"scheduled_for":{"type":"string","description":"ISO-8601 publish-at timestamp. Updating only valid while the post is `status=\"scheduled\"`. Pass null to clear (typically only the scheduler does this on promotion).","nullable":true},"pinned":{"type":"boolean","description":"Toggle pinned status (used by highlights / stories)."}}},"PostRevisionResponseDto":{"type":"object","properties":{"post_id":{"type":"string","description":"Post UUID this revision belongs to.","example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"},"revision_n":{"type":"number","description":"Revision sequence number (1 = first edit).","example":1},"body":{"type":"string","description":"Body text BEFORE this edit landed.","example":"shipping today.","nullable":true},"title":{"type":"string","description":"Title BEFORE this edit landed.","example":"Phase 8","nullable":true},"attributes":{"type":"object","description":"Attributes BEFORE this edit landed.","example":{}},"edited_at":{"type":"string","description":"ISO timestamp the edit landed.","example":"2026-05-01T12:15:00.000Z"}},"required":["post_id","revision_n","body","title","attributes","edited_at"]},"PostRevisionListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PostRevisionResponseDto"}},"pagination":{"$ref":"#/components/schemas/PaginationResponseDto"}},"required":["data","pagination"]},"RecordViewsDto":{"type":"object","properties":{"post_ids":{"description":"UUIDs of posts that became visible to the viewer in this batch. Capped at 200 to keep the payload sensible — the server will silently skip ids that aren't in the community or have been removed.","example":["bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb","cccccccc-cccc-cccc-cccc-cccccccccccc"],"type":"array","items":{"type":"string"}}},"required":["post_ids"]},"DraftPostResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Draft post UUID.","example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"},"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"actor_id":{"type":"string","description":"Author actor UUID.","example":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"},"kind":{"type":"string","description":"Free-form post kind.","example":"text"},"title":{"type":"string","description":"Optional title.","example":null,"nullable":true},"body":{"type":"string","description":"Body draft text.","example":"WIP — half a thought…","nullable":true},"url":{"type":"string","description":"Optional URL.","example":null,"nullable":true},"attributes":{"type":"object","description":"Free-form attributes blob.","example":{}},"visibility":{"type":"string","description":"Visibility scope chosen at draft time. Applies once promoted to published.","enum":["public","followers","close_friends","private"],"example":"public"},"status":{"type":"string","description":"Always 'draft' for this endpoint.","enum":["draft"],"example":"draft"},"reaction_counts":{"type":"object","description":"Empty for drafts (engagement begins at publish).","example":{}},"comment_count":{"type":"number","description":"Always 0 for drafts.","example":0},"expires_at":{"type":"string","description":"Optional expires_at carried into the published post.","example":null,"nullable":true},"pinned":{"type":"boolean","description":"Pinned flag carried into the published post.","example":false},"created_at":{"type":"string","description":"ISO timestamp the draft was created.","example":"2026-05-01T12:00:00.000Z"},"updated_at":{"type":"string","description":"ISO timestamp of the last update.","example":"2026-05-01T12:00:00.000Z"}},"required":["id","community_id","actor_id","kind","title","body","url","attributes","visibility","status","reaction_counts","comment_count","expires_at","pinned","created_at","updated_at"]},"DraftListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DraftPostResponseDto"}},"pagination":{"$ref":"#/components/schemas/PaginationResponseDto"}},"required":["data","pagination"]},"CreateRepostDto":{"type":"object","properties":{"actor_id":{"type":"string","description":"Reposting actor UUID — the customer-backend supplies the end-user identity here.","example":"aaaaaaaa-0000-0000-0000-000000000001"}},"required":["actor_id"]},"RepostPostResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Post UUID.","example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"},"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"actor_id":{"type":"string","description":"Author actor UUID.","example":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"},"kind":{"type":"string","description":"Post kind. 'repost' for repost rows.","example":"repost"},"title":{"type":"string","description":"Optional title.","example":null,"nullable":true},"body":{"type":"string","description":"Body. Always null for reposts.","example":null,"nullable":true},"url":{"type":"string","description":"Optional URL.","example":null,"nullable":true},"attributes":{"type":"object","description":"Free-form attributes blob.","example":{}},"visibility":{"type":"string","description":"Visibility scope (inherited from the source).","enum":["public","followers","close_friends","private"],"example":"public"},"status":{"type":"string","description":"Lifecycle status.","enum":["published","hidden","removed","draft"],"example":"published"},"reaction_counts":{"type":"object","description":"Live denormalised reaction counts.","example":{}},"comment_count":{"type":"number","description":"Live denormalised comment count.","example":0},"source_post_id":{"type":"string","description":"For reposts: source post UUID.","example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb","nullable":true},"repost_count":{"type":"number","description":"Reposts of this post (always 0 for the repost row itself).","example":0},"expires_at":{"type":"string","description":"ISO timestamp; null = no expiry.","example":null,"nullable":true},"pinned":{"type":"boolean","description":"Pinned for highlights.","example":false},"created_at":{"type":"string","description":"ISO timestamp.","example":"2026-05-01T12:00:00.000Z"},"updated_at":{"type":"string","description":"ISO timestamp.","example":"2026-05-01T12:00:00.000Z"}},"required":["id","community_id","actor_id","kind","title","body","url","attributes","visibility","status","reaction_counts","comment_count","source_post_id","repost_count","expires_at","pinned","created_at","updated_at"]},"CreateRepostResponseDto":{"type":"object","properties":{"repost":{"description":"The newly-created (or existing) repost post row.","allOf":[{"$ref":"#/components/schemas/RepostPostResponseDto"}]},"source":{"description":"The source post, with refreshed `repost_count`.","allOf":[{"$ref":"#/components/schemas/RepostPostResponseDto"}]},"created":{"type":"boolean","description":"`true` on first call; `false` on idempotent retry of an existing repost.","example":true}},"required":["repost","source","created"]},"RepostListResponseDto":{"type":"object","properties":{"data":{"description":"Page of repost post rows, each carrying the reposting actor_id.","type":"array","items":{"$ref":"#/components/schemas/RepostPostResponseDto"}},"pagination":{"$ref":"#/components/schemas/PaginationResponseDto"}},"required":["data","pagination"]},"ActorCountersResponseDto":{"type":"object","properties":{"actor_id":{"type":"string","description":"Actor UUID.","example":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"},"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"follower_count":{"type":"number","description":"How many actors follow this actor.","example":42},"following_count":{"type":"number","description":"How many actors this actor follows.","example":17},"post_count":{"type":"number","description":"Published posts authored by this actor.","example":5},"comment_count":{"type":"number","description":"Published comments authored by this actor.","example":12},"blocks_count":{"type":"number","description":"Actors this actor has blocked.","example":0},"updated_at":{"type":"string","description":"ISO timestamp of the last counter update.","example":"2026-05-01T12:00:00.000Z"}},"required":["actor_id","community_id","follower_count","following_count","post_count","comment_count","blocks_count","updated_at"]},"CommunityAnalyticsResponseDto":{"type":"object","properties":{"community_id":{"type":"string","description":"Community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"total_actors":{"type":"number","description":"Active actor rows count.","example":1234},"total_posts":{"type":"number","description":"Posts where status='published'.","example":5678},"total_comments":{"type":"number","description":"Comments where status='published'.","example":9876},"total_open_flags":{"type":"number","description":"Flags where status='open'.","example":3},"posts_last_7d":{"type":"number","description":"Posts created in the last 7 days (any status).","example":42},"comments_last_7d":{"type":"number","description":"Comments created in the last 7 days (any status).","example":88},"actors_last_7d":{"type":"number","description":"Actors created in the last 7 days.","example":12},"computed_at":{"type":"string","description":"ISO timestamp this rollup was computed at.","example":"2026-05-01T12:00:00.000Z"}},"required":["community_id","total_actors","total_posts","total_comments","total_open_flags","posts_last_7d","comments_last_7d","actors_last_7d","computed_at"]},"ActorEdgeDto":{"type":"object","properties":{"src_actor_id":{"type":"string","description":"Source agora actor UUID (the one performing the follow / mute / block / restrict).","example":"aaaaaaaa-0000-0000-0000-000000000001"},"dst_actor_id":{"type":"string","description":"Destination agora actor UUID (the one being followed / muted / blocked / restricted).","example":"aaaaaaaa-0000-0000-0000-000000000002"},"payload":{"type":"object","description":"Optional free-form payload stored on the edge (e.g. mute reason)."}},"required":["src_actor_id","dst_actor_id"]},"EdgeResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Edge UUID.","example":"eeeeeeee-0000-0000-0000-000000000001"},"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"kind":{"type":"string","description":"Edge kind. `follow`, `mute`, `block`, `save`, or `reaction:<type>` for post reactions.","example":"follow"},"src_actor_id":{"type":"string","description":"Source actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"dst_actor_id":{"type":"string","description":"Destination actor UUID (for actor edges).","example":"aaaaaaaa-0000-0000-0000-000000000002","nullable":true},"dst_post_id":{"type":"string","description":"Destination post UUID (for reaction / save edges).","example":null,"nullable":true},"payload":{"type":"object","description":"Free-form payload stored on the edge (e.g. mute reason).","example":{}},"created_at":{"type":"string","description":"ISO timestamp the edge was created.","example":"2026-05-01T12:00:00.000Z"}},"required":["id","community_id","kind","src_actor_id","dst_actor_id","dst_post_id","payload","created_at"]},"CreateEdgeResponseDto":{"type":"object","properties":{"edge":{"$ref":"#/components/schemas/EdgeResponseDto"},"created":{"type":"boolean","description":"`true` if the edge was created on this call; `false` on idempotent retry of an existing edge.","example":true}},"required":["edge","created"]},"EdgeListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EdgeResponseDto"}},"pagination":{"$ref":"#/components/schemas/PaginationResponseDto"}},"required":["data","pagination"]},"HashtagResponseDto":{"type":"object","properties":{"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"tag":{"type":"string","description":"Lowercase tag (1..32 chars from [a-z0-9_]).","example":"storymode"},"first_used_at":{"type":"string","description":"ISO timestamp the tag first appeared in this community.","example":"2026-05-01T12:00:00.000Z"}},"required":["community_id","tag","first_used_at"]},"HashtagListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/HashtagResponseDto"}},"pagination":{"$ref":"#/components/schemas/PaginationResponseDto"}},"required":["data","pagination"]},"HashtagDetailResponseDto":{"type":"object","properties":{"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"tag":{"type":"string","description":"Lowercase tag (1..32 chars from [a-z0-9_]).","example":"storymode"},"first_used_at":{"type":"string","description":"ISO timestamp the tag first appeared in this community.","example":"2026-05-01T12:00:00.000Z"},"post_count":{"type":"number","description":"Number of currently-published posts using the tag.","example":17}},"required":["community_id","tag","first_used_at","post_count"]},"HashtagAutocompleteResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/HashtagResponseDto"}}},"required":["data"]},"CreateHashtagFollowDto":{"type":"object","properties":{"tag":{"type":"string","description":"Lowercase tag (without `#`). Must already exist in the community directory.","example":"storymode","minLength":1,"maxLength":32}},"required":["tag"]},"HashtagFollowResponseDto":{"type":"object","properties":{"actor_id":{"type":"string","description":"Agora actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"tag":{"type":"string","description":"Lowercase tag (without `#`).","example":"storymode"},"followed_at":{"type":"string","description":"ISO timestamp the follow was created.","example":"2026-05-01T12:00:00.000Z"}},"required":["actor_id","community_id","tag","followed_at"]},"HashtagFollowListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/HashtagFollowResponseDto"}},"pagination":{"$ref":"#/components/schemas/PaginationResponseDto"}},"required":["data","pagination"]},"CreateCommentDto":{"type":"object","properties":{"actor_id":{"type":"string","description":"Agora actor UUID who authors the comment.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"body":{"type":"string","description":"Comment body (1-10000 chars after trim).","example":"Looks great, shipping today.","minLength":1,"maxLength":10000},"parent_id":{"type":"string","description":"Parent comment UUID for replies. Omit for a top-level comment.","example":"bbbbbbbb-0000-0000-0000-000000000001","nullable":true},"visibility":{"type":"string","description":"Visibility scope. `author_only` = DM-style (post author + comment author only).","enum":["public","author_only"],"example":"public"}},"required":["actor_id","body"]},"CommentResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Comment UUID.","example":"bbbbbbbb-0000-0000-0000-000000000001"},"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"post_id":{"type":"string","description":"Post the comment threads under.","example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"},"parent_id":{"type":"string","description":"Parent comment UUID for replies; `null` for top-level.","example":null,"nullable":true},"actor_id":{"type":"string","description":"Comment author actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"body":{"type":"string","description":"Comment body.","example":"Looks great, shipping today."},"depth":{"type":"number","description":"Thread depth — 0 for top-level, 1 for first reply, etc. Capped per community.","example":0},"status":{"type":"string","description":"Lifecycle status. `removed` rows are hidden from public reads.","enum":["published","hidden","removed"],"example":"published"},"visibility":{"type":"string","description":"`public` (default) is visible to anyone who can read the post; `author_only` is DM-style (post-author + comment-author only).","enum":["public","author_only"],"example":"public"},"created_at":{"type":"string","description":"ISO timestamp the row was inserted.","example":"2026-05-01T12:00:00.000Z"},"updated_at":{"type":"string","description":"ISO timestamp of the last update.","example":"2026-05-01T12:00:00.000Z"}},"required":["id","community_id","post_id","parent_id","actor_id","body","depth","status","visibility","created_at","updated_at"]},"CommentListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CommentResponseDto"}},"pagination":{"$ref":"#/components/schemas/PaginationResponseDto"}},"required":["data","pagination"]},"UpdateCommentDto":{"type":"object","properties":{"body":{"type":"string","description":"Updated body.","minLength":1,"maxLength":10000},"status":{"type":"string","description":"Direct status transition. Prefer the moderation lane for `hidden`/`removed`.","enum":["published","hidden","removed"]},"visibility":{"type":"string","enum":["public","author_only"]}}},"ApproveCommentDto":{"type":"object","properties":{"approver_actor_id":{"type":"string","description":"The post-author — must equal the post.actor_id.","example":"aaaaaaaa-0000-0000-0000-000000000001"}},"required":["approver_actor_id"]},"CreateCommentReactionDto":{"type":"object","properties":{"src_actor_id":{"type":"string","description":"Reactor actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"type":{"type":"string","description":"Reaction type (free-form short label, e.g. `like`, `fire`, `heart`).","example":"like","minLength":1,"maxLength":64}},"required":["src_actor_id","type"]},"CommentReactionEntryDto":{"type":"object","properties":{"comment_id":{"type":"string","description":"Comment UUID.","example":"bbbbbbbb-0000-0000-0000-000000000001"},"actor_id":{"type":"string","description":"Reactor actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"type":{"type":"string","description":"Reaction type label.","example":"like"},"created_at":{"type":"string","description":"ISO timestamp.","example":"2026-05-01T12:00:00.000Z"}},"required":["comment_id","actor_id","community_id","type","created_at"]},"AddCommentReactionResponseDto":{"type":"object","properties":{"reaction":{"$ref":"#/components/schemas/CommentReactionEntryDto"},"created":{"type":"boolean","description":"`true` on first call; `false` on idempotent retry.","example":true},"reaction_counts":{"type":"object","description":"Live denormalised counts on the comment after this write.","example":{"like":4}}},"required":["reaction","created","reaction_counts"]},"CommentReactionCountsResponseDto":{"type":"object","properties":{"reaction_counts":{"type":"object","description":"Live denormalised counts after the delete (or current counts if the reaction was already absent).","example":{"like":3}}},"required":["reaction_counts"]},"CommentReactionListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CommentReactionEntryDto"}},"pagination":{"$ref":"#/components/schemas/PaginationResponseDto"}},"required":["data","pagination"]},"FeedPostResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Post UUID.","example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"},"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"actor_id":{"type":"string","description":"Author actor UUID.","example":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"},"kind":{"type":"string","description":"Free-form post kind.","example":"text"},"title":{"type":"string","description":"Optional title.","example":null,"nullable":true},"body":{"type":"string","description":"Body text/markdown.","example":"Stories are live!","nullable":true},"url":{"type":"string","description":"Optional URL.","example":null,"nullable":true},"attributes":{"type":"object","description":"Free-form attributes blob.","example":{}},"visibility":{"type":"string","description":"Visibility scope; the feed is already filtered to what the requester can see.","enum":["public","followers","close_friends","private"],"example":"public"},"status":{"type":"string","description":"Lifecycle status. Feed only returns `published`.","enum":["published","hidden","removed"],"example":"published"},"reaction_counts":{"type":"object","description":"Live denormalised reaction counts.","example":{"like":12}},"comment_count":{"type":"number","description":"Live denormalised comment count.","example":4},"expires_at":{"type":"string","description":"ISO timestamp; null = no expiry.","example":null,"nullable":true},"pinned":{"type":"boolean","description":"Pinned for highlights.","example":false},"created_at":{"type":"string","description":"ISO timestamp the post was created.","example":"2026-05-01T12:00:00.000Z"},"updated_at":{"type":"string","description":"ISO timestamp of the last update.","example":"2026-05-01T12:00:00.000Z"}},"required":["id","community_id","actor_id","kind","title","body","url","attributes","visibility","status","reaction_counts","comment_count","expires_at","pinned","created_at","updated_at"]},"FeedResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/FeedPostResponseDto"}},"pagination":{"description":"Cursor encodes `(score, id)` for `ranked` feeds and `(created_at, id)` for `chronological` feeds. Cursors are NOT interchangeable between modes.","allOf":[{"$ref":"#/components/schemas/PaginationResponseDto"}]}},"required":["data","pagination"]},"CreateActorListDto":{"type":"object","properties":{"owner_id":{"type":"string","description":"List owner — agora actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"name":{"type":"string","description":"Display name (1..200).","minLength":1,"maxLength":200,"example":"Tech news"},"description":{"type":"string","description":"Optional description (0..2000).","maxLength":2000,"nullable":true},"visibility":{"type":"string","description":"Default `private`.","enum":["public","private"],"example":"public"}},"required":["owner_id","name"]},"ActorListListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ActorListResponseDto"}},"pagination":{"$ref":"#/components/schemas/PaginationResponseDto"}},"required":["data","pagination"]},"UpdateActorListDto":{"type":"object","properties":{"caller_actor_id":{"type":"string","description":"Caller — agora actor UUID. Must equal the list owner.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"name":{"type":"string","description":"New name.","minLength":1,"maxLength":200},"description":{"type":"string","description":"New description.","maxLength":2000,"nullable":true},"visibility":{"type":"string","description":"New visibility.","enum":["public","private"]}},"required":["caller_actor_id"]},"DeleteActorListDto":{"type":"object","properties":{"caller_actor_id":{"type":"string","description":"Caller — agora actor UUID. Must equal the list owner.","example":"aaaaaaaa-0000-0000-0000-000000000001"}},"required":["caller_actor_id"]},"AddMemberDto":{"type":"object","properties":{"caller_actor_id":{"type":"string","description":"Group admin performing the add — agora actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"actor_id":{"type":"string","description":"The new member — agora actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000004"},"role":{"type":"string","description":"Default `member`.","enum":["member","admin"],"example":"member"}},"required":["caller_actor_id","actor_id"]},"ActorListMemberResponseDto":{"type":"object","properties":{"list_id":{"type":"string","description":"Owning list UUID.","example":"llllllll-0000-0000-0000-000000000001"},"actor_id":{"type":"string","description":"Member actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000002"},"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"added_at":{"type":"string","description":"ISO timestamp.","example":"2026-05-01T12:00:00.000Z"}},"required":["list_id","actor_id","community_id","added_at"]},"RemoveMemberDto":{"type":"object","properties":{"caller_actor_id":{"type":"string","description":"Agora actor UUID of the acting party. Equal to the target for self-leave.","example":"aaaaaaaa-0000-0000-0000-000000000001"}},"required":["caller_actor_id"]},"ActorListMemberListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ActorListMemberResponseDto"}},"pagination":{"$ref":"#/components/schemas/PaginationResponseDto"}},"required":["data","pagination"]},"CreateMutedTermDto":{"type":"object","properties":{"term":{"type":"string","description":"Term to mute. 1..200 chars after trim.","minLength":1,"maxLength":200,"example":"spoiler"},"scope":{"type":"string","description":"Default `all`.","enum":["feed","dm","mention","all"]},"expires_at":{"type":"string","description":"Optional expiry (ISO timestamp in the future).","nullable":true}},"required":["term"]},"MutedTermResponseDto":{"type":"object","properties":{"actor_id":{"type":"string","description":"Owning actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"term":{"type":"string","description":"Case-insensitive substring.","example":"spoiler"},"scope":{"type":"string","description":"Where the mute applies.","enum":["feed","dm","mention","all"],"example":"all"},"expires_at":{"type":"string","description":"ISO timestamp; null = never expires.","example":null,"nullable":true},"created_at":{"type":"string","description":"ISO timestamp.","example":"2026-05-01T12:00:00.000Z"}},"required":["actor_id","community_id","term","scope","expires_at","created_at"]},"CreateFlagDto":{"type":"object","properties":{"reporter_actor_id":{"type":"string","description":"Agora actor UUID who is reporting the content.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"target_kind":{"type":"string","description":"What kind of object is being flagged.","enum":["post","comment"],"example":"post"},"target_id":{"type":"string","description":"UUID of the post or comment being flagged.","example":"pppppppp-pppp-pppp-pppp-pppppppppppp"},"reason":{"type":"string","description":"Optional free-form reason (up to 1000 chars).","example":"Spam — same link posted 6 times in 5 minutes.","maxLength":1000}},"required":["reporter_actor_id","target_kind","target_id"]},"FlagResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Flag UUID.","example":"ffffffff-0000-0000-0000-000000000001"},"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"target_kind":{"type":"string","description":"What kind of object is being flagged.","enum":["post","comment"],"example":"post"},"target_id":{"type":"string","description":"UUID of the post or comment being flagged.","example":"pppppppp-pppp-pppp-pppp-pppppppppppp"},"reporter_actor_id":{"type":"string","description":"Agora actor UUID who reported.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"reason":{"type":"string","description":"Free-text reason; empty string when none provided.","example":"Spam — same link posted 6 times in 5 minutes."},"status":{"type":"string","description":"Lifecycle. `open` is in the queue; admins close to `dismissed` or `actioned`.","enum":["open","dismissed","actioned"],"example":"open"},"created_at":{"type":"string","description":"ISO timestamp the row was inserted.","example":"2026-05-01T12:00:00.000Z"},"updated_at":{"type":"string","description":"ISO timestamp of the last update.","example":"2026-05-01T12:00:00.000Z"}},"required":["id","community_id","target_kind","target_id","reporter_actor_id","reason","status","created_at","updated_at"]},"FlagListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/FlagResponseDto"}},"pagination":{"$ref":"#/components/schemas/PaginationResponseDto"}},"required":["data","pagination"]},"ModerationActionDto":{"type":"object","properties":{"action":{"type":"string","description":"Action to apply. `hide`/`remove` transition target status; `unhide`/`restore` reverse them. `warn`/`ban` are audit-only. `dismiss` closes the flag with no target change.","enum":["hide","remove","warn","ban","dismiss","unhide","restore"],"example":"hide"},"notes":{"type":"string","description":"Optional moderator notes attached to the audit row (up to 2000 chars).","example":"Spam confirmed by manual review.","maxLength":2000}},"required":["action"]},"ModerationActionResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Action UUID.","example":"mmmmmmmm-0000-0000-0000-000000000001"},"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"flag_id":{"type":"string","description":"Flag UUID this action closed; `null` for unsolicited mod actions.","example":"ffffffff-0000-0000-0000-000000000001","nullable":true},"target_kind":{"type":"string","enum":["post","comment"],"example":"post"},"target_id":{"type":"string","description":"UUID of the targeted post / comment.","example":"pppppppp-pppp-pppp-pppp-pppppppppppp"},"moderator_account_id":{"type":"string","description":"PlatformUser account UUID of the moderator.","example":"99999999-0000-0000-0000-000000000001"},"action":{"type":"string","description":"Action applied.","enum":["hide","remove","warn","ban","dismiss","unhide","restore"],"example":"hide"},"notes":{"type":"string","description":"Optional moderator notes.","example":"Spam confirmed.","nullable":true},"created_at":{"type":"string","description":"ISO timestamp the action was recorded.","example":"2026-05-01T12:00:00.000Z"}},"required":["id","community_id","flag_id","target_kind","target_id","moderator_account_id","action","notes","created_at"]},"AuditListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ModerationActionResponseDto"}},"pagination":{"$ref":"#/components/schemas/PaginationResponseDto"}},"required":["data","pagination"]},"ApplyShadowBanDto":{"type":"object","properties":{"duration_hours":{"type":"number","description":"Hours until auto-restore. Omit for indefinite.","nullable":true,"example":168},"reason":{"type":"string","description":"Moderator note (1..1000 chars).","nullable":true,"maxLength":1000}}},"NotificationResponse":{"type":"object","properties":{"id":{"type":"string","description":"Notification UUID.","example":"11111111-1111-1111-1111-111111111111"},"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"recipient_actor_id":{"type":"string","description":"Actor whose inbox this row lives in.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"kind":{"type":"string","description":"What happened. `follow` / `reaction` / `comment_on_post` / `reply_to_comment`. `mention` is reserved.","enum":["follow","reaction","comment_on_post","reply_to_comment","mention"],"example":"reaction"},"actor_id":{"type":"string","description":"Actor who triggered the event. Never equals `recipient_actor_id`.","example":"aaaaaaaa-0000-0000-0000-000000000002"},"target_kind":{"type":"string","description":"Switches what `target_id` references. `actor` for follow; `post` for reaction / comment_on_post; `comment` for reply_to_comment.","enum":["actor","post","comment"],"example":"post"},"target_id":{"type":"string","description":"id of the actor / post / comment the event happened on.","example":"bbbbbbbb-0000-0000-0000-000000000001"},"payload":{"type":"object","description":"Kind-specific extras. `reaction`: `{type}`. `comment_on_post` / `reply_to_comment`: `{comment_id}`.","example":{"type":"like"}},"read_at":{"type":"string","description":"ISO timestamp; null while the row is unread.","example":null,"nullable":true},"created_at":{"type":"string","description":"ISO timestamp the row was inserted.","example":"2026-05-01T12:00:00.000Z"}},"required":["id","community_id","recipient_actor_id","kind","actor_id","target_kind","target_id","payload","read_at","created_at"]},"NotificationListPagination":{"type":"object","properties":{"next_cursor":{"type":"string","description":"Opaque base64url cursor for the next page; `null` when no further pages.","example":"eyJjcmVhdGVkX2F0IjoiMjAyNi0wNS0wMVQxMjowMDowMC4wMDBaIiwiaWQiOiIxMTExMSJ9","nullable":true},"has_more":{"type":"boolean","description":"Whether more pages exist after this one.","example":false}},"required":["next_cursor","has_more"]},"NotificationListResponse":{"type":"object","properties":{"data":{"description":"Page of notifications, newest first.","type":"array","items":{"$ref":"#/components/schemas/NotificationResponse"}},"pagination":{"$ref":"#/components/schemas/NotificationListPagination"}},"required":["data","pagination"]},"ErrorResponse":{"type":"object","properties":{"statusCode":{"type":"number","description":"HTTP status code echoed in the body.","example":404},"message":{"type":"object","description":"Human-readable error message.","example":"Post not found"},"code":{"type":"string","description":"Stable error code.","example":"NOT_FOUND"}},"required":["statusCode","message"]},"UnreadCountResponse":{"type":"object","properties":{"count":{"type":"number","description":"Number of unread notifications for the actor.","example":7}},"required":["count"]},"UpdateNotificationDto":{"type":"object","properties":{"read":{"type":"boolean","description":"`true` to mark read, `false` to mark unread.","example":true}},"required":["read"]},"MarkAllReadDto":{"type":"object","properties":{"before":{"type":"string","description":"Optional ISO timestamp; only flips rows older than this. Omit to flip all unread.","example":"2026-05-01T00:00:00.000Z"}}},"MarkAllReadResponse":{"type":"object","properties":{"updated":{"type":"number","description":"Number of rows whose `read_at` flipped from null to now().","example":12}},"required":["updated"]},"NotificationPrefResponseDto":{"type":"object","properties":{"actor_id":{"type":"string","description":"Agora actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"kind":{"type":"string","description":"NotificationKind this row applies to.","enum":["follow","reaction","comment_on_post","reply_to_comment","mention","vote","repost","quote_post"],"example":"follow"},"enabled":{"type":"boolean","description":"When `false`, the producer silently skips notifications of this kind for this actor. Default `true`.","example":true},"updated_at":{"type":"string","description":"ISO timestamp of the last update. Synthesised entries (no explicit row) report epoch.","example":"2026-05-01T12:00:00.000Z"}},"required":["actor_id","kind","enabled","updated_at"]},"NotificationPrefListResponseDto":{"type":"object","properties":{"data":{"description":"One row per known NotificationKind. Kinds the actor has not explicitly toggled report `enabled=true` and an epoch `updated_at`, so the client always sees the full catalogue with effective state.","type":"array","items":{"$ref":"#/components/schemas/NotificationPrefResponseDto"}}},"required":["data"]},"UpdateNotificationPrefDto":{"type":"object","properties":{"enabled":{"type":"boolean","description":"`true` to receive notifications of this kind, `false` to mute. Default behaviour (no row) is `true`.","example":false}},"required":["enabled"]},"SuggestedActorResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Agora actor UUID.","example":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"},"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"external_id":{"type":"string","description":"Customer-supplied stable identifier.","example":"user_abc123"},"display_name":{"type":"string","description":"Display name.","example":"Ada Lovelace","nullable":true},"avatar_url":{"type":"string","description":"Avatar URL.","example":"https://cdn.example.com/avatars/ada.png","nullable":true},"metadata":{"type":"object","description":"Free-form metadata blob.","example":{"tier":"pro"}},"status":{"type":"string","description":"Lifecycle status. Suggestions only surface `active` actors.","enum":["active","suspended","deleted"],"example":"active"},"created_at":{"type":"string","description":"ISO timestamp.","example":"2026-05-01T12:00:00.000Z"},"updated_at":{"type":"string","description":"ISO timestamp.","example":"2026-05-01T12:00:00.000Z"}},"required":["id","community_id","external_id","display_name","avatar_url","metadata","status","created_at","updated_at"]},"SuggestedFollowResponseDto":{"type":"object","properties":{"actor":{"$ref":"#/components/schemas/SuggestedActorResponseDto"},"score":{"type":"number","description":"For `followed_by_friends`: number of the requester's followees who follow this candidate. For `top_in_community`: the candidate's `follower_count`.","example":4},"reason":{"type":"string","description":"Why this candidate surfaced. `followed_by_friends` is the friend-of-a-friend signal; `top_in_community` is the cold-start fallback used when FoF doesn't fill the requested limit.","enum":["followed_by_friends","top_in_community"],"example":"followed_by_friends"}},"required":["actor","score","reason"]},"SuggestedFollowsListResponseDto":{"type":"object","properties":{"data":{"description":"Ordered candidates. FoF candidates are returned first (highest count first); cold-start fallbacks fill any remaining slots up to the requested limit.","type":"array","items":{"$ref":"#/components/schemas/SuggestedFollowResponseDto"}}},"required":["data"]},"CreateBookmarkDto":{"type":"object","properties":{"actor_id":{"type":"string","description":"Agora actor UUID who is bookmarking — the customer-backend supplies the end-user identity here.","example":"aaaaaaaa-0000-0000-0000-000000000001"}},"required":["actor_id"]},"BookmarkResponseDto":{"type":"object","properties":{"actor_id":{"type":"string","description":"Agora actor UUID who saved the post.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"post_id":{"type":"string","description":"Saved post UUID.","example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"},"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"created_at":{"type":"string","description":"ISO timestamp the bookmark was created.","example":"2026-05-01T12:00:00.000Z"}},"required":["actor_id","post_id","community_id","created_at"]},"BookmarkToggleResponseDto":{"type":"object","properties":{"bookmark":{"$ref":"#/components/schemas/BookmarkResponseDto"},"created":{"type":"boolean","description":"`true` on first call (row inserted); `false` on idempotent retry of an existing bookmark.","example":true}},"required":["bookmark","created"]},"BookmarkListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BookmarkResponseDto"}},"pagination":{"$ref":"#/components/schemas/PaginationResponseDto"}},"required":["data","pagination"]},"MentionEntryResponseDto":{"type":"object","properties":{"source":{"type":"string","description":"Where the mention happened.","enum":["post","comment"],"example":"post"},"source_id":{"type":"string","description":"UUID of the source row — post id when source=post, comment id when source=comment.","example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"},"post_id":{"type":"string","description":"Parent post UUID. Always set, including for comment mentions, so the client can deep-link.","example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"},"mentioned_id":{"type":"string","description":"The actor that was mentioned.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"created_at":{"type":"string","description":"ISO timestamp the source row was created.","example":"2026-05-01T12:00:00.000Z"}},"required":["source","source_id","post_id","mentioned_id","created_at"]},"MentionListResponseDto":{"type":"object","properties":{"data":{"description":"Interleaved post + comment mentions, newest-first. Cursor is opaque base64url(JSON({created_at, source, source_id})).","type":"array","items":{"$ref":"#/components/schemas/MentionEntryResponseDto"}},"pagination":{"$ref":"#/components/schemas/PaginationResponseDto"}},"required":["data","pagination"]},"SearchActorResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Agora actor UUID.","example":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"},"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"external_id":{"type":"string","description":"Customer-supplied stable identifier.","example":"ada"},"display_name":{"type":"string","description":"Display name.","example":"Ada Lovelace","nullable":true},"avatar_url":{"type":"string","description":"Avatar URL.","example":"https://...","nullable":true},"metadata":{"type":"object","description":"Free-form metadata.","example":{}},"status":{"type":"string","description":"Lifecycle status. Search only surfaces `active` actors.","enum":["active","suspended","deleted"],"example":"active"},"created_at":{"type":"string","description":"ISO timestamp.","example":"2026-05-01T12:00:00.000Z"},"updated_at":{"type":"string","description":"ISO timestamp.","example":"2026-05-01T12:00:00.000Z"}},"required":["id","community_id","external_id","display_name","avatar_url","metadata","status","created_at","updated_at"]},"ActorSearchHitDto":{"type":"object","properties":{"actor":{"$ref":"#/components/schemas/SearchActorResponseDto"},"rank":{"type":"number","description":"PG `ts_rank` score on the (display_name A, external_id B, bio C) tsvector. Higher = more relevant.","example":0.6079}},"required":["actor","rank"]},"ActorSearchResponseDto":{"type":"object","properties":{"data":{"description":"Up to `limit` (default 20, max 50) ranked hits. Not paginated in v1; results are bounded by `limit`.","type":"array","items":{"$ref":"#/components/schemas/ActorSearchHitDto"}}},"required":["data"]},"SearchPostResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Post UUID.","example":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"},"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"actor_id":{"type":"string","description":"Author actor UUID.","example":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"},"kind":{"type":"string","description":"Free-form post kind.","example":"text"},"title":{"type":"string","description":"Optional title.","example":null,"nullable":true},"body":{"type":"string","description":"Body text.","example":"shipping today.","nullable":true},"url":{"type":"string","description":"Optional URL.","example":null,"nullable":true},"attributes":{"type":"object","description":"Free-form attributes blob.","example":{}},"visibility":{"type":"string","description":"Visibility scope.","enum":["public","followers","close_friends","private"],"example":"public"},"status":{"type":"string","description":"Lifecycle status. Search only surfaces 'published'.","enum":["published","hidden","removed","draft"],"example":"published"},"reaction_counts":{"type":"object","description":"Live denormalised reaction counts.","example":{}},"comment_count":{"type":"number","description":"Live denormalised comment count.","example":0},"expires_at":{"type":"string","description":"ISO timestamp; null = no expiry.","example":null,"nullable":true},"pinned":{"type":"boolean","description":"Pinned for highlights.","example":false},"created_at":{"type":"string","description":"ISO timestamp.","example":"2026-05-01T12:00:00.000Z"},"updated_at":{"type":"string","description":"ISO timestamp.","example":"2026-05-01T12:00:00.000Z"}},"required":["id","community_id","actor_id","kind","title","body","url","attributes","visibility","status","reaction_counts","comment_count","expires_at","pinned","created_at","updated_at"]},"PostSearchHitDto":{"type":"object","properties":{"post":{"$ref":"#/components/schemas/SearchPostResponseDto"},"rank":{"type":"number","description":"PG ts_rank score on the (title A, body B) tsvector. Higher = more relevant. Returned even when order='recent' so clients can render a relevance badge.","example":0.6079}},"required":["post","rank"]},"PostSearchResponseDto":{"type":"object","properties":{"data":{"description":"Up to `limit` (default 20, max 50) hits. Not paginated in v1.","type":"array","items":{"$ref":"#/components/schemas/PostSearchHitDto"}}},"required":["data"]},"StoryTrayEntryResponse":{"type":"object","properties":{"actor_id":{"type":"string","description":"Author actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"actor_external_id":{"type":"string","description":"Author's external_id.","example":"alice"},"actor_display_name":{"type":"string","description":"Author display name.","example":"Alice","nullable":true},"actor_avatar_url":{"type":"string","description":"Author avatar URL.","example":"https://...","nullable":true},"latest_story_created_at":{"type":"string","description":"When the most recent unexpired-or-pinned story was created.","example":"2026-05-01T11:00:00.000Z"},"story_count":{"type":"number","description":"Total unexpired-or-pinned stories from this author.","example":3},"has_unviewed":{"type":"boolean","description":"True when the requester has not viewed the latest story.","example":true}},"required":["actor_id","actor_external_id","actor_display_name","actor_avatar_url","latest_story_created_at","story_count","has_unviewed"]},"StoryTrayResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StoryTrayEntryResponse"}}},"required":["data"]},"CursorPagination":{"type":"object","properties":{"next_cursor":{"type":"string","description":"Opaque base64url cursor; null when no further pages.","example":null,"nullable":true},"has_more":{"type":"boolean","description":"Whether more pages exist after this one.","example":false}},"required":["next_cursor","has_more"]},"HighlightsResponse":{"type":"object","properties":{"data":{"description":"Page of pinned posts.","example":[],"type":"array","items":{"type":"string"}},"pagination":{"$ref":"#/components/schemas/CursorPagination"}},"required":["data","pagination"]},"RecordViewDto":{"type":"object","properties":{"viewer_actor_id":{"type":"string","description":"Viewer agora actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000002"}},"required":["viewer_actor_id"]},"StoryViewerResponse":{"type":"object","properties":{"viewer_actor_id":{"type":"string","description":"Viewer actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000002"},"viewer_external_id":{"type":"string","description":"Viewer's external_id.","example":"bob"},"viewer_display_name":{"type":"string","description":"Viewer display name.","example":"Bob","nullable":true},"viewer_avatar_url":{"type":"string","description":"Viewer avatar URL.","example":"https://...","nullable":true},"viewed_at":{"type":"string","description":"When the view was recorded.","example":"2026-05-01T11:30:00.000Z"}},"required":["viewer_actor_id","viewer_external_id","viewer_display_name","viewer_avatar_url","viewed_at"]},"StoryViewersResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StoryViewerResponse"}},"pagination":{"$ref":"#/components/schemas/CursorPagination"}},"required":["data","pagination"]},"CreateCloseFriendDto":{"type":"object","properties":{"owner_actor_id":{"type":"string","description":"Owner agora actor UUID (the actor whose close-friends list this is).","example":"aaaaaaaa-0000-0000-0000-000000000001"},"member_actor_id":{"type":"string","description":"Member agora actor UUID (the actor being added).","example":"aaaaaaaa-0000-0000-0000-000000000002"}},"required":["owner_actor_id","member_actor_id"]},"CloseFriendResponse":{"type":"object","properties":{"owner_actor_id":{"type":"string","description":"Owner actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"member_actor_id":{"type":"string","description":"Member actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000002"},"member_external_id":{"type":"string","description":"Member's external_id.","example":"bob"},"member_display_name":{"type":"string","description":"Member display name.","example":"Bob","nullable":true},"member_avatar_url":{"type":"string","description":"Member avatar URL.","example":"https://...","nullable":true},"created_at":{"type":"string","description":"When the close-friend edge was created.","example":"2026-05-01T11:00:00.000Z"}},"required":["owner_actor_id","member_actor_id","member_external_id","member_display_name","member_avatar_url","created_at"]},"CloseFriendsListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CloseFriendResponse"}},"pagination":{"$ref":"#/components/schemas/CursorPagination"}},"required":["data","pagination"]},"CastVoteDto":{"type":"object","properties":{"actor_id":{"type":"string","description":"Voter agora actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000002"},"option_index":{"type":"number","description":"Zero-based index into post.attributes.poll.options.","example":1}},"required":["actor_id","option_index"]},"PollResultOptionResponse":{"type":"object","properties":{"index":{"type":"number","description":"Zero-based index into the original options list.","example":0},"label":{"type":"string","description":"Option label as supplied at post creation.","example":"VS Code"},"count":{"type":"number","description":"Number of votes recorded for this option.","example":42}},"required":["index","label","count"]},"PollResultsResponse":{"type":"object","properties":{"post_id":{"type":"string","description":"Post UUID the poll lives on.","example":"bbbbbbbb-0000-0000-0000-000000000001"},"question":{"type":"string","description":"Poll question, copied from post.attributes.poll.question.","example":"Best dev tool?"},"options":{"type":"array","items":{"$ref":"#/components/schemas/PollResultOptionResponse"}},"total_votes":{"type":"number","description":"Sum of counts across every option.","example":67}},"required":["post_id","question","options","total_votes"]},"UnreadCountResponseDto":{"type":"object","properties":{"count":{"type":"number","description":"Number of unread incoming messages across every conversation the actor participates in.","example":7}},"required":["count"]},"CreateConversationDto":{"type":"object","properties":{"actor_id":{"type":"string","description":"Agora actor UUID opening the conversation.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"kind":{"type":"string","description":"Conversation shape. Default `direct`.","enum":["direct","group"],"example":"direct"},"recipient_actor_id":{"type":"string","description":"For 1-on-1 (`kind=\"direct\"`): the other participant — agora actor UUID. Required when kind=direct.","example":"aaaaaaaa-0000-0000-0000-000000000002"},"actor_ids":{"description":"For groups (`kind=\"group\"`): initial members (excluding the creator). Required when kind=group. Capped by `community.settings.max_group_conversation_size` (default 50).","example":["aaaaaaaa-0000-0000-0000-000000000002","aaaaaaaa-0000-0000-0000-000000000003"],"type":"array","items":{"type":"string"}},"name":{"type":"string","description":"Optional group display name. Ignored for 1-on-1.","maxLength":200}},"required":["actor_id"]},"ConversationParticipantDto":{"type":"object","properties":{"actor_id":{"type":"string","description":"Participant actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"joined_at":{"type":"string","description":"ISO timestamp the actor joined.","example":"2026-05-01T12:00:00.000Z"},"last_read_at":{"type":"string","description":"ISO timestamp of the participant's last read marker; `null` until first /read.","example":null,"nullable":true},"muted":{"type":"boolean","description":"When true, dm_new_message notifications are suppressed for this participant.","example":false},"role":{"type":"string","description":"Role on this conversation. `admin` for groups can add / remove members and rename.","enum":["member","admin"],"example":"member"}},"required":["actor_id","joined_at","last_read_at","muted","role"]},"ConversationResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Conversation UUID.","example":"11111111-2222-3333-4444-555555555555"},"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"kind":{"type":"string","description":"Conversation shape — 1-on-1 (`direct`) or multi-participant (`group`).","enum":["direct","group"],"example":"direct"},"name":{"type":"string","description":"Group display name; null for 1-on-1.","example":null,"nullable":true},"created_by":{"type":"string","description":"Agora actor UUID of the group founder; null for legacy / 1-on-1.","example":null,"nullable":true},"participants":{"description":"Participants, joined-at ascending.","type":"array","items":{"$ref":"#/components/schemas/ConversationParticipantDto"}},"created_at":{"type":"string","description":"ISO timestamp.","example":"2026-05-01T12:00:00.000Z"},"updated_at":{"type":"string","description":"ISO timestamp.","example":"2026-05-01T12:00:00.000Z"},"last_message_at":{"type":"string","description":"ISO timestamp of the most recent message (or conversation create when no messages yet).","example":"2026-05-01T12:00:00.000Z"}},"required":["id","community_id","kind","name","created_by","participants","created_at","updated_at","last_message_at"]},"UpdateMemberRoleDto":{"type":"object","properties":{"caller_actor_id":{"type":"string","description":"The admin performing the role change — agora actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"role":{"type":"string","description":"Target role.","enum":["member","admin"],"example":"admin"}},"required":["caller_actor_id","role"]},"RenameGroupDto":{"type":"object","properties":{"caller_actor_id":{"type":"string","description":"Agora actor UUID of the renaming admin.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"name":{"type":"string","description":"New display name. Pass null or empty to clear.","maxLength":200,"nullable":true}},"required":["caller_actor_id","name"]},"ConversationListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ConversationResponseDto"}},"pagination":{"$ref":"#/components/schemas/PaginationResponseDto"}},"required":["data","pagination"]},"UpdateConversationDto":{"type":"object","properties":{"actor_id":{"type":"string","description":"The participant whose row is being updated.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"muted":{"type":"boolean","description":"Mute / unmute on the caller's participant row.","example":true}},"required":["actor_id"]},"LeaveConversationDto":{"type":"object","properties":{"actor_id":{"type":"string","description":"The participant leaving.","example":"aaaaaaaa-0000-0000-0000-000000000001"}},"required":["actor_id"]},"DirectMessageAttachmentDto":{"type":"object","properties":{"kind":{"type":"string","description":"Attachment kind discriminator.","example":"image"},"url":{"type":"string","description":"Attachment URL.","example":"https://cdn.example/attachments/abc.png"}},"required":["kind","url"]},"SendMessageDto":{"type":"object","properties":{"sender_id":{"type":"string","description":"The sender — agora actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"body":{"type":"string","description":"Message body. Required when `kind=\"text\"`. 1..10000 chars after trim.","maxLength":10000,"example":"Hey!"},"kind":{"type":"string","enum":["text","image","video","audio","system"],"example":"text"},"attachments":{"type":"array","items":{"$ref":"#/components/schemas/DirectMessageAttachmentDto"}},"reply_to_id":{"type":"string","description":"Threaded inline reply target.","nullable":true}},"required":["sender_id"]},"DirectMessageResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Message UUID.","example":"mmmmmmmm-0000-0000-0000-000000000001"},"conversation_id":{"type":"string","description":"Conversation UUID.","example":"11111111-2222-3333-4444-555555555555"},"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"sender_id":{"type":"string","description":"Sender actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"kind":{"type":"string","enum":["text","image","video","audio","system"],"example":"text"},"body":{"type":"string","description":"Message body. Returns `\"(deleted)\"` when soft-deleted.","example":"Hey there."},"attachments":{"type":"array","items":{"$ref":"#/components/schemas/DirectMessageAttachmentDto"}},"reply_to_id":{"type":"string","description":"Threaded inline reply target.","example":null,"nullable":true},"edited_at":{"type":"string","description":"ISO timestamp of the last edit; `null` for never-edited.","example":null,"nullable":true},"deleted_at":{"type":"string","description":"ISO timestamp when soft-deleted; `null` until then.","example":null,"nullable":true},"created_at":{"type":"string","description":"ISO timestamp.","example":"2026-05-01T12:00:00.000Z"}},"required":["id","conversation_id","community_id","sender_id","kind","body","attachments","reply_to_id","edited_at","deleted_at","created_at"]},"DirectMessageListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DirectMessageResponseDto"}},"pagination":{"$ref":"#/components/schemas/PaginationResponseDto"}},"required":["data","pagination"]},"UpdateMessageDto":{"type":"object","properties":{"body":{"type":"string","description":"Updated body.","minLength":1,"maxLength":10000,"example":"edited"},"sender_id":{"type":"string","description":"The sender — must match the message's sender_id.","example":"aaaaaaaa-0000-0000-0000-000000000001"}},"required":["body","sender_id"]},"DeleteMessageDto":{"type":"object","properties":{"sender_id":{"type":"string","description":"The sender — must match the message's sender_id.","example":"aaaaaaaa-0000-0000-0000-000000000001"}},"required":["sender_id"]},"MarkConversationReadDto":{"type":"object","properties":{"actor_id":{"type":"string","description":"The reader — agora actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"read_through":{"type":"string","description":"ISO timestamp. Messages with `created_at <= read_through` count as read. Omit to mark every message up to now().","example":"2026-05-01T12:00:00.000Z"}},"required":["actor_id"]},"ParticipantSummaryDto":{"type":"object","properties":{"actor_id":{"type":"string","description":"Participant actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"joined_at":{"type":"string","description":"ISO timestamp the actor joined.","example":"2026-05-01T12:00:00.000Z"},"last_read_at":{"type":"string","description":"ISO timestamp of the participant's last read marker.","example":null,"nullable":true},"muted":{"type":"boolean","description":"When true, dm_new_message notifications are suppressed for this participant.","example":false}},"required":["actor_id","joined_at","last_read_at","muted"]},"ConversationAdminResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Conversation UUID.","example":"11111111-2222-3333-4444-555555555555"},"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"kind":{"type":"string","enum":["direct","group"],"example":"direct"},"participants":{"type":"array","items":{"$ref":"#/components/schemas/ParticipantSummaryDto"}},"created_at":{"type":"string","description":"ISO timestamp.","example":"2026-05-01T12:00:00.000Z"},"updated_at":{"type":"string","description":"ISO timestamp.","example":"2026-05-01T12:00:00.000Z"},"last_message_at":{"type":"string","description":"ISO timestamp.","example":"2026-05-01T12:00:00.000Z"}},"required":["id","community_id","kind","participants","created_at","updated_at","last_message_at"]},"ConversationAdminListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ConversationAdminResponseDto"}},"pagination":{"$ref":"#/components/schemas/PaginationResponseDto"}},"required":["data","pagination"]},"DirectMessageAdminResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Message UUID.","example":"mmmmmmmm-0000-0000-0000-000000000001"},"conversation_id":{"type":"string","description":"Conversation UUID.","example":"11111111-2222-3333-4444-555555555555"},"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"sender_id":{"type":"string","description":"Sender actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"kind":{"type":"string","enum":["text","image","video","audio","system"],"example":"text"},"body":{"type":"string","description":"Message body. Returns `\"(deleted)\"` when soft-deleted.","example":"Hey there."},"attachments":{"description":"Attachments.","example":[],"type":"array","items":{"type":"string"}},"reply_to_id":{"type":"string","description":"Threaded inline reply target.","example":null,"nullable":true},"edited_at":{"type":"string","description":"ISO timestamp of the last edit; `null` for never-edited.","example":null,"nullable":true},"deleted_at":{"type":"string","description":"ISO timestamp when soft-deleted; `null` until then.","example":null,"nullable":true},"created_at":{"type":"string","description":"ISO timestamp.","example":"2026-05-01T12:00:00.000Z"}},"required":["id","conversation_id","community_id","sender_id","kind","body","attachments","reply_to_id","edited_at","deleted_at","created_at"]},"DirectMessageAdminListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DirectMessageAdminResponseDto"}},"pagination":{"$ref":"#/components/schemas/PaginationResponseDto"}},"required":["data","pagination"]},"CreateDirectMessageReactionDto":{"type":"object","properties":{"src_actor_id":{"type":"string","description":"The reactor — agora actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"type":{"type":"string","description":"Reaction type (emoji or label, 1..64 chars).","minLength":1,"maxLength":64,"example":"heart"}},"required":["src_actor_id","type"]},"DirectMessageReactionResponseDto":{"type":"object","properties":{"message_id":{"type":"string","description":"Reacted message UUID.","example":"mmmmmmmm-0000-0000-0000-000000000001"},"actor_id":{"type":"string","description":"Reactor actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"type":{"type":"string","description":"Reaction type (emoji or label, 1..64 chars).","example":"heart"},"created_at":{"type":"string","description":"ISO timestamp.","example":"2026-05-01T12:00:00.000Z"}},"required":["message_id","actor_id","community_id","type","created_at"]},"DirectMessageReactionCreatedResponseDto":{"type":"object","properties":{"reaction":{"$ref":"#/components/schemas/DirectMessageReactionResponseDto"},"created":{"type":"boolean","description":"True when this call inserted the row; false on idempotent retry.","example":true},"reaction_counts":{"type":"object","description":"Denormalised map of reaction type → count after the change.","example":{"heart":3,"laugh":1}}},"required":["reaction","created","reaction_counts"]},"DirectMessageReactionRemovedResponseDto":{"type":"object","properties":{"reaction_counts":{"type":"object","description":"Denormalised map of reaction type → count after the change.","example":{"heart":2}}},"required":["reaction_counts"]},"DirectMessageReactionListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DirectMessageReactionResponseDto"}},"pagination":{"$ref":"#/components/schemas/PaginationResponseDto"}},"required":["data","pagination"]},"CreateReactionDto":{"type":"object","properties":{"src_actor_id":{"type":"string","description":"Reactor — agora actor UUID.","example":"aaaaaaaa-0000-0000-0000-000000000001"},"type":{"type":"string","description":"Reaction type (1..64 chars).","minLength":1,"maxLength":64,"example":"like"}},"required":["src_actor_id","type"]},"ReactionResponseDto":{"type":"object","properties":{"edge":{"$ref":"#/components/schemas/EdgeResponseDto"},"created":{"type":"boolean","description":"`true` on first call; `false` on idempotent retry.","example":true},"reaction_counts":{"type":"object","description":"Denormalised reaction counts after the write.","example":{"like":13,"fire":3}}},"required":["edge","created","reaction_counts"]},"ReactionCountsResponseDto":{"type":"object","properties":{"reaction_counts":{"type":"object","description":"Denormalised reaction counts after the write.","example":{"like":12}}},"required":["reaction_counts"]},"HashtagPostListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PostResponseDto"}},"pagination":{"$ref":"#/components/schemas/PaginationResponseDto"}},"required":["data","pagination"]},"CreateWebhookDto":{"type":"object","properties":{"url":{"type":"string","description":"Subscriber URL. Must be `https://`.","example":"https://example.com/webhooks/agora"},"event_types":{"description":"Event types to subscribe to. Currently supported: `post.created`, `post.updated`, `post.reaction.created`, `repost.created`, `comment.created`, `comment.updated`, `comment.reaction.created`, `edge.follow.created`.","example":["post.created","comment.created"],"type":"array","items":{"type":"string"}}},"required":["url","event_types"]},"CreatedWebhookResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Webhook UUID.","example":"wwwwwwww-0000-0000-0000-000000000001"},"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"url":{"type":"string","description":"Subscriber URL.","example":"https://example.com/webhooks/agora"},"event_types":{"description":"Subscribed event types. Each item is one of the agora event names (e.g. `post.created`, `comment.created`, `edge.follow.created`).","example":["post.created","comment.created"],"type":"array","items":{"type":"string"}},"signing_secret_hint":{"type":"string","description":"Last 4 chars of the signing secret. Use the full secret returned by create / rotate to verify the X-Pcft-Signature header.","example":"abcd"},"disabled_at":{"type":"string","description":"ISO timestamp when the webhook was auto-disabled (or null if active).","example":null,"nullable":true},"disabled_reason":{"type":"string","description":"Reason the dispatcher auto-disabled the row.","example":null,"nullable":true},"consecutive_failures":{"type":"number","description":"Consecutive failed attempts since the last success.","example":0},"last_success_at":{"type":"string","description":"ISO timestamp of the most recent 2xx response.","example":null,"nullable":true},"last_failure_at":{"type":"string","description":"ISO timestamp of the most recent non-2xx response.","example":null,"nullable":true},"created_at":{"type":"string","description":"ISO timestamp.","example":"2026-05-01T12:00:00.000Z"},"updated_at":{"type":"string","description":"ISO timestamp.","example":"2026-05-01T12:00:00.000Z"},"signing_secret":{"type":"string","description":"Full plaintext HMAC signing secret. Returned ONCE — store it; subsequent reads only expose the last-4 hint.","example":"abc...xyz"}},"required":["id","community_id","url","event_types","signing_secret_hint","disabled_at","disabled_reason","consecutive_failures","last_success_at","last_failure_at","created_at","updated_at","signing_secret"]},"WebhookResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Webhook UUID.","example":"wwwwwwww-0000-0000-0000-000000000001"},"community_id":{"type":"string","description":"Owning community UUID.","example":"cccccccc-cccc-cccc-cccc-cccccccccccc"},"url":{"type":"string","description":"Subscriber URL.","example":"https://example.com/webhooks/agora"},"event_types":{"description":"Subscribed event types. Each item is one of the agora event names (e.g. `post.created`, `comment.created`, `edge.follow.created`).","example":["post.created","comment.created"],"type":"array","items":{"type":"string"}},"signing_secret_hint":{"type":"string","description":"Last 4 chars of the signing secret. Use the full secret returned by create / rotate to verify the X-Pcft-Signature header.","example":"abcd"},"disabled_at":{"type":"string","description":"ISO timestamp when the webhook was auto-disabled (or null if active).","example":null,"nullable":true},"disabled_reason":{"type":"string","description":"Reason the dispatcher auto-disabled the row.","example":null,"nullable":true},"consecutive_failures":{"type":"number","description":"Consecutive failed attempts since the last success.","example":0},"last_success_at":{"type":"string","description":"ISO timestamp of the most recent 2xx response.","example":null,"nullable":true},"last_failure_at":{"type":"string","description":"ISO timestamp of the most recent non-2xx response.","example":null,"nullable":true},"created_at":{"type":"string","description":"ISO timestamp.","example":"2026-05-01T12:00:00.000Z"},"updated_at":{"type":"string","description":"ISO timestamp.","example":"2026-05-01T12:00:00.000Z"}},"required":["id","community_id","url","event_types","signing_secret_hint","disabled_at","disabled_reason","consecutive_failures","last_success_at","last_failure_at","created_at","updated_at"]},"UpdateWebhookDto":{"type":"object","properties":{"url":{"type":"string","description":"New subscriber URL."},"event_types":{"description":"New subscription set.","type":"array","items":{"type":"string"}}}},"DeliveryResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Delivery row UUID."},"webhook_id":{"type":"string","description":"Webhook UUID."},"event_id":{"type":"string","description":"Stable event id (carried in the request body as `id`).","example":"evt_…"},"event_type":{"type":"string","description":"Event type for this attempt.","example":"post.created"},"attempt_number":{"type":"number","description":"Attempt number. Always 1 in v1 (no retry).","example":1},"request_body":{"type":"object","description":"Signed request body that was sent.","example":{}},"response_status":{"type":"number","description":"HTTP status code (null on transport / SSRF failure).","example":200,"nullable":true},"response_body":{"type":"string","description":"Truncated response body (≤1024 bytes).","example":"OK","nullable":true},"error_message":{"type":"string","description":"Free-form error string. Null on success.","example":null,"nullable":true},"latency_ms":{"type":"number","description":"Total round-trip time in ms.","example":67},"succeeded":{"type":"boolean","description":"Whether the delivery returned 2xx.","example":true},"created_at":{"type":"string","description":"ISO timestamp.","example":"2026-05-01T12:00:00.000Z"}},"required":["id","webhook_id","event_id","event_type","attempt_number","request_body","response_status","response_body","error_message","latency_ms","succeeded","created_at"]},"DeliveryListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryResponseDto"}},"pagination":{"$ref":"#/components/schemas/PaginationResponseDto"}},"required":["data","pagination"]}}}}