Skip to content
BetaDocs

Package Tools

These tools wrap /api/v1/sites/{siteId}/packages. The same API key works over REST; see calling the same operations over REST.

A Typeroll package (.typeroll) is one file that carries block templates (sections), page templates, global blocks, site block types, styles, the design (colours, typography, scales, content widths and font roles), menus, site field definitions, media and, when the author may pass them on, font files. A site package carries a whole site.

Within one Organization, the organization collection keeps templates, sections and block types ready for every site; it stores each entry as a single-entry package and can take in a .typeroll file.

A package belongs to no organization. It holds no organization or site ids, and its media and font files travel inside it, so anyone can import it into their own site: a template pack bought from a studio, sections shared by a colleague, or a starter site. The content export in Export and backup is separate: it is the complete site as JSON for backups and the open-source renderer, and it is not imported.

kind Holds
collection What the author chose: templates, page templates, free global blocks, block types, styles, design, menus
site A whole site: pages (published, unlisted and drafts), header and footer, menus, site fields with their values
clipboard Blocks copied from a page, with the styles, block types and free global blocks they use

What the chosen content uses comes along by itself: the free global blocks it places, the site block types it uses (and those they build on), and the styles it uses.

A package is a zip with a manifest and its files:

typeroll.json the manifest
media/<sha256>.<ext> media files
fonts/<sha256>.woff2 font files the author may pass on

The manifest lists the sha256 of every file. Import refuses a package whose files do not match, that is missing a file or carries one more, that holds anything outside these paths, or that breaks a limit:

Limit Value
Package file 200 MB
Unpacked files in total 400 MB
Manifest 20 MB
Entries 5,000
Media files 2,000
One media file in a package 50 MB
One file in the Media library 25 MB
Font files 60
Block templates 500
Page templates 100
Global blocks 200
Block types 200
Styles 300
Menus 50
Pages 2,000

Media. Every image or file the exported content uses goes into the package. In content, its address becomes a token, typeroll-media:<id>. Import adds each file to the site’s Media library through the normal private upload path and replaces the tokens with the new addresses. A file the library already has, with the same bytes, is reused.

The author can mark files as demo content (demo_media). Imported demo files carry a demo badge in the Media library, and the launch checklist and the publication list them while live pages use them, so they are replaced before the site launches. Keeping a file clears the flag: send demo: false with PUT /api/sites/{siteId}/media/{mediaId} or PATCH /api/v1/sites/{siteId}/media/{mediaId}.

Links. A collection keeps links to the author’s pages only as plain addresses, empties lists of chosen pages and leaves menu items that point at pages out, with a warning. A site package keeps them and points them at the imported pages.

App blocks. Blocks of an installed app are named by the app (app:<extension id>/<component>), not by the author’s installation. On a site with the same app they connect to its installation; without it they show nothing until the app is installed, with a warning.

Not in a package: form submissions, API keys, secrets, domains, publishing connections, members, revisions, content types and HTML pages.

  • Catalog families (Google Fonts) need no files: the design names them and import sets the roles.
  • Uploaded font files go into the package only when the exporter confirms that their licence allows passing them on (redistributable_confirmed: true). Most commercial licences do not; leave them out and the package keeps only the family names.
  • Importing font files needs the importer’s confirmation too (font_licence_confirmed: true): the font’s licence must allow use on their website. It is stored with the file in the Fonts library, with who confirmed it.
  • Adobe Fonts, Bunny Fonts and Fontshare never transfer. The package names the families; a buyer adds their own kit or stylesheet, or chooses other fonts. The import warns about it.

