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.
| Token | Allowed |
|---|---|
read | GET/HEAD only; PUT and POST get 403 |
read_update | all 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
| Endpoint | Behaviour |
|---|---|
GET /document | the document plus its referenced media paths |
PUT /document | validates and saves document as an import version |
GET/POST /document/bundle | ZIP with document and media; import takes the raw body or a file field bundle, overwrite_media=1 replaces files |
POST /translate | target_language, optional source_language; replaces the stored document as an ai_translate version |
POST /draft | only brief and optional template_id; returns a document without saving |
POST /duplicate | returns the sent document with fresh block ids |
GET /templates | up to 100 templates |
GET /versions, POST /restore | the newest 100 versions; restore version_id as a new restore version |
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...}}