Posts API
Posts are the content items within a channel, blog articles, LinkedIn posts, newsletter editions, etc. Posts are always accessed through their parent channel.
Endpoints
| Method | Endpoint | Scope | Description |
|---|---|---|---|
| GET | /channels/{channel_id}/posts | read:posts | List posts in a channel |
| GET | /channels/{channel_id}/posts/{id} | read:posts | Get a single post |
| PATCH | /channels/{channel_id}/posts/{id}/publish | write:posts | Confirm publication of a pending post |
| PATCH | /channels/{channel_id}/posts/{id}/unpublish | write:posts | Revert a published post to draft |
List Posts
Retrieve a paginated list of posts for a given channel.
GET /channels/{channel_id}/posts
Path Parameters
| Parameter | Type | Description |
|---|---|---|
channel_id | integer | Channel ID |
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number |
per_page | integer | 20 | Items per page (max: 100) |
limit | integer | - | Shorthand for per_page |
Example Request
curl -s "https://api.wortfreunde.ch/v1/channels/161/posts?limit=2" \
-H "Authorization: Bearer $WORTFREUNDE_API_KEY" | python3 -m json.tool
Response
{
"data": [
{
"id": 214,
"title": "Warum strukturierte Content-Planung mehr bringt als spontane Posts",
"teaser": "Montagmorgen. Der Cursor blinkt. Was soll ich heute posten?...",
"slug": "strukturierte-content-planung-vs-spontane-posts",
"publication_status": "published",
"created_at": "2026-03-02T16:54:36.465Z",
"updated_at": "2026-03-09T05:00:48.275Z",
"channel": {
"id": 161,
"title": "Blog",
"platform": "git",
"team": {
"id": 42,
"name": "Wortfreunde"
},
"posts_count": 3
}
},
{
"id": 208,
"title": "Content-Produktion effizient gestalten: 7 Strategien fuer Fuehrungskraefte",
"teaser": "Viele Fuehrungskraefte verbringen Stunden mit Posts, ohne Ergebnis...",
"slug": "content-produktion-effizient-gestalten-strategien-fuer-fuehrungskraefte",
"publication_status": "published",
"created_at": "2026-02-28T10:03:19.931Z",
"updated_at": "2026-02-28T10:08:20.763Z",
"channel": {
"id": 161,
"title": "Blog",
"platform": "git",
"team": {
"id": 42,
"name": "Wortfreunde"
},
"posts_count": 3
}
}
],
"meta": {
"page": 1,
"pages": 1,
"count": 3,
"per_page": 20
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
id | integer | Unique post identifier |
title | string | Post title |
teaser | string | Short summary / excerpt |
slug | string | URL-safe identifier |
publication_status | string | Status (see below) |
created_at | string | ISO 8601 creation timestamp |
updated_at | string | ISO 8601 last update timestamp |
channel | object | The channel this post belongs to |
Publication Status
| Status | Description |
|---|---|
draft | Not yet published |
publishing_pending | Scheduled and awaiting external confirmation (API platform only) |
published | Live and visible |
scheduled | Scheduled for future publication |
Pagination
The meta object contains pagination details:
| Field | Type | Description |
|---|---|---|
page | integer | Current page number |
pages | integer | Total number of pages |
count | integer | Total number of posts in this channel |
per_page | integer | Items per page |
Get Post
Retrieve a single post with its full content.
GET /channels/{channel_id}/posts/{id}
Path Parameters
| Parameter | Type | Description |
|---|---|---|
channel_id | integer | Channel ID |
id | integer | Post ID |
Example Request
curl -s https://api.wortfreunde.ch/v1/channels/161/posts/214 \
-H "Authorization: Bearer $WORTFREUNDE_API_KEY" | python3 -m json.tool
Response
{
"data": {
"id": 214,
"title": "Warum strukturierte Content-Planung mehr bringt als spontane Posts",
"body": "## Struktur\n\nDas Problem ist nicht fehlende Kreativitaet...",
"teaser": "Montagmorgen. Der Cursor blinkt. Was soll ich heute posten?...",
"slug": "strukturierte-content-planung-vs-spontane-posts",
"publication_status": "published",
"created_at": "2026-03-02T16:54:36.465Z",
"updated_at": "2026-03-09T05:00:48.275Z",
"published_at": "2026-03-09T05:00:48.269Z",
"channel": {
"id": 161,
"title": "Blog",
"platform": "git",
"team": {
"id": 42,
"name": "Wortfreunde"
},
"posts_count": 3
},
"tags": [],
"media": [],
"meta_title": "Strukturierte Content-Planung vs. spontane Posts",
"meta_description": "Erfahre, warum strukturierte Content-Planung mehr Sichtbarkeit und Resonanz bringt als spontane Posts."
}
}
Response Fields
The single post response includes all fields from the list response, plus:
| Field | Type | Description |
|---|---|---|
body | string | Full post content in Markdown |
published_at | string | null | ISO 8601 publication timestamp |
tags | array | List of tag objects |
media | array | List of attached media objects |
meta_title | string | null | SEO title override |
meta_description | string | null | SEO description override |
external_id | string | null | External system identifier (API platform only) |
external_url | string | null | URL where the post was published externally (API platform only) |
The body field contains the full Markdown content of the post. In the list endpoint, body is omitted to keep responses lightweight, use the teaser for previews.
Error Responses
Post Not Found
curl -s https://api.wortfreunde.ch/v1/channels/161/posts/99999 \
-H "Authorization: Bearer $WORTFREUNDE_API_KEY"
{
"error": {
"code": "not_found",
"message": "Post not found"
}
}
| Status | Description |
|---|---|
| 401 | Invalid or missing API key |
| 403 | Insufficient scopes, requires read:posts or write:posts |
| 404 | Channel or post not found |
| 422 | Validation error (wrong status, wrong platform) |
| 429 | Rate limit exceeded |
Publish Post
Confirms publication of a post that is in publishing_pending status. This endpoint is used by external systems to complete the publishing workflow for API platform channels.
PATCH /channels/{channel_id}/posts/{id}/publish
Scope
Requires write:posts.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
channel_id | integer | Channel ID (must be API platform) |
id | integer | Post ID |
Request Body (optional)
{
"post": {
"external_id": "ext-12345",
"external_url": "https://example.com/posts/12345"
}
}
| Field | Type | Description |
|---|---|---|
post.external_id | string | Optional external system identifier for the published post |
post.external_url | string | Optional URL where the post was published externally |
The post wrapper is optional, if omitted, no external fields are set.
Example Request
curl -s -X PATCH "https://api.wortfreunde.ch/v1/channels/42/posts/214/publish" \
-H "Authorization: Bearer $WORTFREUNDE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"post": {"external_id": "wp-789", "external_url": "https://blog.example.com/my-post"}}' \
| python3 -m json.tool
Response
Returns the updated post with publication_status: "published":
{
"data": {
"id": 214,
"title": "My API Platform Post",
"publication_status": "published",
"published_at": "2026-03-10T14:30:00.000Z",
"external_id": "wp-789",
"external_url": "https://blog.example.com/my-post",
"channel": {
"id": 42,
"title": "External Blog",
"platform": "api"
}
}
}
Error Responses
| Code | Error Code | Description |
|---|---|---|
| 403 | - | Token lacks write:posts scope |
| 404 | not_found | Channel or post not found |
| 422 | only_api_platform | Channel is not an API platform channel |
| 422 | invalid_status | Post is not in publishing_pending status |
| 422 | no_publication | No API publication record exists for the post |
Unpublish Post
Reverts a published API platform post back to draft status.
PATCH /channels/{channel_id}/posts/{id}/unpublish
Scope
Requires write:posts.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
channel_id | integer | Channel ID (must be API platform) |
id | integer | Post ID |
Example Request
curl -s -X PATCH "https://api.wortfreunde.ch/v1/channels/42/posts/214/unpublish" \
-H "Authorization: Bearer $WORTFREUNDE_API_KEY" \
| python3 -m json.tool
Response
Returns the updated post with publication_status: "draft":
{
"data": {
"id": 214,
"title": "My API Platform Post",
"publication_status": "draft",
"published_at": null,
"external_id": null,
"external_url": null,
"channel": {
"id": 42,
"title": "External Blog",
"platform": "api"
}
}
}
Error Responses
| Code | Error Code | Description |
|---|---|---|
| 403 | - | Token lacks write:posts scope |
| 404 | not_found | Channel or post not found |
| 422 | only_api_platform | Channel is not an API platform channel |
| 422 | invalid_status | Post is not in published status |
| 422 | no_publication | No API publication record exists for the post |
API Platform Publishing Workflow
The API platform provides an async publishing workflow for external systems:
- Schedule, A post on an API platform channel is scheduled via the content plan
- Dispatch, At the scheduled time, Wortfreunde sets the post to
publishing_pendingand fires apost.publishing_pendingwebhook - Publish, The external system receives the webhook, processes the post, and calls
PATCH .../publishto confirm - Update, If the post content changes after publishing, a
post.updatedwebhook is fired so the external system can re-sync - Unpublish, To revert, the external system calls
PATCH .../unpublish, which fires apost.unpublishedwebhook
Unlike LinkedIn and Git platform channels (which publish directly), API platform posts remain editable even after publishing. Content changes do not automatically revert the publication status, instead, a post.updated webhook notifies the external system.