Import never swaps a package’s styles for the site’s “nearest” styles. Every style the imported content uses is created on the site, so the imported design stays as the author made it and can then be changed site-wide:

  • A style the site already has with the same id and the same definition is reused.
  • A style with the same id but another definition is created under a new id, such as hero-2, named “Hero (imported)”, and the imported content is rewritten to it. The site’s own hero is unchanged.
  • Renaming a style later changes only its display name; ids stay.
  • A standard role (for example lead) is held by one style per site: an imported copy beside the site’s own is a plain class style.
  • Importing or pasting the same content again reuses the earlier copy (hero-2) when its definition is still the same.
  • A style the content uses that neither the package nor the site has is the one case of a mapping: the site’s standard style of the same role is used (button-secondary becomes the site’s secondary button style), with a warning. Without one, those blocks keep their default look.

A template looks right on every site it is inserted into when it uses the site’s design and words instead of fixed values.

Use palette roles (primary, surface, on_primary …) and font roles (heading, body, display, accent, mono) rather than hex values and font families. Saving a block template, from the editor’s Save as template…, save_block_template, update_block_template, the collection or an export, answers with design_findings: each hard-coded colour or font family, where it is (path, block_id) and the role to use (suggestions). When exactly one palette role has the colour, replacement gives the field change; the editor and the block templates list offer it as Use primary and save the template again. Nothing is blocked. lint_design_tokens lists the same findings for the site’s block templates.

From render version 15, authored text and HTML can use {{site.name}} and {{site.fields.<name>}} (a site field with a text, number or true/false value), next to {{site.current_year}}:

<p>Welcome to {{site.name}}. Call us on {{site.fields.phone}}.</p>

The values are inserted as text (escaped) when the page is rendered: the preview shows the current values, and a publication the values it froze. A field with another kind of value, or one the site lacks, shows nothing. Before render version 15 these tokens show as written; only {{site.current_year}} expands.

A block or page template can carry its text in several languages under translations, keyed by language code. Each translation has its own blocks and, optionally, name, description and (page templates) label:

{
"id": "welcome",
"name": "Welcome",
"kind": "section",
"blocks": [
{ "id": "p", "type": "core/prose", "data": { "html": "<p>Welcome</p>" } }
],
"translations": {
"sv": {
"name": "Välkommen",
"blocks": [
{
"id": "p",
"type": "core/prose",
"data": { "html": "<p>Välkommen</p>" }
}
]
},
"de": {
"name": "Willkommen",
"blocks": [
{
"id": "p",
"type": "core/prose",
"data": { "html": "<p>Willkommen</p>" }
}
]
}
}
}

Import picks the site’s language (an exact match such as sv-FI, else the base language sv), and otherwise the template’s own text. Placing a template in a page that has its own language (a language section) uses the page’s language. Only the chosen language is stored on the site. inspect_package and the import report list each such template in template_languages: { section, id, language, available }, where language is null when the template’s own text was used.

An export writes one language per template, the text as it is on the site, also on a site with several languages. To offer more, add translations to the templates in typeroll.json before you share the package. Media a translation uses must be in the package’s media list and files, as for any other content. A changed manifest no longer matches a signature, so sign the package again. Translated trees are checked like the others, and the styles they use travel with the package.

The built-in starters mark text to replace with brackets, such as [Feature name]. Live pages that still show such placeholders are listed in the launch checklist and the publication’s warnings. Prefer real sample text in your own templates.

options.design decides how much of the package’s look an import takes:

Mode Adds
content The blocks and every style they use (the default)
styles Also the package’s other class styles
full Also the element styles (body, h1–h6, link), replacing the site’s, and the colours, typography, scales, content widths and font roles

full changes the whole site’s look, so it needs design permission (a site administrator or a builder), like the design settings. fonts: false imports the full design without the font roles and files.

Block types, block templates, page templates, global blocks, menus and pages follow the same rule as styles: an identical block type is reused, anything else that collides gets a free id (hero-2) and every reference in the imported content follows. A site package’s header and footer replace the site’s. A page whose address is taken is imported at a free address (/about-2); a second homepage is not imported. A menu location the site already fills is left off the imported menu. A settings page whose id the site already uses with other fields is skipped.

The whole import is planned and checked before anything is written. If a write fails part-way, the import stops with package_import_partial and details.report lists what was written.

