| name | freek-dev-blog |
| description | Create and schedule link posts on freek.dev blog. Use when asked to publish, post, or share a link on freek.dev. Handles post creation, updating, and scheduling via the blog API. |
freek.dev Blog Posts
API
All post operations use the freek.dev Blog Posts API. No browser automation needed.
Base URL
https://freek.dev/api
Authentication
Bearer token in the Authorization header.
Token location: .secrets/blog-freek-dev.md
Authorization: Bearer <token>
Endpoints
List posts — GET /api/posts
Query parameters (all optional):
published (0 or 1): filter by published status
original_content (0 or 1): filter by original content
tag (string): filter by tag name
search (string): search by title
Returns paginated results.
Get a post — GET /api/posts/{id}
Create a post — POST /api/posts
Body (JSON):
title (string, required)
text (string, required, markdown)
publish_date (ISO 8601 date, nullable)
published (boolean, default false)
original_content (boolean, default false)
external_url (URL string, nullable)
tags (array of strings)
send_automated_tweet (boolean, default false)
author_twitter_handle (string, nullable)
series_slug (string, nullable)
Returns 201 with the created post.
Update a post — PUT /api/posts/{id}
Same fields as create, all optional. Only send fields you want to change.
Delete a post — DELETE /api/posts/{id}
Returns 204 No Content.
Response Format
All responses are wrapped in a data key:
{
"data": {
"id": 1,
"title": "...",
"slug": "...",
"text": "...",
"html": "...",
"publish_date": "2026-03-02T14:00:00+00:00",
"published": true,
"original_content": false,
"external_url": null,
"series_slug": null,
"author_twitter_handle": null,
"send_automated_tweet": false,
"tags": ["laravel", "php"],
"url": "https://freek.dev/1-post-slug",
"preview_url": "https://freek.dev/1-post-slug?preview_secret=abc123",
"created_at": "2026-03-02T12:00:00+00:00",
"updated_at": "2026-03-02T12:00:00+00:00"
}
}
API Usage Examples
Create a link post:
curl -X POST https://freek.dev/api/posts \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Article title here",
"text": "Short summary of the linked content.",
"external_url": "https://example.com/article",
"send_automated_tweet": true
}'
Schedule a post (set publish_date + published):
curl -X PUT https://freek.dev/api/posts/3040 \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"publish_date": "2026-03-03T13:30:00+00:00",
"published": true
}'
Update post text:
curl -X PUT https://freek.dev/api/posts/3040 \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"text": "Updated markdown content here."}'
Creating a Link Post
90% of posts are link posts (external content with a summary).
Steps
- Create the post via
POST /api/posts with:
title: Use the original article title or a clear variant
text: 2 sentences max summarizing the linked content. No links in the text.
external_url: The full URL being linked
original_content: false (it's a link post)
send_automated_tweet: true
- Before scheduling, ALWAYS inspect the existing unpublished queue (
GET /api/posts?published=0) and look at publish_date values. Do not assume the next slot is today.
- Schedule sequentially after the last already-scheduled post, usually one post per day, around 14:30 CET/CEST unless the existing queue clearly follows a different pattern.
- To schedule, set
publish_date to the chosen future time. Do NOT set published: true (the blog auto-publishes when the date arrives).
- Always confirm by displaying the created title, full summary text, and preview URL back to the user (so Freek can review what was written)
Link Post Text Guidelines
- Keep it to 2 sentences max
- Summarize what's interesting about the linked content
- No links in the summary text (the external URL field handles linking)
- No code blocks unless essential
- Write in third person ("They've released..." not "I found...")
- Never use em dashes in summaries or titles. Use commas, periods, or "and" instead.
Creating an Original Content Post
For posts written by Freek (package announcements, tutorials, opinions).
Steps
- Create the post via
POST /api/posts with:
title: The post title
text: Full markdown content (see write-freek-dev-blogpost skill for writing style)
original_content: true
send_automated_tweet: true (usually)
tags: Relevant tags as array of strings
- Share the
preview_url from the response for review
- Schedule when Freek approves
Creating a YouTube Video Post
When Freek shares a YouTube link for the blog.
Steps
- Get the video ID from the YouTube URL (e.g.
dQw4w9WgXcQ from youtube.com/watch?v=dQw4w9WgXcQ)
- Get the video title via oembed API:
youtube.com/oembed?url=VIDEO_URL&format=json
- Create the post via
POST /api/posts with:
- Always confirm by displaying the title, full summary text, and preview URL
YouTube Embed Notes
- If the original URL has a timestamp (
t=24 or start=24), add ?start=24 to the embed src
- Use
width="560" height="315" as standard dimensions
Creating a Tweet Embed Post
When Freek wants to embed a Twitter/X thread on the blog.
Steps
- Gather the tweet IDs from the thread
- Create the post via
POST /api/posts with:
- Confirm with the preview URL so Freek can verify the embeds render
Tweet Embed Notes
data-conversation="none" prevents showing the parent tweet above each reply
- Use ONE
<script> tag at the end, not per-blockquote
- Twitter's widget.js renders the blockquotes client-side into rich embeds
Scheduling Convention
Standard scheduling: sequential daily at ~14:30 CET (13:30 UTC in winter, 12:30 UTC in summer).
⚠️ CRITICAL: NEVER set published: true for scheduled posts. Setting published: true publishes IMMEDIATELY and triggers the automated tweet. The blog auto-publishes posts when their publish_date arrives.
To schedule a new post: Create with published: false (or omit, it defaults to false) and set publish_date to the desired future date.
To schedule an existing post: Update with published: false and publish_date:
PUT /api/posts/{id}
{"published": false, "publish_date": "2026-03-03T13:30:00+00:00"}
Example Link Post
Title: SQL performance improvements: automatic detection & regression testing
Text:
The final part of Oh Dear's series on SQL performance. Mattias introduces phpunit-query-count-assertions, a package that catches N+1 queries, duplicate queries, and missing indexes in your test suite.
External url: https://ohdear.app/news-and-updates/...