A template holds the parts of a site that repeat across many pages: a header, a footer, a sidebar, or the shared body of every blog post. An agent writes a template with manage_template, using the same {blocks: [...]} body shape a page uses.
What a template is
A template is a stored row with a slug, a title, a type, and a body. The body is validated and rendered exactly like a page layout: the same block types, the same authoring rules, the same publish lifecycle.
The four template types
header,footer,sidebar: region templates. Each renders in that slot on every page that uses it.post-detail: the body of a single blog post route. This is not a region. It is the article's own<article>content, in place of the page-layout body a page would otherwise carry.
A template's type cannot change after it is created. Moving a template's content into a different slot means creating a new template of the right type, not updating the existing one.
Blocks a region template refuses
A header, footer, or sidebar body rejects a few block types that a page may use freely:
- PostList: a feed rendered on every page is not what a region is for.
- FAQ: the search-engine markup for an FAQ block ties to one specific page, and a region has no single owning page.
- FormBlock: a form's submissions resolve to the page that owns it, and a region has no single owning page.
- Poll: a poll's ballot is addressed by the page that carries it, the same reasoning as FormBlock.
- Any block carrying an
anchor: an anchor must be unique on the page it appears on, and a region renders unmodified on every page, so the same anchor would collide with itself.
Blocks a post-detail template may use
A post-detail body accepts the same general-purpose blocks a region does (HeroSection, RichText, ImageBlock, ProductGrid, Callout, SocialLinks, Countdown), plus one block a region cannot carry: PostField.
PostField is a slot bound to one fixed part of the post:
| Field | Meaning |
|---|---|
title | The post's title. |
byline | The post's author, from its frontmatter. |
publishedAt | The post's publish date. |
body | The post's compiled MDX content. |
tags | The post's tags. |
coverImage | The post's cover image. |
{ "type": "PostField", "props": { "field": "title" } }
A post-detail body must carry exactly one title slot and exactly one body slot. The other four fields are optional and may each appear at most once.
The manage_template tool
action is create, update, or unpublish. There is no read action: read templates back with query on the templates collection.
create needs slug, title, type, and body:
{
"action": "create",
"slug": "site-footer",
"title": "Footer",
"type": "footer",
"body": {
"blocks": [
{ "type": "SocialLinks", "props": { "links": [{ "platform": "x", "href": "https://x.com/example" }] } }
]
}
}
update needs id, expectedRevision, title, and body. slug and conditions are optional. type is never part of an update.
{
"action": "update",
"id": "tmpl_01h...",
"expectedRevision": 2,
"title": "Footer",
"body": { "blocks": [ /* ... */ ] }
}
unpublish needs id and expectedRevision, and takes no content:
{ "action": "unpublish", "id": "tmpl_01h...", "expectedRevision": 3 }
Unpublishing keeps the published body in place, so republishing later needs no re-authoring. The result names an outcome and a regionBecameEmpty flag, telling you whether that slot now has nothing published in it at all.
Every write is CAS-guarded: a mismatched expectedRevision returns a revision_conflict error naming the current revision to retry with.
A template's draft is published the same way a page's is, through the shared publish tool:
{ "entityType": "template", "id": "tmpl_01h...", "expectedRevision": 0 }
Where a template renders: conditions
A template may carry optional conditions: {pathGlob?, country?}. Both clauses, if present, must match for the template to be a candidate at all.
pathGlobis either an exact absolute path (/docs/getting-started) or an absolute literal prefix ending in/**(/docs/**). No other wildcard syntax is accepted.countryis a two-letter country code (DE,US).
A template published with no conditions is unconditional: it is the default for its type. At most one unconditional template per type may be published at once. Publishing a new unconditional template of a type automatically replaces the previously published unconditional one of that type; the replaced template keeps its body and can be republished later without re-authoring. Publishing a conditional template never replaces anything. Any number of conditional templates of the same type may be published side by side.
How Noon picks the winner
When a page renders, Noon looks at every published template of a type whose conditions match the request, and picks the most specific one:
- An exact-path match beats a
/**prefix match, which beats no path condition at all. - Among prefix matches, the longer literal prefix wins.
- If the path comparison is still tied, a template with a matching country condition beats one with none.
If nothing conditional matches, the unconditional default, if published, renders. If a request's path cannot be resolved at all, every conditional template is treated as non-matching, but the unconditional default may still serve. Two published templates that tie on every point above are broken by comparing their ids as plain text and picking the lower one. This only happens if you publish two templates with identical conditions, which is worth avoiding rather than relying on.
Check your work before publishing
explain_template_resolution is a read-only tool that answers "which template would render here, and why" without changing anything.
{ "path": "/docs/getting-started", "country": "DE", "type": "header" }
The answer names the winner, if any, and lists every published candidate of that type: whether each of its clauses matched, and for every candidate that lost, the first reason it lost against the winner. Omit type to check every template type in one call.
Site settings
manage_site replaces the whole site settings document in one call. Any key you omit resets to its default, so read the current settings with query before writing if you only want to change one field.
| Key | What it holds |
|---|---|
siteName | The site's display name, used in page titles and structured data. |
titleTemplate | One of three fixed patterns for the page <title>: {{title}}, {{title}} – {{siteName}}, or {{siteName}} – {{title}}. |
defaultMetaDescription | The fallback meta description for pages that don't set their own. |
defaultOgImage | The fallback social-preview image. |
favicon | The site's favicon: an .ico file, or a .png at one of a fixed set of sizes. |
siteLogo | The site's logo image. |
defaultLocale | The site's default language tag, e.g. en or de-DE. |
identity | The site's organization or person identity, with its social profile links, used in structured data. |
permalinkPattern | The URL pattern blog posts are served under. |
customCss | One block of site-wide CSS. See the themes guide. |
analytics | Matomo analytics configuration, or none. |
There is no navigation key on site settings. Site navigation, such as a menu bar or a footer link list, is authored as a header or footer template, built from ordinary blocks like SocialLinks or RichText.