A package can be signed by its publisher (Ed25519 over the manifest, which lists the sha256 of every file). Inspecting shows signature.status:

Status Meaning
verified A publisher this portal trusts signed it; publisher names them
unsigned No signature; the author named in the package is not verified
unknown_key Signed with a key this portal does not know
invalid The package was changed after it was signed

Unsigned packages can be imported; the author they name is shown as unverified. A self-hosted portal lists the publishers it trusts in TYPEROLL_PACKAGE_TRUSTED_KEYS, a JSON list of { "key_id", "publisher", "public_jwk" }.

A package from someone else is checked like any other input: its files must match their sha256, its block trees, styles, menus and block types are validated, and SVG files are sanitized as uploads are. Before importing:

  1. Inspect it (inspect_package, or Packages in the portal) and read the plan, the warnings and the licence it names.
  2. Choose the design mode. Start with content to add sections without changing the site’s look.
  3. Confirm font licences only when you hold one for your website.
  4. Replace the demo images before publishing.

A site package can be the starting point of a new Site instead of being imported into an existing one: New site → Start from a template or another site in the portal, or create_site with from_package_base64, from_package_url or from_collection_entry (a package entry of the collection). Another Site of the same Organization works the same way with from_site_id.

The package is imported into the new Site’s empty main version with the creator as its administrator, so ids are kept, styles are created under their own ids and design defaults to full: the new Site takes the template’s whole look. Font files need font_licence_confirmed: true, demo images stay marked for replacement, and a package with an invalid signature cannot be used. The default home page, header, footer and menus are added only where the package has none. If the import fails, the half-created Site is removed again.

What is copied and what never is (domains, publishing, keys, apps and their credentials) is listed in Start a site from a template or another site.

Export part of the site, or the whole site, and get a signed download address valid for one hour. A collection needs write permission; a site package needs admin, like the content export.

{
"selection": {
"kind": "collection",
"block_templates": ["hero", "pricing"],
"styles": "used",
"design": true,
"fonts": { "families": ["Brand Sans"], "redistributable_confirmed": true },
"demo_media": ["3f2c…"]
},
"meta": {
"id": "acme-restaurant-sections",
"name": "Acme restaurant sections",
"version": "1.0.0",
"author": { "name": "Acme Studio", "url": "https://acme.example" },
"licence": { "name": "Commercial, one site" },
"languages": ["en"]
}
}

block_templates, page_templates, global_blocks, block_types and menus take a list of ids or "all"; styles is "used" (default) or "all"; site_fields adds the settings page definitions. kind: "site" takes everything. The answer is { download_url, filename, bytes, sha256, expires_at, warnings, design_findings }, where design_findings lists hard-coded colours and fonts in the exported block templates, each with its template_id (see Colours and fonts).

