Files
RenewCMS/docs/llm/recipe-articles.md
T

4.9 KiB

Recipe: create, edit and publish an article

Create a draft, replace its body, check its SEO, publish it and read it back, with an API key.

Set the key once. Every request below is real output from a running instance; ids and timestamps differ on yours.

export RENEWCMS_API_KEY=rcms_...

1. Create a draft

Titles are 3 to 200 characters and can be edited later with PATCH. The request body is HTML; the response body is markdown unless you pass ?format=html or ?format=json.

curl -X POST "{{BASE_URL}}/v1/article/" \
  -H "Authorization: Bearer $RENEWCMS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"How we price a bathroom renovation","body":"<p>A bathroom renovation is priced by room size, plumbing changes and finish level.</p>"}'

Status 201:

{"id":1,"title":"How we price a bathroom renovation","body":"A bathroom renovation is priced by room size, plumbing changes and finish level.","body_format":"markdown","is_online":false,"site_id":1,"authors":[],"created_at":"2026-10-23T01:11:16.767761797+02:00","updated_at":"2026-10-23T01:11:16.767761797+02:00","slug":"how-we-price-a-bathroom-renovation","excerpt":"","seo_title":"","seo_description":"","cover_image_id":null,"cover_image_alt":"","social_title":"","social_description":"","social_image_id":null,"canonical_url":"","noindex":false,"seo_dismissed":[]}

The article starts offline (is_online: false) on the default site. To create it on another site, add "site":"<site key>" to the body (and optionally "lang":"fr", an enabled language of that site); an unknown key answers 404 site_not_found, and a language the chosen site does not enable answers 422 language_not_enabled.

2. Replace the body, move it to a site, set the excerpt

PATCH takes only body, site_id, author_ids and the SEO fields. At least one is required; {} answers 400. Send {"body": ""} to empty a body.

curl -X PATCH "{{BASE_URL}}/v1/article/1" \
  -H "Authorization: Bearer $RENEWCMS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"body":"<h2>What drives the price</h2><p>Room size, plumbing changes and finish level.</p>","site_id":2,"excerpt":"What drives the price of a bathroom renovation."}'

Status 200:

{"id":1,"title":"How we price a bathroom renovation","body":"## What drives the price\n\nRoom size, plumbing changes and finish level.","body_format":"markdown","is_online":false,"site_id":2,"authors":[],"created_at":"2026-10-23T01:11:16.767761797+02:00","updated_at":"2026-10-23T01:11:16.771008847+02:00","slug":"how-we-price-a-bathroom-renovation","excerpt":"What drives the price of a bathroom renovation.","seo_title":"","seo_description":"","cover_image_id":null,"cover_image_alt":"","social_title":"","social_description":"","social_image_id":null,"canonical_url":"","noindex":false,"seo_dismissed":[]}

3. Check the SEO before publishing

An empty JSON object checks the saved article. Pass any SEO field to check a change without saving it.

curl -X POST "{{BASE_URL}}/v1/article/1/seo/check" \
  -H "Authorization: Bearer $RENEWCMS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Status 200:

{"status":"suggestions","suggestions":[{"code":"desc_short","field":"seo_description","params":{"length":47,"min":70}},{"code":"cover_none","field":"cover_image_id","params":{}},{"code":"no_au","field":"author_ids","params":{}},{"code":"short","field":"body","params":{"length":66,"min":300}}],"slug":{"available":true,"suggestion":null}}

Suggestions never block publishing.

4. Publish

curl -X PATCH "{{BASE_URL}}/v1/article/1/publish" \
  -H "Authorization: Bearer $RENEWCMS_API_KEY"

Status 200, same article object with "is_online":true. PATCH /v1/article/{id}/unpublish reverses it.

5. Read it back without credentials

curl "{{BASE_URL}}/v1/article/1?format=markdown"

Before step 4 this answers 404 with the text Article not found, because anonymous callers only see online articles.

Read a language version (public)

Two public routes, no credential needed (a cookie or key is ignored and a draft is never returned):

GET /v1/public/site/journal/lang/fr/article/comment-nous-chiffrons-une-renovation
GET /v1/public/site/journal/story/5?lang=fr&fallback=default

The first matches site, language and slug exactly and never falls back (404 article_not_found). The second resolves a story: without lang it asks for the site default language, and fallback=default serves the default-language version when the requested one is not published (fallback: true, requested_lang keeps the asked language); otherwise 404 language_not_available. Both answer the article object plus alternates, the other published versions of the story in enabled languages (id, lang, story_id, slug, url; url is null when the site has no Public URL pattern), default language first, then by tag. An invalid lang is 400 invalid_language.