Content items

Overview
This is where writing happens — once — and from here the text goes to every configured publishing target: your own website, a WordPress, wherever else.
That is the whole difference from a blog that belongs to a website. An item is not part of a page but a record of its own. It can live in several places without being written several times, and when you correct it, you correct it everywhere.
Content types: the shape, not the content
A content type defines which fields an item of that kind has. "Blog" ships with the system: title, excerpt, body, featured image. You can add more — "press release", "job ad", "recipe" — each with its own fields.
That sounds like a detail and is the reason this module exists: without types, the second kind of text eventually repurposes a field of the first ("subtitle" as a salary figure), and nobody can work out later what was meant.
An item's type cannot be changed. Its values belong to that schema; whatever does not exist in the other type would silently be gone.
Languages live INSIDE the item
An item carries its languages within itself, not as a second row beside it. The language switch above the text changes which version you are writing. The other stays untouched.
Every language that actually contains text is published. An empty English version is not published — it would look like a mistake out there, and one the reader attributes to you.
The workflow
| State | What it means |
|---|---|
| Draft | Being written. Changes freely. |
| In review | Waiting for someone to read it. Still editable — review is a state, not a lock. |
| Scheduled | Has a date. The scheduler checks every five minutes and publishes it by itself. |
| Published | It is out, on every matching target. |
| Archived | Taken out of the working set, not deleted. |
The workflow is fixed and cannot be extended with your own intermediate states. That is deliberate: program logic hangs off these names — "scheduled" wakes the scheduler, "published" delivers. A self-invented state would be a waiting room with no exit.
Only "scheduled" requires a date. Without it the scheduler never picks the item up — it would lie there forever while looking as though everything was done. That is why it is required.
Core tasks
Write. Pick the type, fill the fields, save. The address is derived from the headline if you do not give one.
Let AI draft it. You describe the topic, the AI fills the fields of the type. The result lands in the form — you see it, change it and save it yourself.
Have a text rewritten. Below every text and rich text field there is Rewrite once AI is active: Shorter, More detailed, More formal, More casual, Simpler or Fix spelling. The suggestion appears in a window and only replaces the text when you click Apply — it is saved with the post. The AI changes the form, not the content: it does not invent numbers, names or quotes that are not in the text. Which model handles the task is set under Settings → AI (task “Rewrite the text of a post field”), including an own model.
Create an image or video with AI. Image and file fields offer Create with AI: describe, choose the format (1:1, 4:5, 9:16, 16:9), for video the duration, optionally a reference image — the cost is shown before starting. The result is in the brand's file storage right away, sits in the field and carries the AI-generated mark. With Higgsfield images and videos are created, without Higgsfield images come from the existing AI. Setup, cost and budget: AI settings.
Draft via the assistant. The AI tool create_content_draft creates a post as a draft — title, optionally a topic from which the AI suggests the other fields. It never publishes; releasing stays with the person who has the permission.
Send to review. The item moves to "in review" and appears under For approval — one click there approves and schedules, a second returns it with a note. Detailed feedback runs as a task on the item, not as a separate comment system: "please check the figures in the third paragraph" is a task, and tasks already have a history, assignees and a due date here.
Targets come pre-selected. If the content type lists networks under Published on, a new post already carries them under Targets — for its brand the connected account, format included. Deselect what this post does not need; the format can be changed per target. If the brand lacks a network, Connect appears there (or a hint if you may not manage connections). If you switch brand or type before saving, the pre-selection of the new choice applies.
Schedule or publish now. Both require the publish permission. Now means now — not "in the next run".
Withdraw. Takes the item back from every target and returns it to draft. The publication date stays: it says when the item was first out.
View, compare and restore an earlier version. Every publication saves a version of the item. Under Versions at the top of the item you see each one with its date and author, read its text and compare it field by field with what is in the editor now. Restore brings the texts of that version back and turns the item into a draft — it only goes out again through the normal approval. Nothing is lost on the way: the state being replaced is saved as a version of its own first. The address stays as it is. A published item is not overwritten silently: withdraw it first, then restore the version.
On your own website
The Own website target brings a post onto your website — without a network call, but with an address: /blog/<address> in the default language, /<language>/blog/<address> in every other language the post has a title for and the website carries. A language the website does not carry is rejected with a reason instead of handing out an address that leads nowhere.
- The website shows the most recently published version. Changes to a published post appear only
after you publish it again — which creates a new version. This also applies to search engine details and categories: the version freezes them too, and the blog's category pages list the post under its published categories. The editor points this out in the “Address & search engines” section.
- If the address changes, the old one redirects to the new one with a 301.
- Withdrawing removes the address; the post stays in the content system.
- The post appears in the blog overview, in post lists, in the sitemap and in the website search —
everywhere the same version.
- The post's search engine title and description go into the page head; without them, title and excerpt
apply. The featured image becomes the preview image for social networks.
- The text is cleaned on delivery: scripts, style attributes and embedded third-party content do not go
out; a level-1 heading in the text becomes level 2 (one main heading per page).
Images for the website. For image fields, Upload for the website puts the image into the public image library: it is converted to WebP and stored in several widths (480, 960 and 1600 pixels, as far as the original is that wide). The website then serves each screen the right size, with fixed dimensions against jumping pages. Choose image, in contrast, takes a file from the file storage — it is only visible when signed in and therefore does not appear on the website. The button appears when the builder is active; the images are then also in its media library. Permission: content.item.manage in the post's brand.
Tags and related posts. Besides the categories of the content type, a post carries free tags — type a term or pick an existing one from the suggestions. On the website every tag gets its own archive page, and below the article it appears as a link. In the post, the Related posts section shows which published posts of the same type fit this one: shared tags count twice, shared categories once. The website shows exactly this list below the article. A post without tags and categories has no related posts.
Editorial calendar
The list can be a table, a board and a calendar. On the board you drag an item from column to column — that triggers the same path as the button inside the item. In the calendar items sit on their planned date.
Notes, not blocks
Below the text you find notes: a too-short title, a missing description, an h1 inside the body, an empty link, an image without alternative text.
None of them prevents publishing — and that is a deliberate decision. A check that blocks teaches people to work around it: you set the meta title to "x" so the button lights up again, and the value is then worse than the missing one it replaced.
The missing alternative text is the only note at the highest level. It is not a convention but accessibility: for a blind reader it is the entire content of the image. It belongs to the item, not to the file — the same image can mean two things in two places.
Fields in detail
The What it does column answers what changes in the system — not what the field is called. The grey name behind the label is the API field: the same thing runs under that name through automation, import and AI tools.
Creating and editing content
| Field | Required | Values / format | What it does |
|---|---|---|---|
Content type typeKey | yes | an existing content type | What kind of content is created. It determines which fields the mask shows at all — an article has different ones from a recipe. Switching later is rarely sensible because field content can be lost. |
Language locale | yes | language code, max. 5 characters | Which language this content is written in. A translation is a separate content item, not a field — tied together by the translation group. |
Address slug | no | text, max. 220 characters | The readable part of the public address. Built from the title; changed by hand, existing links and search entries break. |
Fields fields | yes | depending on the content type | The actual content — title, text, images, whatever the type provides for. |
Categories categories | no | several categories | Classification of the content. |
Tags tags | no | free terms | Free classification besides the categories. On the website every tag has an archive page; shared tags make posts related posts. On saving, the list replaces the previous one. |
Author authorEmployeeId | no | an employee | Who wrote the content. It appears where the channels show an author. |
Planned publication plannedPublishAt | no | point in time | When the content should go out. Until then it appears in the calendar but nowhere public. |
Targets targets | no | list of channels | Where it is published. One item can appear on several channels; each channel gets its own rendering of the fields it needs. If omitted on creation, Published on of the content type applies (the brand's targets with their options); a list that is sent — even an empty one — applies as sent. |
Options per target targetOptions | no | per target the options from its schema (availableTargets[].optionSchema), e.g. {"<target ULID>": {"format": "carousel"}} | What only this target wants to know: for Instagram the format (feed post, carousel with 2–10 images, story), for YouTube title and visibility, for TikTok visibility and comments. In the form the options sit on the selected target under Targets. Empty = the network default; a key or value the target does not know is rejected. Applies to all languages of the item. |
SEO seo | no | title, description, image | What search engines and shared previews show. Left empty the system takes title and excerpt — often enough, but never as good as a written sentence. |
Brand brandId | no | one of your brands | Which brand the content runs under. |
Translation group translationGroupUlid | no | identifier of a group | Ties the language versions of the same content together. Through it the reader finds the other language — and you see which translation is still missing. |
Settings & permissions
- Module
module.content. content.item.view— see items and types.content.item.manage— write, schedule, archive.content.item.publish— schedule, publish, withdraw. The permission with outward effect.content.type.manage— maintain content types and field schema. Deliberately separate: a
field change acts backwards on every existing item.
FAQ & troubleshooting
The scheduled item did not appear. Check the date and the state. If it says "publishing" and does not move, a run claimed it and crashed — after an hour it is recovered automatically and tried again.
"The field … does not exist on this content type." A field name does not match the schema — usually a typo, or a field that has since been renamed. Rejecting is deliberate: discarding it silently would mean the typo costs the whole text.
The English version has disappeared. It should not: the languages sit side by side inside the item, and saving one leaves the other untouched. Use the language switch to check which version you are in.
The address got a number appended. It was taken. Two items on the same address would not be decidable — which one wins would depend on database ordering.
I cannot see items of another brand. That is correct. What is visible is what you hold a content permission for in that brand — mere membership is not enough.