Developers
Blog and content API
Write, schedule and share blog posts, and manage them over a headless API.
Cauliflower includes a small headless CMS for your company blog. Platform admins write posts in the admin portal; they're published at /blog with an RSS feed, a sitemap and social previews — and every post is also available over a JSON API, so agents, scripts or another website can read and write content.
Writing posts
Open Admin → Content → New post:
- Write in Markdown (headings, lists, tables, code, quotes and images) with a side-by-side preview that renders exactly like the blog. Upload images straight from the toolbar.
- Publishing — leave the publish date empty to publish now, or pick a future date to schedule the post. Set the URL, tags and author.
- Cover image — 1200×630 works best; it's used on the blog and as the link preview. Posts without a PNG or JPEG cover get a generated preview with their title.
- Search & sharing — meta title and description with a search result preview, and a canonical URL when the post first appeared elsewhere.
- Social posts — LinkedIn and X copy for promoting the post. Write
{url}where the post's link goes. Draft with AI writes both from the article using the provider configured under Admin → AI assistant.
Unpublish turns a post back into a draft. Every change is recorded in the platform audit log.
Sharing posts automatically
Under Admin → Content → Publishing & API, set a publish webhook URL. When a post goes live — immediately, or when its scheduled time arrives — Cauliflower sends one POST:
{
"event": "post.published",
"post": {
"id": "post_…", "slug": "why-demo-requests-go-cold", "url": "https://cal.example.com/blog/why-demo-requests-go-cold",
"title": "…", "excerpt": "…", "cover_image_url": "…", "tags": ["speed-to-lead"],
"published_at": "2026-10-08T09:00:00.000Z",
"social": { "linkedin": "…", "x": "…" }
}
}Point it at Zapier, Make, n8n or your own endpoint to queue the social posts in Buffer, post to LinkedIn or Slack, or send a newsletter. The RSS feed at /blog/rss.xml works with newsletter tools and feed readers too.
Content API
Base URL: https://cal.example.com/api/content
Reading (public)
Published posts can be read without authentication, from any origin (CORS is open for GET):
| Request | Returns |
|---|---|
GET /posts | Published posts, newest first. Query: tag, limit (up to 100), offset, include=html to add rendered HTML. |
GET /posts/<slug> | One published post, including content_html. |
Each post has id, slug, url, title, excerpt, content_markdown, cover_image_url, author, tags, status, published_at, updated_at, reading_minutes, seo and social.
Writing (token)
Create a token under Admin → Content → Publishing & API. It's shown once; send it as Authorization: Bearer cms_….
| Request | Does |
|---|---|
POST /posts | Creates a post. Body: title (required), content, excerpt, slug, tags, cover_image_url, author, status (draft by default, or published), published_at, seo_title, seo_description, canonical_url, social. |
PATCH /posts/<id or slug> | Updates only the fields you send. |
DELETE /posts/<id or slug> | Deletes the post. |
GET /posts?status=all | Includes drafts and scheduled posts. |
GET /posts/<id or slug> | Works for drafts too. |
curl -X POST https://cal.example.com/api/content/posts \
-H "Authorization: Bearer $CMS_TOKEN" -H "Content-Type: application/json" \
-d '{"title":"Routing leads by territory","content":"## Why territories…","tags":["routing"]}'Every write response includes images: how many external images were copied and a warning for any that couldn't be.
Images
External images in a post's Markdown ( and <img> tags) and its cover_image_url are copied into Cauliflower when you create or update the post, so posts never hotlink other sites or break when those images move. Images that can't be downloaded keep their link and are listed in images.warnings. Send "rehost_images": false to skip this.
To upload an image yourself, POST /media with a CMS token and one of:
| Body | Does |
|---|---|
multipart form with a file field (up to 10 files) | Stores the uploaded files. |
{ "url": "https://…" } or { "urls": [ … ] } | Downloads images from the web and stores copies. |
{ "data": "<base64>" } | Stores an image you generated (a data: URL works too). |
{ "openaiFileIdRefs": [ … ] } | Stores files a ChatGPT GPT Action passes from the conversation, such as images ChatGPT generated. |
It returns { "url": "…", "data": [{ "id", "url", "content_type", "size" }] }. Use the url as cover_image_url or in Markdown. Images can be PNG, JPEG, WebP, GIF or AVIF up to 5 MB; SVG isn't accepted. Downloads only reach public addresses, never private networks.
Brief and schema
| Request | Returns |
|---|---|
GET /brief (token) | What a writer needs before starting: product facts, audience, voice, topics, rules, image style, call to action, every existing post and the pages worth linking to. Edit it under Admin → Content → Growth agent. |
GET /openapi.json (public) | An OpenAPI 3.1 schema of the content API, for ChatGPT GPT Actions and other agent tools. |
Tokens can be revoked at any time. A good pattern is to let an AI agent create drafts on a schedule and have a person review and publish them.
Growth agent
Admin → Content → Growth agent turns ChatGPT (or any agent that can make HTTP requests) into a writer that runs the blog on its own. Each run, it reads the brief, picks a topic that no post covers yet, researches it with web search, writes the article in your voice with internal and external links, makes a cover and in-body illustrations (or finds licensed photos), uploads them, writes the SEO fields and social copy, and posts it as a draft (or publishes it, if you choose).
- Fill in What the agent knows: the product, audience, voice, topics, hard rules, image style and call to action. Empty fields use sensible defaults. The agent reads these from the brief every run, so you can change them later without touching the prompt.
- Click Put a new token in the prompt and Copy prompt. The prompt stays under ChatGPT's 8,000-character limit for GPT instructions.
- In ChatGPT, create a GPT: paste the prompt into Instructions, turn on Web Search and Image Generation, add an action with Import from URL set to
https://cal.example.com/api/content/openapi.json, and set its authentication to API Key → Bearer with the token. - Say "Write the next post" whenever you want one, or ask ChatGPT to schedule it.
Drafts appear under Content for review. Publishing still triggers the publish webhook, so social posts go out the same way as posts you write yourself.