Entries
An entry is one row of a content type. Field values are stored per locale in ContentFieldValue; non-localizable fields use the synthetic __shared locale.
Create
POST /api/v1/projects/{slug}/entries
Authorization: Bearer krios_mk_…
{
"contentTypeApiName": "blogPost",
"siteId": "site_main",
"locale": "en-US",
"treeParentId": "node_blogIndex",
"treeNodeName": "Hello World",
"slug": "hello-world",
"fields": {
"title": "Hello World",
"body": { "type": "doc", "content": [...] }
},
"publishAt": "2026-06-01T09:00:00Z" // optional — schedules a future publish
}
treeParentId must reference an existing node whose allowedChildTypes permits the new type. The server enforces both the per-node override and the parent CT's allowedChildTypes. Always lands as draft.
Update
Optimistic concurrency via version. Pass the version you observed; the server bumps it on success.
PUT /api/v1/projects/{slug}/entries/{id}
{
"version": 3,
"locale": "fr-CA",
"fields": { "title": "Bonjour", "body": { "type": "doc", … } }
}
If version doesn't match, you get 409 with the current version in the body. Catch and re-fetch.
Version history
Every save creates an immutable ContentVersion snapshot containing the full field data for the locale, the author, and an optional message. The admin UI's Versions tab lists them with diff + restore actions.
GET /api/v1/projects/{slug}/entries/{id}/versions
GET /api/v1/projects/{slug}/entries/{id}/versions/{versionNum}
POST /api/v1/projects/{slug}/entries/{id}/versions/{versionNum}/restore
{ "locale": "en-US" }
The version number is the path segment ({versionNum}), and the body carries the locale to restore. Restore creates a new version (it doesn't rewrite history) so the audit trail stays linear.
Soft delete
DELETE /api/v1/projects/{slug}/entries/{id}
The row stays in the database with isDeleted=true. References to deleted entries resolve to null at delivery (the GraphQL field returns null; REST ?include= skips them).
Recover a soft-deleted entry via the REST endpoint (also surfaced as the admin UI's restore action):
POST /api/v1/projects/{slug}/entries/{id}/restore
No body. Returns 409 entry_not_deleted if the entry isn't currently soft-deleted. (This is distinct from version restore above, which lives at …/versions/{versionNum}/restore.)
Hard delete is a separate management API call that bypasses referential integrity (caller must explicitly confirm).
Where used
GET /api/v1/projects/{slug}/entries/{id}/references
Returns every entry that references the requested one (via reference, blocks, or rich-text embeddedEntry). The same data drives the safety gate on delete: deleting a referenced entry returns 409 with the referencer list.
Duplicate
POST /api/v1/projects/{slug}/entries/{id}/duplicate
{
"title": "Hello World (Copy)",
"treeParentId": "node_blogIndex",
"locales": ["en-US", "fr-CA"], // optional — only copy these locales
"deepCopyBlocks": true // optional — recursively duplicate referenced block entries
}
The clone is always created as draft, slug auto-suffixed (hello-world-copy).
Copy from another locale
When seeding a translation:
POST /api/v1/projects/{slug}/entries/{id}/copy-locale
{ "sourceLocale": "en-US", "targetLocale": "fr-CA" }
Copies localizable field values from source to target on the same entry. Sets the target's translation status to in-progress so stale detection works.
CLI shortcuts
krios entries list --type blogPost --site main --status published
krios entries get <id> --locale en-US
krios entries create --type blogPost --locale en-US --site main --file home.json
krios entries update <id> --locale en-US --file changes.json --version 4
krios entries publish <id> --locale en-US