{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":200,"description":"Post title, 1–200 characters. The slug is derived from it when `slug` is omitted."},"slug":{"type":"string","minLength":2,"maxLength":100,"pattern":"^[a-z0-9][a-z0-9-]*[a-z0-9]$","description":"URL slug, e.g. \"hello-world\": 2–100 lowercase letters, digits and hyphens, no hyphen at either end. The post lives at {blog_url}{slug}/. Omit to derive it from the title. Cannot be changed later; \"lang\" is reserved."},"body":{"type":"string","minLength":1,"description":"Post content as markdown. Raw HTML is stripped. A YouTube URL alone on its own line becomes an embedded player. {{fact:name}} shows the current value of the blog's live fact `name` (see set_fact)."},"excerpt":{"type":"string","maxLength":300,"description":"Summary shown under the title in the post list, up to 300 characters. Omit to derive it from the body."},"tags":{"default":[],"type":"array","items":{"type":"string"},"description":"Tags shown on the post page, e.g. [\"ai\", \"travel\"]. Defaults to none."},"status":{"default":"published","type":"string","enum":["draft","published"],"description":"\"published\" (default) puts the post live immediately; \"draft\" saves it without publishing."},"seoTitle":{"type":"string","maxLength":200,"description":"Title for search results and link previews, up to 200 characters, used exactly as written (the blog name is not added). Defaults to `title`, which search results show as \"<title> — <blog name>\"."},"seoDescription":{"type":"string","maxLength":300,"description":"Description for search results and link previews, up to 300 characters. Defaults to the excerpt."},"author":{"type":"string","maxLength":100,"description":"Author name, up to 100 characters. Used in the page's metadata, the feed and the markdown source."},"coverImage":{"type":"string","format":"uri","description":"Cover image URL (http/https), shown above the post and used as its link-preview image. Use an uploaded image URL or any public image URL."},"language":{"type":"string","maxLength":35,"description":"Language of this post as a BCP-47 tag, e.g. \"en\", \"ru\", \"pt-BR\". Omit to inherit the blog's language."},"publishAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Schedule the post: an ISO 8601 time with a timezone, e.g. \"2026-11-03T09:00:00Z\", in the future and at most 90 days ahead. The post is saved as a draft and goes live by itself at that time (within a minute or so). Omit to publish or save now."},"translationOf":{"type":"string","minLength":1,"maxLength":100,"description":"Slug of an existing post in this blog that this post translates. The two become a translation group: one post per language, cross-linked with hreflang and a language switcher. Set `language` to the translation's language; it must differ from every other member's."}},"required":["title","body","tags","status"],"additionalProperties":false}