Skip to content

Preparing JSON files

Each file is a single JSON object, one canonical document. The safest way to start is from an existing record (the ZIP export or Download JSON in the editor panel) and to change only its values, so the shape is guaranteed correct.

Set identity deliberately. See Record identity for the full rules.

  • New record. Omit id and _match, and give a new unique slug (and parent for hierarchical types).
  • Update. Keep the existing record’s slug (and parent), or use _match.id.

For hierarchical types such as pages and hierarchical CPTs, always set parent, because identity is the pair (parent, slug).

fields is a map of field name to value, mirroring the pattern file exactly. These are ACF fields, and the theme defines the names.

"fields": {
"hero_section": { "heading": "Hero heading", "description": "Hero copy." },
"intro_section": {
"heading": "Intro heading",
"points": [ { "point": "Point 1" }, { "point": "Point 2" } ]
}
}
  • A group is a nested object, a repeater is a list of row objects, and a simple field is a string, number or boolean.
  • Key names must match the pattern, including sub-keys inside repeaters, groups and flexible content. A renamed key raises octa_unknown_field and is dropped.
  • Don’t add fields that aren’t in the pattern. Omit what you don’t fill in.
  • Repeaters use the same keys as the pattern row, and you can add or remove rows freely.
  • Flexible content may gain more sections, but only using layouts that already exist in the pattern. The acf_fc_layout must match a defined layout. An invented layout raises octa_unknown_layout.

A relation field (post object or relationship) is written as a list of "{post_type}/{slug}" strings.

"fields": { "related_offers": ["offer/service-5", "offer/service-7"] }

It works recursively, inside groups and repeater rows. A missing target is skipped with octa_relation_not_found. A plain-text field that merely contains a /, such as a URL, is left alone, because detection is by field type, not content.

A map of taxonomy slug to a list of term slugs, for posts only.

"taxonomies": { "category": ["news"], "series": ["series-x"] }
  • Only existing terms are assigned, whether in the database or in the same batch as separate term files. An unknown term raises octa_term_not_found. Bulk import does not create terms from this field, so add a term file to the batch instead.
  • Replace per taxonomy. The list is authoritative, [] detaches all, and a taxonomy not in the map is left untouched.
  • Use slugs, not IDs.

The block is plugin-neutral. Octa Editor maps it to Yoast or Rank Math.

"seo": { "title": "SEO title", "description": "SEO description." }
  • Omit for no change. "" or null clears the override and returns to the SEO plugin’s default template.
  • Write template variables in the active plugin’s syntax. Check _meta.providers.seo in the pattern file. Yoast uses %%title%%, and Rank Math uses %title%.
  • Plain text only. HTML in seo.* is stripped.
  • Don’t add a _meta block to new files. It’s read-only export data, ignored on import.
  • Don’t rename field keys or change group and repeater structure against the pattern.
  • Don’t reuse the same slug under the same parent in two files of one batch.
  • Don’t put <script> (including JSON-LD <script type="application/ld+json">), <style>, <iframe>, event-handler attributes (onclick, onerror) or javascript: URLs in content or in any fields value. The import rejects the whole file with octa_unsafe_html and saves nothing, even for administrators. An embed the record already holds (added in wp-admin) can come back unchanged, and the import leaves it as it is; only new or changed markup is rejected. Two exceptions: an embed in a term’s name or description rejects every edit of that term, because WordPress strips it whenever the term is saved, and rows that hold an embed must come back as a list ([…]), not as an object with numbered keys. Add structured data and embeds with an SEO or embed plugin instead. Ordinary formatting HTML (<strong>, links, Gutenberg blocks) is fine.
  • Don’t wrap the JSON in code fences or add comments. The file must be exactly one JSON object.
{
"type": "post",
"post_type": "page",
"status": "publish",
"title": "New implementation service",
"slug": "new-implementation-service",
"parent": "services",
"menu_order": 3,
"seo": { "title": "", "description": "Implementations from analysis to hand-off." },
"fields": {
"hero_section": {
"heading": "New implementation service",
"description": "End-to-end implementations, from analysis to hand-off."
},
"related_offers": ["offer/service-5"]
},
"content": ""
}
{
"type": "term",
"taxonomy": "series",
"title": "Implementation series",
"slug": "implementation-series",
"description": "A series about implementations.",
"fields": {}
}