Read posts in one verified brand, interpret filter/status semantics and paginate with offsets.
Access
- Verify the original credential, production environment and intended organization. Discover the actual authorized Socialize workspace and bind it separately from action input; a post or campaign id is not a workspace id.
- Published permission: app_topolo_socialize.posts:read. The published action is a read, but workspace authorization can synchronize the local brand mirror; do not infer a zero-SQL-write full request.
- The request must use its intended named workspace or an explicitly understood verified default. A named inaccessible resource is denied, without silent default fallback. API-key bindings further narrow actual grants; do not forge identity headers or substitute another principal.
Find the workspace and filters
- Resolve the brand/workspace through supported workspace discovery or the accessible brands guide, then preserve that scope across pages. Do not add brandId or organizationId to the strict action body.
- Optional campaignId is an actual discovered campaign id in the intended workspace; threadId is the intended stored thread identifier. authorId is a post author filter, never a replacement authenticated caller.
Request
GET /api/posts
Action: app_topolo_socialize.posts.list.
Optional inputs: authorId, status, platform, campaignId, threadId, limit and offset. Use current action discovery for the exact schema; selection of a workspace stays in separate resource context.
Response and pagination
- The handler returns {data:[post,...],meta:{count,limit,offset}}. count is this page’s length, not the total matching count. Default limit50/offset0; published schema restricts limit1–100 and non-negative integer offsets. No cursor or hasMore flag is supplied.
- Rows are ordered by created_at descending with no secondary id sort. Concurrent inserts/deletes or tied timestamps can shift offset pages; retain/deduplicate ids and do not claim a stable snapshot. Stop when the page is shorter than the selected limit, recognizing concurrent changes can alter later pages.
- platform=x is normalized to stored twitter. Other supported platforms are linkedin, linkedin_company, facebook, instagram, threads, tiktok and youtube. status supports draft, scheduled, published, failed, cancelled and archived.
- status=failed selects failed_at IS NOT NULL; draft selects stored draft with no failed_at. Other status filters compare stored status. Mapping labels any row with failed_at as failed, so a different stored-status filter can still return a displayed failed status; inspect failure fields rather than assuming all returned statuses equal the requested string.
- With campaignId, a distinct join includes both reusable content variants and concrete campaign occurrence posts. It does not restrict results to future queue occurrences or prove A/B distribution. The verified workspace and other filters remain applied.
- threadId matches metadata JSON’s threadId only when the stored metadata is valid JSON. Returned metadata itself remains a JSON string or null; it is not automatically expanded into an object.
- Each post exposes its actual owning brandId, content/author/platform/status, schedule/publication/failure/provider-record fields, mediaItems, normalized placement/publishMode and timestamps. Null provider/permalink fields are possible. A recorded published status or link is not a live provider availability/delivery test.
- mediaItems is parsed from stored JSON. A malformed stored media_items value can fail the page through the generic error path; this handler does not silently drop that row or return a partial-success page.
Effects
- resolveBrandId verifies the canonical workspace and may insert/reactivate/synchronize the local brand mirror before the post SELECT. Permission and API-key scope come from the original verified request.
- PostService.getPosts reads stored rows and maps them; this path does not create posts, queue publishing, hydrate provider data, generate copy/images or call a model. No scheduling or provider delivery follows from reading a page.
Examples
Every example is source-derived, synthetic and unrun. Placeholder ids and stored times confer no authority; nothing was posted, generated or delivered.
List draft posts using the X alias
Source-derived synthetic and unrun. Assumes the illustrated row belongs to the verified workspace, has stored twitter/draft and no failure timestamp.
{
"platform": "x",
"status": "draft",
"limit": 10,
"offset": 0
}
Illustrative response:
{
"data": [
{
"id": "post_example",
"content": "Example editorial draft.",
"authorId": "user_example",
"brandId": "ws_socialize_example",
"platform": "twitter",
"status": "draft",
"scheduledFor": null,
"publishedAt": null,
"failedAt": null,
"failureCode": null,
"failureMessage": null,
"lastPublishAttemptAt": null,
"lastPublishResultAt": null,
"permalink": null,
"platformPostId": null,
"mediaUrl": null,
"mediaType": null,
"mediaThumbnailUrl": null,
"mediaItems": [],
"placement": "feed",
"publishMode": "auto",
"metadata": null,
"createdAt": "2026-10-21 00:00:00",
"updatedAt": "2026-10-21 00:00:00"
}
],
"meta": {
"count": 1,
"limit": 10,
"offset": 0
}
}
Read data[0].brandId and normalized platform; count1 is the returned page length.
An empty later page
Source-derived synthetic and unrun. Assumes this authorized page has no matching rows; no Auth/storage failure is being simulated as an empty list.
{
"limit": 10,
"offset": 10
}
Illustrative response:
{
"data": [],
"meta": {
"count": 0,
"limit": 10,
"offset": 10
}
}
Treat it as an empty page of this scoped query, not evidence about another workspace or a fixed total.
Inspect stored failure information
Source-derived synthetic and unrun. Assumes existing persisted failure fields and valid stored empty media-array JSON; no provider call or failed delivery was executed.
{
"status": "failed",
"limit": 10,
"offset": 0
}
Illustrative response:
{
"data": [
{
"id": "post_failed_example",
"content": "Example editorial draft.",
"authorId": "user_example",
"brandId": "ws_socialize_example",
"platform": "twitter",
"status": "failed",
"scheduledFor": "2026-10-21T01:00:00.000Z",
"publishedAt": null,
"failedAt": "2026-10-21T01:00:02.000Z",
"failureCode": "example_failure",
"failureMessage": "Illustrative stored failure.",
"lastPublishAttemptAt": "2026-10-21T01:00:00.000Z",
"lastPublishResultAt": "2026-10-21T01:00:02.000Z",
"permalink": null,
"platformPostId": null,
"mediaUrl": null,
"mediaType": null,
"mediaThumbnailUrl": null,
"mediaItems": [],
"placement": "feed",
"publishMode": "auto",
"metadata": null,
"createdAt": "2026-10-21 00:00:00",
"updatedAt": "2026-10-21 00:00:00"
}
],
"meta": {
"count": 1,
"limit": 10,
"offset": 0
}
}
Inspect the stored failure details and original workspace; this read does not retry publishing or certify provider state.
Refusals and recovery
| Status | Source condition/code | Next step |
|---|---|---|
| 400 | /problems/validation-error | Use valid query/context inputs and the current strict schema; local validation rejects undeclared fields or out-of-range limits before the action call. A raw handler validation failure has problem-detail form, not a successful empty page. |
| 403 | workspace_forbidden | Discover and bind an actually granted canonical workspace with the original credential; never guess another workspace or silently use a default for a refused named resource. |
| 503 | workspace_unresolved | Auth could not verify workspace access. Preserve the refusal; this is not evidence of zero matching posts. |
| 500 | /problems/internal-server-error | Preserve request evidence and inspect data/dependencies with the existing owner. Malformed stored media JSON can fail response mapping; no repair or post mutation is implied. |
Outer credential/permission/rate-limit responses can differ from handler problem details. Deliberately invalid inputs such as limit101 are local validation failures, not captured HTTP responses.
Verify the result
- Compare every returned brandId to the intended verified workspace before relying on its content. Check optional filters, the page’s meta count/limit/offset and deduplicated ids; count is not a total.
- Inspect failedAt/failureCode/failureMessage alongside status and raw scheduling/publication fields. Do not treat campaign membership, schedule time, returned link or a successful list response as proof of provider execution.
- After a dependency or malformed-storage error, retain the exact scope/input/request evidence and ask the existing owner to diagnose it. Do not mutate a post or substitute a workspace to make a read succeed.
Related guides
Change log
2026-10-21: Added source-qualified post listing with explicit workspace selection, x/twitter mapping, offset limitations, stored failure-status semantics, full returned post fields and three synthetic unrun worked examples. Reviewed source9ff70e66954dc0275c604e827a8fae682de007b7; current published action markerbc22ce7a2ceaa4396388794c4d42895c777bed76. Source-to-serving association remains unverified.