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.
The file
Section titled “The file”A package is a zip with a manifest and its files:
typeroll.json the manifestmedia/<sha256>.<ext> media filesfonts/<sha256>.woff2 font files the author may pass onThe 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 |
What travels
Section titled “What travels”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.
Fonts and licences
Section titled “Fonts and licences”- 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.
Styles are created, not mapped
Section titled “Styles are created, not mapped”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 ownherois 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-secondarybecomes the site’s secondary button style), with a warning. Without one, those blocks keep their default look.
Templates that follow the site
Section titled “Templates that follow the site”A template looks right on every site it is inserted into when it uses the site’s design and words instead of fixed values.
Colours and fonts
Section titled “Colours and fonts”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.
Text that names the site
Section titled “Text that names the site”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.
Text in the site’s language
Section titled “Text in the site’s language”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.
Placeholders
Section titled “Placeholders”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.
Design modes
Section titled “Design modes”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.
Conflicts and renames
Section titled “Conflicts and renames”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.
Signatures and trusted publishers
Section titled “Signatures and trusted publishers”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" }.
Buying packages from others
Section titled “Buying packages from others”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:
- Inspect it (
inspect_package, or Packages in the portal) and read the plan, the warnings and the licence it names. - Choose the design mode. Start with
contentto add sections without changing the site’s look. - Confirm font licences only when you hold one for your website.
- Replace the demo images before publishing.
Start a new site from a site package
Section titled “Start a new site from a site package”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_package
Section titled “export_package”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).
inspect_package
Section titled “inspect_package”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_package
Section titled “import_package”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.
Copy and paste blocks
Section titled “Copy and paste blocks”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
Section titled “copy_blocks”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_blocks
Section titled “paste_blocks”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.
REST endpoints
Section titled “REST endpoints”| 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.