Read a package and show what importing it would do, without changing anything. Send data_base64 (at most 32 MB) or source_url (an https:// address, up to 200 MB) and options. The answer is { package, signature, plan, warnings, errors, needs }: the plan lists each style, block type, template, global block, menu, page and media file with its action (create, reuse, rename, replace, skip), and the id maps. errors lists what would stop the import; needs says whether it needs design permission (design, also reported as admin for older callers) and a font licence confirmation.

Import a package. The same body as inspect_package, with options:

Option Type Meaning
design string content (default), styles or full
fonts boolean With full: import the font roles and files (default true)
only string[] Only these entries, such as block_templates.hero or menus.main
font_licence_confirmed true Required when the import adds font files

The answer is { report } with what was created, reused, renamed, replaced and skipped, the design keys updated, counts, the language used for each template with translations (template_languages) and warnings. Imported block templates record where they came from in imported_from (package id, name, version, author and time).

Refusals carry a code: package_invalid, package_too_large, package_unsafe, package_integrity_failed, package_conflict (with details.errors), package_permission, font_licence_required, font_redistribution_unconfirmed, package_selection_invalid, package_source_unavailable and package_import_partial.

Copied blocks travel as a package of kind clipboard inside a small JSON text, the one the block editor puts on the system clipboard (Copy and paste blocks):

{
"typeroll_clipboard": 1,
"source": {
"portal": "https://cms.example.com",
"site_id": "studio",
"site_key": "…",
"page_id": "home"
},
"copied_at": "2026-11-07T12:00:00.000Z",
"package": {
"format": "typeroll-package",
"kind": "clipboard",
"contents": {
"blocks": [],
"styles": [],
"block_types": [],
"global_blocks": []
},
"media": []
},
"media": [
{
"site_id": "studio",
"media_id": "…",
"addresses": ["https://cms.example.com/api/sites/studio/media/…/content"]
}
],
"page_links": { "about": "/about" },
"text": "Fresh bread"
}

The package holds the copied blocks with the styles, site block types (app blocks by their app) and free global blocks they use. Media files are not embedded: the blocks keep their addresses, and media lists the library file behind each. A clipboard is at most 2 MB and 200 top-level blocks.

Pasting into another site imports the package the way a collection entry is inserted into a page: styles are created, never mapped to “nearest” styles (see above), block types and global blocks likewise, and the blocks get new ids. A media file is copied into the target site’s Media library only when the caller can read the site it came from, with the same session or key: an organization key reads the organization’s sites and those shared with it, a site key only its own site. A private file the caller cannot read is left out with a warning; nothing is read with other rights. Pasting into the same site places the blocks as they are. Copying needs read permission on the source site, pasting write permission on the target, which also covers creating the styles, block types and global blocks the blocks need.

Copy blocks of a page’s draft. Answers { clipboard, count, warnings }, where clipboard is the JSON text above. Writes nothing.

Parameter Type Required Notes
page_id string yes
block_ids string[] yes The blocks to copy; a block inside another listed one comes along
version string no A branch id

Paste a clipboard into a page’s draft, after after_block_id (in its parent or slot when it is nested) or at position (in parent_id / slot_index); at the end by default. Answers { report, page: { id, added_ids, blocks }, mode, media: { copied, reused, left_out, kept_external }, message }. Save the page to publish the change.

Parameter Type Required Notes
clipboard string yes The text from copy_blocks, unchanged
page_id string yes
after_block_id string no Not together with a position
position integer no Index in the parent; the end when omitted
parent_id string | null no A container block
slot_index integer no A slot of parent_id
version string no A branch id

Refusals: clipboard_invalid (not copied blocks, another version, invalid content; details.errors), clipboard_too_large, clipboard_permission, clipboard_paste_invalid, page_not_found and block_not_found.

Method and path Body Answer
POST /api/v1/sites/{siteId}/packages/export { selection, meta } { download_url, filename, bytes, sha256, expires_at, warnings }
GET /api/v1/sites/{siteId}/packages/downloads/{token} The .typeroll file (the signed token is the credential)
POST /api/v1/sites/{siteId}/packages/inspect { data_base64 | source_url, options? } { package, signature, plan, warnings, errors, needs }
POST /api/v1/sites/{siteId}/packages/import { data_base64 | source_url, options } { report }
POST /api/v1/sites/{siteId}/clipboard/copy { page_id, block_ids } { clipboard, clipboard_text, clipboard_html, count, warnings }
POST /api/v1/sites/{siteId}/clipboard/paste { clipboard, page_id, after_block_id? | position?, parent_id?, slot_index? } { report, page, mode, media, message }

The portal uses session routes with the same rules: POST /api/sites/{siteId}/packages/export answers with the file itself (application/vnd.typeroll.package+zip), inspect and import take a multipart upload (file and options as JSON), and GET /api/sites/{siteId}/packages/exportable lists what the export form can offer. POST /api/sites/{siteId}/clipboard/copy and …/clipboard/paste take the same JSON as the v1 routes. Extension credentials cannot export or import packages, or copy and paste blocks.