Skip to content

REST API ​

A JSON API under /api/renick/content/v1, built on the same services as the editor. Route list: REST reference.

Off by default

Every request gets 404 "API disabled" until you enable the API under Settings → Content → API Tokens.

API tokens ​

Send a token with every request, over HTTPS only:

Authorization: Bearer <token>

Tokens are managed on the same settings page (permission renick.content.manage_api_tokens). A new token (rc_…) is shown once and stored only as a SHA-256 hash. Deactivating or deleting it revokes it immediately. A missing, unknown or inactive token gets 401.

TokenAllowed
readGET/HEAD only; PUT and POST get 403
read_updateall endpoints, including writes, AI calls, bundle import and restore
Legacy bearer token (field above the list)all endpoints; leave it empty once every client has a personal token

read_update tokens are fully trusted

They can write richtext and code blocks, whose HTML/JS runs unsanitized for every visitor. Treat them like backend editor access and use read wherever possible.

Host addressing ​

Document endpoints take host_type (model class), host_id, field (default content) and, for Tailor, section (blueprint handle, required when the id exists in several blueprints).

host_type must be a Tailor EntryRecord, a class under host_types in config/api.php (default: SharedContent) or a class added via the renick.content.api.resolveHostTypes event. Any other class, and a missing record, gets 404 "Host not found". A field that is not a content field gets 422.

Limits and errors ​

  • Bodies: documents up to 2 MB, bundles up to 50 MB (413).
  • Rate: 60 requests per minute per token, AI calls (translate, draft) additionally 10 per minute (429, always JSON).
  • Bundle imports are checked before extraction: 20 MB per file, 200 MB in total, media-library file types, safe paths.
  • Other failures, such as an invalid document or a failed AI call, return HTTP 500.

Values live in config/limits.php and config/bundle.php.

Endpoints ​

EndpointBehaviour
GET /documentthe document plus its referenced media paths
PUT /documentvalidates and saves document as an import version
GET/POST /document/bundleZIP with document and media; import takes the raw body or a file field bundle, overwrite_media=1 replaces files
POST /translatetarget_language, optional source_language; replaces the stored document as an ai_translate version
POST /draftonly brief and optional template_id; returns a document without saving
POST /duplicatereturns the sent document with fresh block ids
GET /templatesup to 100 templates
GET /versions, POST /restorethe newest 100 versions; restore version_id as a new restore version
bash
curl -G -H "Authorization: Bearer $TOKEN" \
  --data-urlencode 'host_type=Tailor\Models\EntryRecord' \
  --data-urlencode 'section=Content\Article' -d host_id=1 \
  https://example.com/api/renick/content/v1/document
# → {"document": {...}, "media": ["/photos/hero.jpg"]}

curl -X PUT -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"host_type":"Tailor\\Models\\EntryRecord","section":"Content\\Article","host_id":1,
       "document":{"blocks":[{"type":"heading","data":{"text":"Hello","level":"h2"}}]}}' \
  https://example.com/api/renick/content/v1/document
# → {"document": {...normalized...}}